first commit

This commit is contained in:
/usr/bin/nano
2017-04-15 01:34:36 +03:00
commit c715e2a604
5325 changed files with 329700 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
(function() {
var exports = this, IceAddTitlePlugin;
IceAddTitlePlugin = function(ice_instance) {
this._ice = ice_instance;
};
IceAddTitlePlugin.prototype = {
nodeCreated: function(node, option) {
node.setAttribute('title', (option.action || 'Modified') + ' by ' + node.getAttribute(this._ice.userNameAttribute)
+ ' - ' + ice.dom.date('m/d/Y h:ia', parseInt(node.getAttribute(this._ice.timeAttribute))));
}
};
ice.dom.noInclusionInherits(IceAddTitlePlugin, ice.IcePlugin);
exports._plugin.IceAddTitlePlugin = IceAddTitlePlugin;
}).call(this.ice);

View File

@@ -0,0 +1,510 @@
(function() {
var exports = this, IceCopyPastePlugin;
IceCopyPastePlugin = function(ice_instance) {
this._ice = ice_instance;
this._tmpNode = null;
this._tmpNodeTagName = 'icepaste';
this._pasteId = 'icepastediv';
var self = this;
// API
// 'formatted' - paste will be MS Word cleaned.
// 'formattedClean' - paste will be MS Word cleaned, insert and
// delete tags will be removed keeping insert content in place,
// and tags not found in `preserve` will be stripped.
this.pasteType = 'formattedClean';
// Subset of tags that will not be stripped when pasteType
// is set to 'formattedClean'. Parameter is of type string with
// comma delimited tag and attribute definitions. For example:
// 'p,a[href],i[style|title],span[*]'
// Would allow `p`, `a`, `i` and `span` tags. The attributes for
// each one of these tags would be cleaned as follows: `p` tags
// would have all attributes removed, `a` tags will have all but
// `href` attributes removed, `i` tags will have all but `style`
// and `title` attributes removed, and `span` tags will keep all attributes.
this.preserve = 'p';
// Callback triggered before any paste cleaning happens
this.beforePasteClean = function(body) { return body; };
// Callback triggered at the end of the paste cleaning
this.afterPasteClean = function(body) { return body; };
// Event Listeners
ice_instance.element.oncopy = function() { return self.handleCopy.apply(self); };
// ice_instance.element.oncut = function() { return self.handleCut.apply(self); };
// We can't listen for `onpaste` unless we use an algorithm that temporarily switches
// out the body and lets the browser paste there (found it hard to maintain in mce).
// Instead, we'll watch the keydown event and handle paste with a typical temp element
// algorithm, which means that pasting from the context menu won't work.
};
IceCopyPastePlugin.prototype = {
setSettings: function(settings) {
settings = settings || {};
ice.dom.extend(this, settings);
this.preserve += ',' + this._tmpNodeTagName;
this.setupPreserved();
},
keyDown: function(e) {
if (e.metaKey !== true && e.ctrlKey !== true) {
return;
}
if(e.keyCode == 86) {
this.handlePaste();
}
else if(e.keyCode == 88) {
this.handleCut();
}
return true;
},
keyPress: function(e){
var c = null;
if (e.which == null) {
// IE.
c = String.fromCharCode(e.keyCode);
} else if (e.which > 0) {
c = String.fromCharCode(e.which);
}
var self = this;
if(this.cutElement && c === 'x'){
if(ice.dom.isBrowser("webkit")){
self.cutElement.focus();
}
} else if (c === 'v'){
if(ice.dom.isBrowser("webkit")){
var div = document.getElementById(self._pasteId);
div.focus();
}
}
return true;
},
handleCopy: function(e) {},
// Inserts a temporary placeholder for the current range and removes
// the contents of the ice element body and calls a paste handler.
handlePaste: function(e) {
var range = this._ice.getCurrentRange();
if(!range.collapsed) {
if(this._ice.isTracking) {
this._ice.deleteContents();
range = range.cloneRange();
} else {
range.deleteContents();
range.collapse(true);
}
}
if(this._ice.isTracking)
this._ice._moveRangeToValidTrackingPos(range);
if(range.startContainer == this._ice.element) {
// Fix a potentially empty body with a bad selection
var firstBlock = ice.dom.find(this._ice.element, this._ice.blockEl)[0];
if(!firstBlock) {
firstBlock = ice.dom.create('<' + this._ice.blockEl + ' ><br/></' + this._ice.blockEl + '>');
this._ice.element.appendChild(firstBlock);
}
range.setStart(firstBlock, 0);
range.collapse(true);
this._ice.env.selection.addRange(range);
}
this._tmpNode = this._ice.env.document.createElement(this._tmpNodeTagName);
range.insertNode(this._tmpNode);
switch (this.pasteType) {
case 'formatted':
this.setupPaste();
break;
case 'formattedClean':
this.setupPaste(true);
break;
}
return true;
},
// Create a temporary div and set focus to it so that the browser can paste into it.
// Set a timeout to push a paste handler on to the end of the execution stack.
setupPaste: function(stripTags) {
var div = this.createDiv(this._pasteId), self = this;
div.onpaste = function() {
setTimeout(function(){
self.handlePasteValue(stripTags);
},0);
};
if(this._ice.env.frame){
if(ice.dom.isBrowser("webkit")){
div.blur();
setTimeout(function(){
div.focus();
}, 0);
} else {
div.focus();
}
}
else{
div.focus();
}
return true;
},
// By the time we get here, the pasted content will already be in the body. Extract the
// paste, format it, remove any Microsoft or extraneous tags outside of `this.preserve`
// and merge the pasted content into the original fragment body.
handlePasteValue: function(stripTags) {
// Get the pasted content.
var html = ice.dom.getHtml(document.getElementById(this._pasteId));
var childBlocks = ice.dom.children('<div>' + html + '</div>', this._ice.blockEl);
if(childBlocks.length === 1 && ice.dom.getNodeTextContent('<div>' + html + '</div>') === ice.dom.getNodeTextContent(childBlocks)) {
html = ice.dom.getHtml(html);
}
html = this.beforePasteClean.call(this, html);
if(stripTags) {
// Strip out change tracking tags.
html = this._ice.getCleanContent(html);
html = this.stripPaste(html);
}
html = this.afterPasteClean.call(this, html);
html = ice.dom.trim(html);
var range = this._ice.getCurrentRange();
range.setStartAfter(this._tmpNode);
range.collapse(true);
var innerBlock = null, lastEl = null, newEl = null;
var fragment = range.createContextualFragment(html);
var changeid = this._ice.startBatchChange();
// If fragment contains block level elements, most likely we will need to
// do some splitting so we do not have P tags in P tags, etc. Split the
// container from current selection and then insert paste contents after it.
if(ice.dom.hasBlockChildren(fragment)) {
// Split from current selection.
var block = ice.dom.isChildOfTagName(this._tmpNode, this._ice.blockEl);
range.setEndAfter(block.lastChild);
this._ice.selection.addRange(range);
var contents = range.extractContents();
var newblock = this._ice.env.document.createElement(this._ice.blockEl);
newblock.appendChild(contents);
ice.dom.insertAfter(block, newblock);
range.setStart(newblock, 0);
range.collapse(true);
this._ice.selection.addRange(range);
var prevBlock = range.startContainer;
// Paste all of the children in the fragment.
while(fragment.firstChild) {
if(fragment.firstChild.nodeType === 3 && !jQuery.trim(fragment.firstChild.nodeValue)) {
fragment.removeChild(fragment.firstChild);
continue;
}
// We may have blocks with text nodes at the beginning or end. For example, this paste:
// textnode <p>blocktext</p> <p>blocktext</p> moretext
// In which case we wrap the leading or trailing text nodes in blocks.
if(ice.dom.isBlockElement(fragment.firstChild)) {
if(fragment.firstChild.textContent !== "") {
innerBlock = null;
var insert = null;
if(this._ice.isTracking) {
insert = this._ice.createIceNode('insertType');
this._ice.addChange('insertType', [insert]);
newEl = document.createElement(fragment.firstChild.tagName);
insert.innerHTML = fragment.firstChild.innerHTML;
newEl.appendChild(insert);
} else {
insert = newEl = document.createElement(fragment.firstChild.tagName);
newEl.innerHTML = fragment.firstChild.innerHTML;
}
lastEl = insert;
ice.dom.insertBefore(prevBlock, newEl);
}
fragment.removeChild(fragment.firstChild);
} else {
if(!innerBlock) {
// Create a new block and append an insert
newEl = document.createElement(this._ice.blockEl);
ice.dom.insertBefore(prevBlock, newEl);
if(this._ice.isTracking) {
innerBlock = this._ice.createIceNode('insertType');
this._ice.addChange('insertType', [innerBlock]);
newEl.appendChild(innerBlock);
} else {
innerBlock = newEl;
}
}
lastEl = innerBlock;
innerBlock.appendChild(fragment.removeChild(fragment.firstChild));
}
}
if (!newblock.textContent) {
newblock.parentNode.removeChild(newblock);
}
} else {
if(this._ice.isTracking) {
newEl = this._ice.createIceNode('insertType', fragment);
this._ice.addChange('insertType', [newEl]);
range.insertNode(newEl);
lastEl = newEl;
} else {
var child;
while((child = fragment.firstChild)) {
range.insertNode(child);
range.setStartAfter(child);
range.collapse(true);
lastEl = child;
}
}
}
this._ice.endBatchChange(changeid);
this._cleanup(lastEl);
},
createDiv: function(id) {
var oldEl = ice.dom.getId(id);
if(oldEl) {
ice.dom.remove(oldEl);
}
var div = this._ice.env.document.createElement('div');
div.id = id;
div.setAttribute('contentEditable', true);
ice.dom.setStyle(div, 'width', '1px');
ice.dom.setStyle(div, 'height', '1px');
ice.dom.setStyle(div, 'overflow', 'hidden');
ice.dom.setStyle(div, 'position', 'fixed');
ice.dom.setStyle(div, 'top', '10px');
ice.dom.setStyle(div, 'left', '10px');
document.body.appendChild(div);
return div;
},
// Intercepts cut operation and handles by creating an editable div, copying the current selection
// into it, deleting the current selection with track changes, and selecting the contents in the
// editable div.
handleCut: function() {
this.cutElementId = 'icecut';
this.cutElement = this.createDiv(this.cutElementId);
var range = this._ice.getCurrentRange();
if(range.collapsed) return;
var html = range.getHTMLContents();
if (this._ice.isTracking) this._ice.deleteContents();
else range.deleteContents();
// var crange = range.cloneRange();
// var crange = rangy.createRange();
var crange = document.createRange();
// crange.collapse(true);
this.cutElement.innerHTML = html;
crange.setStart(this.cutElement.firstChild, 0);
crange.setEndAfter(this.cutElement.lastChild);
var self = this;
// this.cutElement.blur();
if(this._ice.env.frame){
// TINYMCE
setTimeout(function(){
self.cutElement.focus();
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
setTimeout(function() {
ice.dom.remove(self.cutElement);
range.setStart(range.startContainer, range.startOffset);
range.collapse(false);
self._ice.env.selection.addRange(range);
// Set focus back to ice element.
if(self._ice.env.frame) {
self._ice.env.frame.contentWindow.focus();
} else {
self._ice.element.focus();
}
}, 100);
}, 0);
} else {
// Vanilla Div
setTimeout(function(){
self.cutElement.focus();
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
setTimeout(function() {
range.setStart(range.startContainer, range.startOffset);
range.collapse(false);
self._ice.env.selection.addRange(range);
// Set focus back to ice element.
if(self._ice.env.frame) {
self._ice.env.frame.contentWindow.focus();
} else {
self._ice.element.focus();
}
}, 100);
}, 0);
if(ice.dom.getWebkitType() === "chrome"){
self.cutElement.focus();
}
}
self._ice.env.selection.addRange(crange);
},
// Strips ice change tracking tags, Microsoft Word styling/content, and any
// tags and attributes not found in `preserve` from the given `content`.
stripPaste: function(content) {
// Clean word stuff out and strip tags that are not in `this.preserve`.
content = this._cleanWordPaste(content);
content = this.cleanPreserved(content);
return content;
},
// Parses `preserve` to setup `_tags` with a comma delimited list of all of the
// defined tags, and the `_attributesMap` with a mapping between the allowed tags and
// an array of their allowed attributes. For example, given this value:
// `preserve` = 'p,a[href|class],span[*]'
// The following will result:
// `_tags` = 'p,a,span'
// `_attributesMap` = ['p' => [], 'a' => ['href', 'class'], 'span' => ['*']]
setupPreserved: function() {
var self = this;
this._tags = '';
this._attributesMap = [];
ice.dom.each(this.preserve.split(','), function(i, tagAttr) {
// Extract the tag and attributes list
tagAttr.match(/(\w+)(\[(.+)\])?/);
var tag = RegExp.$1;
var attr = RegExp.$3;
if(self._tags) self._tags += ',';
self._tags += tag.toLowerCase();
self._attributesMap[tag] = attr.split('|');
});
},
// Cleans the given `body` by removing any tags not found in `_tags` and replacing them with
// their inner contents, and removes attributes from any tags that aren't mapped in `_attributesMap`.
cleanPreserved: function(body) {
var self = this;
var bodyel = this._ice.env.document.createElement('div');
bodyel.innerHTML = body;
// Strip out any tags not found in `this._tags`, replacing the tags with their inner contents.
bodyel = ice.dom.stripEnclosingTags(bodyel, this._tags);
// Strip out any attributes from the allowed set of tags that don't match what is in the `_attributesMap`
ice.dom.each(ice.dom.find(bodyel, this._tags), function(i, el) {
if (ice.dom.hasClass(el, 'skip-clean')) {
return true;
}
var tag = el.tagName.toLowerCase();
var attrMatches = self._attributesMap[tag];
// Kleene star - keep all of the attributes for this tag.
if(attrMatches[0] && attrMatches[0] === '*')
return true;
// Remove any foreign attributes that do not match the map.
if(el.hasAttributes()) {
var attributes = el.attributes;
for(var i = attributes.length - 1; i >= 0; i--) {
if(!ice.dom.inArray(attributes[i].name, attrMatches)) {
el.removeAttribute(attributes[i].name);
}
}
}
});
return bodyel.innerHTML;
},
_cleanWordPaste: function(content) {
// Meta and link tags.
content = content.replace(/<(meta|link)[^>]+>/g, "");
// Comments.
content = content.replace(/<!--(.|\s)*?-->/g, '');
// Remove style tags.
content = content.replace(/<style>[\s\S]*?<\/style>/g, '');
// Remove span and o:p etc. tags.
//content = content.replace(/<\/?span[^>]*>/gi, "");
content = content.replace(/<\/?\w+:[^>]*>/gi, '' );
// Remove XML tags.
content = content.replace(/<\\?\?xml[^>]*>/gi, '');
// Generic cleanup.
content = this._cleanPaste(content);
// Remove class, lang and style attributes.
content = content.replace(/<(\w[^>]*) (lang)=([^ |>]*)([^>]*)/gi, "<$1$4");
return content;
},
_cleanPaste: function(content) {
// Some generic content cleanup. Change all b/i tags to strong/em.
content = content.replace(/<b(\s+|>)/g, "<strong$1");
content = content.replace(/<\/b(\s+|>)/g, "</strong$1");
content = content.replace(/<i(\s+|>)/g, "<em$1");
content = content.replace(/<\/i(\s+|>)/g, "</em$1");
return content;
},
_cleanup: function(moveTo) {
try {
moveTo = moveTo && moveTo.lastChild || moveTo || this._tmpNode;
// Move the range to the end of moveTo so that the cursor will be at the end of the paste.
var range = this._ice.getCurrentRange();
range.setStartAfter(moveTo);
range.collapse(true);
this._ice.selection.addRange(range);
// Set focus back to ice element.
if(this._ice.env.frame) {
this._ice.env.frame.contentWindow.focus();
} else {
this._ice.element.focus();
}
// Kill the tmp node.
this._tmpNode.parentNode.removeChild(this._tmpNode);
this._tmpNode = null;
// Kill any empty change nodes.
var ins = this._ice.env.document.getElementsByClassName(this._ice.changeTypes['insertType'].alias);
for(var i = 0; i < ins.length; i++) {
if(!ins[i].textContent) {
if(ins[i].parentNode) {
ins[i].parentNode.removeChild(ins[i]);
}
}
}
} catch(e) {
window.console && console.error(e);
}
}
};
ice.dom.noInclusionInherits(IceCopyPastePlugin, ice.IcePlugin);
exports._plugin.IceCopyPastePlugin = IceCopyPastePlugin;
}).call(this.ice);

View File

@@ -0,0 +1,76 @@
(function() {
var exports = this;
/**
* When active, this plugin will convert two successively typed dashes, within
* the ice block element, into an emdash.
*/
var IceEmdashPlugin = function(ice_instance) {
this._ice = ice_instance;
};
IceEmdashPlugin.prototype = {
keyDown: function(e) {
// Catch dashes.
if(ice.dom.isBrowser('mozilla')) {
var version = parseInt(ice.dom.browser().version);
if ( (version > 14 && e.keyCode === 173) || (version <= 14 && e.keyCode === 109) ) {
return this.convertEmdash(e);
}
} else if(e.keyCode === 189) {
return this.convertEmdash(e);
}
return true;
},
convertEmdash: function(e) {
var range = this._ice.getCurrentRange();
if(range.collapsed) {
try {
// Move the start back one character so we can enclose the range around the previous character to check if it is a dash
range.moveStart(ice.dom.CHARACTER_UNIT, -1);
// Get the parent block element for the start and end containers
var startBlock = ice.dom.getParents(range.startContainer, this._ice.blockEl)[0];
var endBlock = ice.dom.getParents(range.endContainer, this._ice.blockEl)[0];
// Make sure that the start and end containers aren't in different blocks, or that the start isn't in a delete.
if(startBlock === endBlock && !this._ice.getIceNode(range.startContainer, 'deleteType')) {
// Get the last character and check to see if it is a dash.
c = range.toHtml();
if(c === '-') {
// Extract the last character/dash and insert an emdash
range.extractContents();
range.collapse();
var mdash = this._ice.env.document.createTextNode('\u2014');
if (this._ice.isTracking) {
this._ice._insertNode(mdash, range);
} else {
range.insertNode(mdash);
/* TO be reverted once mozilla fixes FF 15 issue */
range.setStart(mdash, 1);
range.collapse(true);
/* FINISH revert */
}
/* TO be reverted once mozilla fixes FF 15 issue
range = this._ice.getCurrentRange();
range.moveStart(ice.dom.CHARACTER_UNIT, 1);
range.collapse(true);
this._ice.env.selection.addRange(range);
*/
this._ice._preventKeyPress = true;
return false;
}
}
} catch(e) {}
range.collapse();
}
return true;
}
};
ice.dom.noInclusionInherits(IceEmdashPlugin, ice.IcePlugin);
exports._plugin.IceEmdashPlugin = IceEmdashPlugin;
}).call(this.ice);

View File

@@ -0,0 +1,184 @@
(function() {
var exports = this, ice = this.ice;
var IceSmartQuotesPlugin = function(ice_instance) {
this._ice = ice_instance;
};
IceSmartQuotesPlugin.prototype = {
/**
* Finds each block in `element` and converts quotes into smart quotes.
*/
convert: function(element) {
var self = this;
try {
self._ice.placeholdDeletes();
ice.dom.each(element.getElementsByTagName(this._ice.blockEl), function(i, el) {
self._convertBlock(el);
});
} catch(e) {
window.console && console.error(e);
} finally {
self._ice.revertDeletePlaceholders();
}
},
// Converts the quotes in the given element to smart quotes.
_convertBlock: function(el) {
// If there are less than 2 characters, we don't have enough to go on.
if (ice.dom.getNodeTextContent(el) < 2) return;
var previous, current, next, data, html = '', getNextChar,
regularSingle = "'",
regularDouble = '"',
smartSingleLeft = String.fromCharCode(8216), // aka - open curly single quote
smartSingleRight = String.fromCharCode(8217), // aka - close curly single quote
smartDoubleLeft = String.fromCharCode(8220), // aka - open curly double quote
smartDoubleRight = String.fromCharCode(8221), // aka - close curly double quote
isDigit = function(c) { return /\d/.test(c); },
isChar = function(c) { return /\w/.test(c); },
isSpace = function(c) { return c === String.fromCharCode(160) || c === String.fromCharCode(32); },
isStartChar = function(c) { return isSpace(c) || c === '('; },
isEndChar = function(c) { return isSpace(c) || c == null || c === ';' || c === ')' || c == '.' || c === '!' || c === ',' || c === '?' || c === ':'; },
isNonSpace = function(c) { return !isSpace(c); },
isDouble = function(c) { return c === regularDouble || c === smartDoubleLeft || c === smartDoubleRight; },
isSingle = function(c) { return c === regularSingle || c === smartSingleLeft || c === smartSingleRight; };
// Split the html into array allocations with the following criteria:
// html tags: starts with "<" and ends with ">"
// html entities: starts with "&" and ends with ";"
// characters: any character outside of an html tag or entity
// So the following html:
// n&ce <b id="bold">test</b>
// Would split into the following array:
// ['n', '&amp;', 'c', 'e', ' ', '<b id="bold">', 't', 'e', 's', 't', '</b>'];
data = ice.dom.getHtml(el).match(/(<("[^"]*"|'[^']*'|[^'">])*>|&.*;|.)/g);
// Searches through the `data` array from the given index a given number of
// characters forward or backward and returns the found character.
getNextChar = function(data, fromIndex, nCharacters) {
var dLength = data.length,
addWith = nCharacters < 0 ? -1 : 1;
return (function getChar(data, fromIndex, nCharacters) {
// Base case - did we move outside of the bounds of the data array?
if (fromIndex < 0 || fromIndex >= dLength) return null;
var next = data[fromIndex + addWith];
// If we find a character and we have moved through the
// nCharacters, then the recursion is done.
if (next && next.length == 1) {
nCharacters += (addWith * -1);
if (!nCharacters) return next;
}
return getChar(data, fromIndex + addWith, nCharacters);
})(data, fromIndex, nCharacters);
};
ice.dom.each(data, function(pos, val) {
// Convert space entities so that they can be processed as normal characters.
if (val == '&nbsp;') val = data[pos] = ' ';
// If the val is a character, then examine the surroundings
// and convert smart quotes, if necessary.
if (val.length == 1) {
// Keep convenience pointers to the previous, current and next characters.
previous = getNextChar(data, pos, -1);
current = val;
next = getNextChar(data, pos, 1);
switch (current) {
/**
* Conversion Rules:
* ----------------
*
* START: assign smart left/open
* [SPACE|START_PARENTHESIS]'word
* [SPACE|START_PARENTHESIS]"word
*
* END: assign smart right/close
* word'[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
* word"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
*
* PLURAL_CONTRACTION: assign smart right/close
* Matt's
* can't
* O'Reilly
*
* YEAR_ABBREVIATION: assign smart right/close
* [SPACE|NULL]'99[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
*
* NESTED_START: assign smart left/open
* [SPACE|NULL]"[SPACE]'word
*
* NESTED_END: assign smart left/open
* word'[SPACE]"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
*
* Notes:
* - The following will not be converted correctly - ...word 'Til Death - it should
* get a right/close smart quote, but will get a left/open.
* - Distinguishing between year abbreviation, '99, and when to use an open single quote
* could fail if a single quoted region starts with a double digit number - '99 problems'
* - Since they are a rare case and there are many permutations, measurements are not being
* handled (6'8", 6' 8", 6', 8").
*/
// Convert smart single quotes to non-smart quote and fall through to single quote
// handling, in case the context has changed and we need to update the smart quote.
case smartSingleLeft:
case smartSingleRight:
current = regularSingle;
case regularSingle:
// YEAR_ABBREVIATION - look 2 ahead to see if there are two digits in a row - not fool proof
if ((previous == null || isSpace(previous)) && isDigit(next) && isDigit(getNextChar(data, pos, 2)) && isEndChar(getNextChar(data, pos, 3)))
current = smartSingleRight;
// START
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
current = smartSingleLeft;
// END
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
current = smartSingleRight;
// PLURAL_CONTRACTION
else if (isChar(previous) && isChar(next))
current = smartSingleRight;
break;
// Convert smart double quotes to non-smart quote and fall through to double quote
// handling, in case the context has changed and we need to update the smart quote.
case smartDoubleLeft:
case smartDoubleRight:
current = regularDouble;
case regularDouble:
// NESTED_END
if (isEndChar(next) && isSpace(previous) && isSingle(getNextChar(data, pos, -2)))
current = smartDoubleRight;
// START
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
current = smartDoubleLeft;
// END
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
current = smartDoubleRight;
// NESTED_START
else if ((previous == null || isSpace(previous)) && (isSpace(next) && isSingle(getNextChar(data, pos, 1))))
current = smartDoubleLeft;
break;
}
if (current != null) data[pos] = current;
}
});
ice.dom.setHtml(el, data.join(''));
}
};
ice.dom.noInclusionInherits(IceSmartQuotesPlugin, ice.IcePlugin);
exports.ice._plugin.IceSmartQuotesPlugin = IceSmartQuotesPlugin;
}).call(this);