summaryrefslogtreecommitdiff
path: root/program/js/tiny_mce/tiny_mce_src.js
diff options
context:
space:
mode:
authoralecpl <alec@alec.pl>2010-06-24 13:22:08 +0000
committeralecpl <alec@alec.pl>2010-06-24 13:22:08 +0000
commit2011bef155aacdfa8461a4d5c2cd3988d946d135 (patch)
tree15eb4903597ded4ea1d6cbe3c9e18d6d31f13f0a /program/js/tiny_mce/tiny_mce_src.js
parentf821fecac880c8e4b3ca33897b6a32c140348c65 (diff)
- TinyMCE 3.3.7
Diffstat (limited to 'program/js/tiny_mce/tiny_mce_src.js')
-rw-r--r--program/js/tiny_mce/tiny_mce_src.js1039
1 files changed, 688 insertions, 351 deletions
diff --git a/program/js/tiny_mce/tiny_mce_src.js b/program/js/tiny_mce/tiny_mce_src.js
index c4a626508..9db8d18fe 100644
--- a/program/js/tiny_mce/tiny_mce_src.js
+++ b/program/js/tiny_mce/tiny_mce_src.js
@@ -5,9 +5,9 @@
var tinymce = {
majorVersion : '3',
- minorVersion : '3.2',
+ minorVersion : '3.7',
- releaseDate : '2010-03-25',
+ releaseDate : '2010-06-10',
_init : function() {
var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -26,6 +26,8 @@
t.isAir = /adobeair/i.test(ua);
+ t.isIDevice = /(iPad|iPhone)/.test(ua);
+
// TinyMCE .NET webcontrol might be setting the values for TinyMCE
if (win.tinyMCEPreInit) {
t.suffix = tinyMCEPreInit.suffix;
@@ -1262,7 +1264,7 @@ tinymce.create('static tinymce.util.XHR', {
if (keep_children) {
while (child = node.firstChild) {
// IE 8 will crash if you don't remove completely empty text nodes
- if (child.nodeType !== 3 || child.nodeValue)
+ if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)
parent.insertBefore(child, node);
else
node.removeChild(child);
@@ -1908,7 +1910,7 @@ tinymce.create('static tinymce.util.XHR', {
// So if we replace the p elements with divs and mark them and then replace them back to paragraphs
// after we use innerHTML we can fix the DOM tree
h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');
- h = h.replace(/<\/p>/g, '</div>');
+ h = h.replace(/<\/p>/gi, '</div>');
// Set the new HTML with DIVs
set();
@@ -2412,10 +2414,13 @@ tinymce.create('static tinymce.util.XHR', {
for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
nodeType = node.nodeType;
- // Handle normalization of text nodes
- if (!normalized || nodeType != 3 || (lastNodeType != nodeType && node.nodeValue.length))
- idx++;
+ // Normalize text nodes
+ if (normalized && nodeType == 3) {
+ if (nodeType == lastNodeType || !node.nodeValue.length)
+ continue;
+ }
+ idx++;
lastNodeType = nodeType;
}
}
@@ -3255,35 +3260,9 @@ tinymce.create('static tinymce.util.XHR', {
function Selection(selection) {
var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;
- // Compares two IE specific ranges to see if they are different
- // this method is useful when invalidating the cached selection range
- function compareRanges(rng1, rng2) {
- if (rng1 && rng2) {
- // Both are control ranges and the selected element matches
- if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
- return TRUE;
-
- // Both are text ranges and the range matches
- if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
- // IE will say that the range is equal then produce an invalid argument exception
- // if you perform specific operations in a keyup event. For example Ctrl+Del.
- // This hack will invalidate the range cache if the exception occurs
- try {
- // Try accessing nextSibling will producer an invalid argument some times
- range.startContainer.nextSibling;
- return TRUE;
- } catch (ex) {
- // Ignore
- }
- }
- }
-
- return FALSE;
- };
-
// Returns a W3C DOM compatible range object by using the IE Range API
function getRange() {
- var ieRange = selection.getRng(), domRange = dom.createRng(), ieRange2, element, collapsed, isMerged;
+ var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;
// If selection is outside the current document just return an empty range
element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
@@ -3298,84 +3277,96 @@ tinymce.create('static tinymce.util.XHR', {
return domRange;
}
- // Duplicare IE selection range and check if the range is collapsed
- ieRange2 = ieRange.duplicate();
collapsed = selection.isCollapsed();
- // Insert invisible start marker
- ieRange.collapse();
- ieRange.pasteHTML('<span id="_mce_start" style="display:none;line-height:0">' + invisibleChar + '</span>');
+ function findEndPoint(start) {
+ var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;
- // Insert invisible end marker
- if (!collapsed) {
- ieRange2.collapse(FALSE);
- ieRange2.pasteHTML('<span id="_mce_end" style="display:none;line-height:0">' + invisibleChar + '</span>');
- }
+ // Setup temp range and collapse it
+ checkRng = ieRange.duplicate();
+ checkRng.collapse(start);
- // Sets the end point of the range by looking for the marker
- // This method also merges the text nodes it splits so that
- // the DOM doesn't get fragmented.
- function setEndPoint(start) {
- var container, offset, marker, sibling;
+ // Create marker and insert it at the end of the endpoints parent
+ marker = dom.create('a');
+ parent = checkRng.parentElement();
- // Look for endpoint marker
- marker = dom.get('_mce_' + (start ? 'start' : 'end'));
- sibling = marker.previousSibling;
+ // If parent doesn't have any children then set the container to that parent and the index to 0
+ if (!parent.hasChildNodes()) {
+ domRange[start ? 'setStart' : 'setEnd'](parent, 0);
+ return;
+ }
- // Is marker after a text node
- if (sibling && sibling.nodeType == 3) {
- // Get container node and calc offset
- container = sibling;
- offset = container.nodeValue.length;
+ parent.appendChild(marker);
+ checkRng.moveToElementText(marker);
+ position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);
+ if (position > 0) {
+ // The position is after the end of the parent element.
+ // This is the case where IE puts the caret to the left edge of a table.
+ domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);
dom.remove(marker);
+ return;
+ }
- // Merge text nodes to reduce DOM fragmentation
- sibling = container.nextSibling;
- if (sibling && sibling.nodeType == 3) {
- isMerged = TRUE;
- container.appendData(sibling.nodeValue);
- dom.remove(sibling);
+ // Setup node list and endIndex
+ nodes = tinymce.grep(parent.childNodes);
+ endIndex = nodes.length - 1;
+ // Perform a binary search for the position
+ while (startIndex <= endIndex) {
+ index = Math.floor((startIndex + endIndex) / 2);
+
+ // Insert marker and check it's position relative to the selection
+ parent.insertBefore(marker, nodes[index]);
+ checkRng.moveToElementText(marker);
+ position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);
+ if (position > 0) {
+ // Marker is to the right
+ startIndex = index + 1;
+ } else if (position < 0) {
+ // Marker is to the left
+ endIndex = index - 1;
+ } else {
+ // Maker is where we are
+ found = true;
+ break;
}
- } else {
- sibling = marker.nextSibling;
+ }
- // Is marker before a text node
- if (sibling && sibling.nodeType == 3) {
- container = sibling;
- offset = 0;
- } else {
- // Is marker before an element
- if (sibling)
- offset = dom.nodeIndex(sibling) - 1;
- else
- offset = dom.nodeIndex(marker);
+ // Setup container
+ container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;
+
+ // Handle element selection
+ if (container.nodeType == 1) {
+ dom.remove(marker);
+
+ // Find offset and container
+ offset = dom.nodeIndex(container);
+ container = container.parentNode;
- container = marker.parentNode;
+ // Move the offset if we are setting the end or the position is after an element
+ if (!start || index > 0)
+ offset++;
+ } else {
+ // Calculate offset within text node
+ if (position > 0 || index == 0) {
+ checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);
+ offset = checkRng.text.length;
+ } else {
+ checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);
+ offset = container.nodeValue.length - checkRng.text.length;
}
dom.remove(marker);
}
- // Set start of range
- if (start)
- domRange.setStart(container, offset);
-
- // Set end of range or automatically if it's collapsed to increase performance
- if (!start || collapsed)
- domRange.setEnd(container, offset);
+ domRange[start ? 'setStart' : 'setEnd'](container, offset);
};
- // Set start of range
- setEndPoint(TRUE);
+ // Find start point
+ findEndPoint(true);
- // Set end of range if needed
+ // Find end point if needed
if (!collapsed)
- setEndPoint(FALSE);
-
- // Restore selection if the range contents was merged
- // since the selection was then moved since the text nodes got changed
- if (isMerged)
- t.addRange(domRange);
+ findEndPoint();
return domRange;
};
@@ -3479,7 +3470,10 @@ tinymce.create('static tinymce.util.XHR', {
// Select marker the caret to offset position
ieRng.moveToElementText(marker);
marker.parentNode.removeChild(marker);
- ieRng.move('character', so);
+
+ // Move if we need to, moving it 0 characters actually moves it!
+ if (so > 0)
+ ieRng.move('character', so);
} else {
ieRng.moveToElementText(sc);
@@ -3489,9 +3483,17 @@ tinymce.create('static tinymce.util.XHR', {
// If same text container then we can do a more simple move
if (sc == ec && sc.nodeType == 3) {
- ieRng.moveEnd('character', eo - so);
- ieRng.select();
- ieRng.scrollIntoView();
+ try {
+ ieRng.moveEnd('character', eo - so);
+ ieRng.select();
+ ieRng.scrollIntoView();
+ } catch (ex) {
+ // Some times a Runtime error of the 800a025e type gets thrown
+ // especially when the caret is placed before a table.
+ // This is a somewhat strange location for the caret.
+ // TODO: Find a better solution for this would possible require a rewrite of the setRng method
+ }
+
return;
}
@@ -3518,13 +3520,23 @@ tinymce.create('static tinymce.util.XHR', {
this.getRangeAt = function() {
// Setup new range if the cache is empty
- if (!range || !compareRanges(lastIERng, selection.getRng())) {
+ if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {
range = getRange();
// Store away text range for next call
lastIERng = selection.getRng();
}
+ // IE will say that the range is equal then produce an invalid argument exception
+ // if you perform specific operations in a keyup event. For example Ctrl+Del.
+ // This hack will invalidate the range cache if the exception occurs
+ try {
+ range.startContainer.nextSibling;
+ } catch (ex) {
+ range = getRange();
+ lastIERng = null;
+ }
+
// Return cached range
return range;
};
@@ -3621,14 +3633,26 @@ tinymce.create('static tinymce.util.XHR', {
*/
(function(){
-var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
done = 0,
toString = Object.prototype.toString,
- hasDuplicate = false;
+ hasDuplicate = false,
+ baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+ baseHasDuplicate = false;
+ return 0;
+});
var Sizzle = function(selector, context, results, seed) {
results = results || [];
- var origContext = context = context || document;
+ context = context || document;
+
+ var origContext = context;
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
@@ -3638,19 +3662,25 @@ var Sizzle = function(selector, context, results, seed) {
return results;
}
- var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
+ var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
+ soFar = selector, ret, cur, pop, i;
// Reset the position of the chunker regexp (start from head)
- chunker.lastIndex = 0;
-
- while ( (m = chunker.exec(selector)) !== null ) {
- parts.push( m[1] );
+ do {
+ chunker.exec("");
+ m = chunker.exec(soFar);
+
+ if ( m ) {
+ soFar = m[3];
- if ( m[2] ) {
- extra = RegExp.rightContext;
- break;
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
}
- }
+ } while ( m );
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
@@ -3663,9 +3693,10 @@ var Sizzle = function(selector, context, results, seed) {
while ( parts.length ) {
selector = parts.shift();
- if ( Expr.relative[ selector ] )
+ if ( Expr.relative[ selector ] ) {
selector += parts.shift();
-
+ }
+
set = posProcess( selector, set );
}
}
@@ -3674,12 +3705,12 @@ var Sizzle = function(selector, context, results, seed) {
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
- var ret = Sizzle.find( parts.shift(), context, contextXML );
+ ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
}
if ( context ) {
- var ret = seed ?
+ ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
@@ -3691,7 +3722,8 @@ var Sizzle = function(selector, context, results, seed) {
}
while ( parts.length ) {
- var cur = parts.pop(), pop = cur;
+ cur = parts.pop();
+ pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
@@ -3715,20 +3747,20 @@ var Sizzle = function(selector, context, results, seed) {
}
if ( !checkSet ) {
- throw "Syntax error, unrecognized expression: " + (cur || selector);
+ Sizzle.error( cur || selector );
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
- for ( var i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
- for ( var i = 0; checkSet[i] != null; i++ ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
@@ -3748,7 +3780,7 @@ var Sizzle = function(selector, context, results, seed) {
Sizzle.uniqueSort = function(results){
if ( sortOrder ) {
- hasDuplicate = false;
+ hasDuplicate = baseHasDuplicate;
results.sort(sortOrder);
if ( hasDuplicate ) {
@@ -3759,6 +3791,8 @@ Sizzle.uniqueSort = function(results){
}
}
}
+
+ return results;
};
Sizzle.matches = function(expr, set){
@@ -3766,7 +3800,7 @@ Sizzle.matches = function(expr, set){
};
Sizzle.find = function(expr, context, isXML){
- var set, match;
+ var set;
if ( !expr ) {
return [];
@@ -3775,8 +3809,9 @@ Sizzle.find = function(expr, context, isXML){
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
- if ( (match = Expr.match[ type ].exec( expr )) ) {
- var left = RegExp.leftContext;
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice(1,1);
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
@@ -3798,15 +3833,21 @@ Sizzle.find = function(expr, context, isXML){
Sizzle.filter = function(expr, set, inplace, not){
var old = expr, result = [], curLoop = set, match, anyFound,
- isXMLFilter = set && set[0] && isXML(set[0]);
+ isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
- if ( (match = Expr.match[ type ].exec( expr )) != null ) {
- var filter = Expr.filter[ type ], found, item;
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var filter = Expr.filter[ type ], found, item, left = match[1];
anyFound = false;
- if ( curLoop == result ) {
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
result = [];
}
@@ -3857,9 +3898,9 @@ Sizzle.filter = function(expr, set, inplace, not){
}
// Improper expression
- if ( expr == old ) {
+ if ( expr === old ) {
if ( anyFound == null ) {
- throw "Syntax error, unrecognized expression: " + expr;
+ Sizzle.error( expr );
} else {
break;
}
@@ -3871,18 +3912,23 @@ Sizzle.filter = function(expr, set, inplace, not){
return curLoop;
};
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
var Expr = Sizzle.selectors = {
order: [ "ID", "NAME", "TAG" ],
match: {
- ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
- CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
- NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
- PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
+ leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
@@ -3893,20 +3939,20 @@ var Expr = Sizzle.selectors = {
}
},
relative: {
- "+": function(checkSet, part, isXML){
+ "+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test(part),
isPartStrNotTag = isPartStr && !isTag;
- if ( isTag && !isXML ) {
- part = part.toUpperCase();
+ if ( isTag ) {
+ part = part.toLowerCase();
}
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
if ( (elem = checkSet[i]) ) {
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
elem || false :
elem === part;
}
@@ -3916,22 +3962,23 @@ var Expr = Sizzle.selectors = {
Sizzle.filter( part, checkSet, true );
}
},
- ">": function(checkSet, part, isXML){
- var isPartStr = typeof part === "string";
+ ">": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ elem, i = 0, l = checkSet.length;
if ( isPartStr && !/\W/.test(part) ) {
- part = isXML ? part : part.toUpperCase();
+ part = part.toLowerCase();
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
- checkSet[i] = parent.nodeName === part ? parent : false;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
} else {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
@@ -3945,20 +3992,22 @@ var Expr = Sizzle.selectors = {
}
},
"": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck;
+ var doneName = done++, checkFn = dirCheck, nodeCheck;
- if ( !part.match(/\W/) ) {
- var nodeCheck = part = isXML ? part : part.toUpperCase();
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
checkFn = dirNodeCheck;
}
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
},
"~": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck;
+ var doneName = done++, checkFn = dirCheck, nodeCheck;
- if ( typeof part === "string" && !part.match(/\W/) ) {
- var nodeCheck = part = isXML ? part : part.toUpperCase();
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
checkFn = dirNodeCheck;
}
@@ -3972,7 +4021,7 @@ var Expr = Sizzle.selectors = {
return m ? [m] : [];
}
},
- NAME: function(match, context, isXML){
+ NAME: function(match, context){
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [], results = context.getElementsByName(match[1]);
@@ -3999,9 +4048,10 @@ var Expr = Sizzle.selectors = {
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
- if ( !inplace )
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
result.push( elem );
+ }
} else if ( inplace ) {
curLoop[i] = false;
}
@@ -4014,14 +4064,13 @@ var Expr = Sizzle.selectors = {
return match[1].replace(/\\/g, "");
},
TAG: function(match, curLoop){
- for ( var i = 0; curLoop[i] === false; i++ ){}
- return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ return match[1].toLowerCase();
},
CHILD: function(match){
- if ( match[1] == "nth" ) {
+ if ( match[1] === "nth" ) {
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
- match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
// calculate the numbers (first)n+(last) including if they are negative
@@ -4050,7 +4099,7 @@ var Expr = Sizzle.selectors = {
PSEUDO: function(match, curLoop, inplace, result, not){
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
- if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
@@ -4096,7 +4145,7 @@ var Expr = Sizzle.selectors = {
return !!Sizzle( match[3], elem ).length;
},
header: function(elem){
- return /h\d/i.test( elem.nodeName );
+ return (/h\d/i).test( elem.nodeName );
},
text: function(elem){
return "text" === elem.type;
@@ -4123,10 +4172,10 @@ var Expr = Sizzle.selectors = {
return "reset" === elem.type;
},
button: function(elem){
- return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
},
input: function(elem){
- return /input|select|textarea|button/i.test(elem.nodeName);
+ return (/input|select|textarea|button/i).test(elem.nodeName);
}
},
setFilters: {
@@ -4149,10 +4198,10 @@ var Expr = Sizzle.selectors = {
return i > match[3] - 0;
},
nth: function(elem, i, match){
- return match[3] - 0 == i;
+ return match[3] - 0 === i;
},
eq: function(elem, i, match){
- return match[3] - 0 == i;
+ return match[3] - 0 === i;
}
},
filter: {
@@ -4162,17 +4211,19 @@ var Expr = Sizzle.selectors = {
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
- for ( var i = 0, l = not.length; i < l; i++ ) {
- if ( not[i] === elem ) {
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
return false;
}
}
return true;
+ } else {
+ Sizzle.error( "Syntax error, unrecognized expression: " + name );
}
},
CHILD: function(elem, match){
@@ -4180,20 +4231,26 @@ var Expr = Sizzle.selectors = {
switch (type) {
case 'only':
case 'first':
- while (node = node.previousSibling) {
- if ( node.nodeType === 1 ) return false;
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ if ( type === "first" ) {
+ return true;
}
- if ( type == 'first') return true;
node = elem;
case 'last':
- while (node = node.nextSibling) {
- if ( node.nodeType === 1 ) return false;
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
}
return true;
case 'nth':
var first = match[2], last = match[3];
- if ( first == 1 && last == 0 ) {
+ if ( first === 1 && last === 0 ) {
return true;
}
@@ -4211,10 +4268,10 @@ var Expr = Sizzle.selectors = {
}
var diff = elem.nodeIndex - last;
- if ( first == 0 ) {
- return diff == 0;
+ if ( first === 0 ) {
+ return diff === 0;
} else {
- return ( diff % first == 0 && diff / first >= 0 );
+ return ( diff % first === 0 && diff / first >= 0 );
}
}
},
@@ -4222,7 +4279,7 @@ var Expr = Sizzle.selectors = {
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function(elem, match){
- return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
},
CLASS: function(elem, match){
return (" " + (elem.className || elem.getAttribute("class")) + " ")
@@ -4250,7 +4307,7 @@ var Expr = Sizzle.selectors = {
!check ?
value && result !== false :
type === "!=" ?
- value != check :
+ value !== check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
@@ -4269,14 +4326,18 @@ var Expr = Sizzle.selectors = {
}
};
-var origPOS = Expr.match.POS;
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
for ( var type in Expr.match ) {
- Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
}
var makeArray = function(array, results) {
- array = Array.prototype.slice.call( array );
+ array = Array.prototype.slice.call( array, 0 );
if ( results ) {
results.push.apply( results, array );
@@ -4288,23 +4349,25 @@ var makeArray = function(array, results) {
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
try {
- Array.prototype.slice.call( document.documentElement.childNodes );
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
// Provide a fallback method if it does not work
} catch(e){
makeArray = function(array, results) {
- var ret = results || [];
+ var ret = results || [], i = 0;
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
- for ( var i = 0, l = array.length; i < l; i++ ) {
+ for ( var l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
- for ( var i = 0; array[i]; i++ ) {
+ for ( ; array[i]; i++ ) {
ret.push( array[i] );
}
}
@@ -4318,6 +4381,13 @@ var sortOrder;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
if ( ret === 0 ) {
hasDuplicate = true;
@@ -4326,6 +4396,13 @@ if ( document.documentElement.compareDocumentPosition ) {
};
} else if ( "sourceIndex" in document.documentElement ) {
sortOrder = function( a, b ) {
+ if ( !a.sourceIndex || !b.sourceIndex ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.sourceIndex ? -1 : 1;
+ }
+
var ret = a.sourceIndex - b.sourceIndex;
if ( ret === 0 ) {
hasDuplicate = true;
@@ -4334,6 +4411,13 @@ if ( document.documentElement.compareDocumentPosition ) {
};
} else if ( document.createRange ) {
sortOrder = function( a, b ) {
+ if ( !a.ownerDocument || !b.ownerDocument ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.ownerDocument ? -1 : 1;
+ }
+
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.setStart(a, 0);
aRange.setEnd(a, 0);
@@ -4347,12 +4431,32 @@ if ( document.documentElement.compareDocumentPosition ) {
};
}
+// Utility function for retreiving the text value of an array of DOM nodes
+Sizzle.getText = function( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += Sizzle.getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
// We're going to inject a fake input element with a specified name
var form = document.createElement("div"),
- id = "script" + (new Date).getTime();
+ id = "script" + (new Date()).getTime();
form.innerHTML = "<a name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
@@ -4361,7 +4465,7 @@ if ( document.documentElement.compareDocumentPosition ) {
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
- if ( !!document.getElementById( id ) ) {
+ if ( document.getElementById( id ) ) {
Expr.find.ID = function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
@@ -4376,6 +4480,7 @@ if ( document.documentElement.compareDocumentPosition ) {
}
root.removeChild( form );
+ root = form = null; // release memory in IE
})();
(function(){
@@ -4416,68 +4521,75 @@ if ( document.documentElement.compareDocumentPosition ) {
return elem.getAttribute("href", 2);
};
}
+
+ div = null; // release memory in IE
})();
-if ( document.querySelectorAll ) (function(){
- var oldSizzle = Sizzle, div = document.createElement("div");
- div.innerHTML = "<p class='TEST'></p>";
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "<p class='TEST'></p>";
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
- Sizzle = function(query, context, extra, seed){
- context = context || document;
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
- // Only use querySelectorAll on non-XML documents
- // (ID selectors don't work in non-HTML documents)
- if ( !seed && context.nodeType === 9 && !isXML(context) ) {
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(e){}
- }
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
- return oldSizzle(query, context, extra, seed);
- };
+ return oldSizzle(query, context, extra, seed);
+ };
- for ( var prop in oldSizzle ) {
- Sizzle[ prop ] = oldSizzle[ prop ];
- }
-})();
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ div = null; // release memory in IE
+ })();
+}
-if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+(function(){
var div = document.createElement("div");
+
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
// Opera can't find a second classname (in 9.6)
- if ( div.getElementsByClassName("e").length === 0 )
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
return;
+ }
// Safari caches class attributes, doesn't catch changes (in 3.2)
div.lastChild.className = "e";
- if ( div.getElementsByClassName("e").length === 1 )
+ if ( div.getElementsByClassName("e").length === 1 ) {
return;
-
+ }
+
Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function(match, context, isXML) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
+
+ div = null; // release memory in IE
})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
- if ( sibDir && elem.nodeType === 1 ){
- elem.sizcache = doneName;
- elem.sizset = i;
- }
elem = elem[dir];
var match = false;
@@ -4492,7 +4604,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
elem.sizset = i;
}
- if ( elem.nodeName === cur ) {
+ if ( elem.nodeName.toLowerCase() === cur ) {
match = elem;
break;
}
@@ -4506,14 +4618,9 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
}
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
- if ( sibDir && elem.nodeType === 1 ) {
- elem.sizcache = doneName;
- elem.sizset = i;
- }
elem = elem[dir];
var match = false;
@@ -4548,15 +4655,17 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
}
}
-var contains = document.compareDocumentPosition ? function(a, b){
- return a.compareDocumentPosition(b) & 16;
+Sizzle.contains = document.compareDocumentPosition ? function(a, b){
+ return !!(a.compareDocumentPosition(b) & 16);
} : function(a, b){
return a !== b && (a.contains ? a.contains(b) : true);
};
-var isXML = function(elem){
- return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
- !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
+Sizzle.isXML = function(elem){
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
};
var posProcess = function(selector, context){
@@ -5066,17 +5175,21 @@ window.tinymce.dom.Sizzle = Sizzle;
h += '<span id="__caret">_</span>';
// Delete and insert new node
- if (r.startContainer == d && r.endContainer == d) {
+
+ if (r.startContainer == d && r.endContainer == d) {
// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
d.body.innerHTML = h;
} else {
r.deleteContents();
- r.insertNode(t.getRng().createContextualFragment(h));
+ if (d.body.childNodes.length == 0) {
+ d.body.innerHTML = h;
+ } else {
+ r.insertNode(r.createContextualFragment(h));
+ }
}
// Move to caret marker
c = t.dom.get('__caret');
-
// Make sure we wrap it compleatly, Opera fails with a simple select call
r = d.createRange();
r.setStartBefore(c);
@@ -5100,30 +5213,43 @@ window.tinymce.dom.Sizzle = Sizzle;
},
getStart : function() {
- var t = this, r = t.getRng(), e;
+ var rng = this.getRng(), startElement, parentElement, checkRng, node;
- if (r.duplicate || r.item) {
- if (r.item)
- return r.item(0);
+ if (rng.duplicate || rng.item) {
+ // Control selection, return first item
+ if (rng.item)
+ return rng.item(0);
+
+ // Get start element
+ checkRng = rng.duplicate();
+ checkRng.collapse(1);
+ startElement = checkRng.parentElement();
+
+ // Check if range parent is inside the start element, then return the inner parent element
+ // This will fix issues when a single element is selected, IE would otherwise return the wrong start element
+ parentElement = node = rng.parentElement();
+ while (node = node.parentNode) {
+ if (node == startElement) {
+ startElement = parentElement;
+ break;
+ }
+ }
- r = r.duplicate();
- r.collapse(1);
- e = r.parentElement();
+ // If start element is body element try to move to the first child if it exists
+ if (startElement && startElement.nodeName == 'BODY')
+ return startElement.firstChild || startElement;
- if (e && e.nodeName == 'BODY')
- return e.firstChild || e;
-
- return e;
+ return startElement;
} else {
- e = r.startContainer;
+ startElement = rng.startContainer;
- if (e.nodeType == 1 && e.hasChildNodes())
- e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)];
+ if (startElement.nodeType == 1 && startElement.hasChildNodes())
+ startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
- if (e && e.nodeType == 3)
- return e.parentNode;
+ if (startElement && startElement.nodeType == 3)
+ return startElement.parentNode;
- return e;
+ return startElement;
}
},
@@ -5187,10 +5313,10 @@ window.tinymce.dom.Sizzle = Sizzle;
point.push(offset);
} else {
childNodes = container.childNodes;
-
- if (offset >= childNodes.length) {
+
+ if (offset >= childNodes.length && childNodes.length) {
after = 1;
- offset = childNodes.length - 1;
+ offset = Math.max(0, childNodes.length - 1);
}
point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
@@ -5269,7 +5395,7 @@ window.tinymce.dom.Sizzle = Sizzle;
},
moveToBookmark : function(bookmark) {
- var t = this, dom = t.dom, marker1, marker2, rng, root;
+ var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;
// Clear selection cache
if (t.tridentSel)
@@ -5281,12 +5407,16 @@ window.tinymce.dom.Sizzle = Sizzle;
root = dom.getRoot();
function setEndPoint(start) {
- var point = bookmark[start ? 'start' : 'end'], i, node, offset;
+ var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
if (point) {
// Find container node
- for (node = root, i = point.length - 1; i >= 1; i--)
- node = node.childNodes[point[i]];
+ for (node = root, i = point.length - 1; i >= 1; i--) {
+ children = node.childNodes;
+
+ if (children.length)
+ node = children[point[i]];
+ }
// Set offset within container node
if (start)
@@ -5301,8 +5431,6 @@ window.tinymce.dom.Sizzle = Sizzle;
t.setRng(rng);
} else if (bookmark.id) {
- rng = dom.createRng();
-
function restoreEndPoint(suffix) {
var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
@@ -5313,21 +5441,22 @@ window.tinymce.dom.Sizzle = Sizzle;
if (!keep) {
idx = dom.nodeIndex(marker);
} else {
- node = marker;
+ node = marker.firstChild;
idx = 1;
}
- rng.setStart(node, idx);
- rng.setEnd(node, idx);
+ startContainer = endContainer = node;
+ startOffset = endOffset = idx;
} else {
if (!keep) {
idx = dom.nodeIndex(marker);
} else {
- node = marker;
+ node = marker.firstChild;
idx = 1;
}
- rng.setEnd(node, idx);
+ endContainer = node;
+ endOffset = idx;
}
if (!keep) {
@@ -5352,19 +5481,33 @@ window.tinymce.dom.Sizzle = Sizzle;
dom.remove(next);
if (suffix == 'start') {
- rng.setStart(prev, idx);
- rng.setEnd(prev, idx);
- } else
- rng.setEnd(prev, idx);
+ startContainer = endContainer = prev;
+ startOffset = endOffset = idx;
+ } else {
+ endContainer = prev;
+ endOffset = idx;
+ }
}
}
}
};
+ function addBogus(node) {
+ // Adds a bogus BR element for empty block elements
+ // on non IE browsers just to have a place to put the caret
+ if (!isIE && dom.isBlock(node) && !node.innerHTML)
+ node.innerHTML = '<br _mce_bogus="1" />';
+
+ return node;
+ };
+
// Restore start/end points
restoreEndPoint('start');
restoreEndPoint('end');
+ rng = dom.createRng();
+ rng.setStart(addBogus(startContainer), startOffset);
+ rng.setEnd(addBogus(endContainer), endOffset);
t.setRng(rng);
} else if (bookmark.name) {
t.select(dom.select(bookmark.name)[bookmark.index]);
@@ -5469,18 +5612,30 @@ window.tinymce.dom.Sizzle = Sizzle;
if (!r)
r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();
+ if (t.selectedRange && t.explicitRange) {
+ if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {
+ // Safari, Opera and Chrome only ever select text which causes the range to change.
+ // This lets us use the originally set range if the selection hasn't been changed by the user.
+ r = t.explicitRange;
+ } else {
+ t.selectedRange = null;
+ t.explicitRange = null;
+ }
+ }
return r;
},
setRng : function(r) {
var s, t = this;
-
+
if (!t.tridentSel) {
s = t.getSel();
if (s) {
+ t.explicitRange = r;
s.removeAllRanges();
s.addRange(r);
+ t.selectedRange = s.getRangeAt(0);
}
} else {
// Is W3C Range
@@ -7210,6 +7365,26 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
};
*/
};
+
+ tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
+ if (rng1 && rng2) {
+ // Compare native IE ranges
+ if (rng1.item || rng1.duplicate) {
+ // Both are control ranges and the selected element matches
+ if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
+ return true;
+
+ // Both are text ranges and the range matches
+ if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
+ return true;
+ } else {
+ // Compare w3c ranges
+ return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
+ }
+ }
+
+ return false;
+ };
})(tinymce);
(function(tinymce) {
@@ -8047,7 +8222,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
var t = this, cp = t.classPrefix;
Event.add(t.id, 'click', t.showMenu, t);
- Event.add(t.id + '_text', 'focus', function(e) {
+ Event.add(t.id + '_text', 'focus', function() {
if (!t._focused) {
t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
var idx = -1, v, kc = e.keyCode;
@@ -9157,6 +9332,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!t.getElement())
return;
+ // Is a iPad/iPhone, then skip initialization. We need to sniff here since the
+ // browser says it has contentEditable support but there is no visible caret
+ // We will remove this check ones Apple implements full contentEditable support
+ if (tinymce.isIDevice)
+ return;
+
// Add hidden input for non input elements inside form elements
if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
@@ -9522,6 +9703,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},
fontname : {inline : 'span', styles : {fontFamily : '%value'}},
fontsize : {inline : 'span', styles : {fontSize : '%value'}},
+ fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
removeformat : [
@@ -9800,13 +9982,28 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
focus : function(sf) {
- var oed, t = this, ce = t.settings.content_editable;
+ var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
if (!sf) {
+ // Get selected control element
+ ieRng = t.selection.getRng();
+ if (ieRng.item) {
+ controlElm = ieRng.item(0);
+ }
+
// Is not content editable
if (!ce)
t.getWin().focus();
+ // Restore selected control element
+ // This is needed when for example an image is selected within a
+ // layer a call to focus will then remove the control selection
+ if (controlElm && controlElm.ownerDocument == doc) {
+ ieRng = doc.body.createControlRange();
+ ieRng.addElement(controlElm);
+ ieRng.select();
+ }
+
}
if (tinymce.activeEditor != t) {
@@ -10572,7 +10769,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Add node change handlers
t.onMouseUp.add(t.nodeChanged);
- t.onClick.add(t.nodeChanged);
+ //t.onClick.add(t.nodeChanged);
t.onKeyUp.add(function(ed, e) {
var c = e.keyCode;
@@ -10593,11 +10790,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// Add default shortcuts for gecko
- if (isGecko) {
- t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
- t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
- t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
- }
+ t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
+ t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
+ t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
// BlockFormat shortcuts keys
for (i=1; i<=6; i++)
@@ -10748,6 +10943,55 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
});
t.onKeyDown.add(function(ed, e) {
+ var rng, tmpRng, parent, offset;
+
+ // IE has a really odd bug where the DOM might include an node that doesn't have
+ // a proper structure. If you try to access nodeValue it would throw an illegal value exception.
+ // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element
+ // after you delete contents from it. See: #3008923
+ if (isIE && e.keyCode == 46) {
+ rng = t.selection.getRng();
+
+ if (rng.parentElement) {
+ parent = rng.parentElement();
+
+ // Get the current caret position within the element
+ tmpRng = rng.duplicate();
+ tmpRng.moveToElementText(parent);
+ tmpRng.setEndPoint('EndToEnd', rng);
+ offset = tmpRng.text.length;
+
+ // Select next word when ctrl key is used in combo with delete
+ if (e.ctrlKey) {
+ rng.moveEnd('word', 1);
+ rng.select();
+ }
+
+ // Delete contents
+ t.selection.getSel().clear();
+
+ // Check if we are within the same parent
+ if (rng.parentElement() == parent) {
+ try {
+ // Update the HTML and hopefully it will remove the artifacts
+ parent.innerHTML = parent.innerHTML;
+ } catch (ex) {
+ // And since it's IE it can sometimes produce an unknown runtime error
+ }
+
+ // Restore the caret position
+ tmpRng.moveToElementText(parent);
+ tmpRng.collapse();
+ tmpRng.move('character', offset);
+ tmpRng.select();
+ }
+
+ // Block the default delete behavior since it might be broken
+ e.preventDefault();
+ return;
+ }
+ }
+
// Is caracter positon keys
if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
if (t.undoManager.typing)
@@ -10944,7 +11188,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// Present alert message about clipboard access not being available
- if (failed || !doc.queryCommandEnabled(command)) {
+ if (failed || !doc.queryCommandSupported(command)) {
if (tinymce.isGecko) {
editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {
if (state)
@@ -11039,16 +11283,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
mceCleanup : function() {
- storeSelection();
+ var bookmark = selection.getBookmark();
+
editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});
- restoreSelection();
+
+ selection.moveToBookmark(bookmark);
},
mceRemoveNode : function(command, ui, value) {
var node = value || selection.getNode();
// Make sure that the body node isn't removed
- if (node != ed.getBody()) {
+ if (node != editor.getBody()) {
storeSelection();
editor.dom.remove(node, TRUE);
restoreSelection();
@@ -11153,8 +11399,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (value.href)
dom.setAttribs(link, value);
else
- ed.dom.remove(link, TRUE);
+ editor.dom.remove(link, TRUE);
}
+ },
+
+ selectAll : function() {
+ var root = dom.getRoot();
+ var rng = dom.createRng();
+ rng.setStart(root, 0);
+ rng.setEnd(root, root.childNodes.length);
+ editor.selection.setRng(rng);
}
});
@@ -11346,6 +11600,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
TRUE = true,
FALSE = false;
+ function cloneFormats(node) {
+ var clone, temp, inner;
+
+ do {
+ if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
+ if (clone) {
+ temp = node.cloneNode(false);
+ temp.appendChild(clone);
+ clone = temp;
+ } else {
+ clone = inner = node.cloneNode(false);
+ }
+
+ clone.removeAttribute('id');
+ }
+ } while (node = node.parentNode);
+
+ if (clone)
+ return {wrapper : clone, inner : inner};
+ };
+
// Checks if the selection/caret is at the end of the specified block element
function isAtEnd(rng, par) {
var rng2 = par.ownerDocument.createRange();
@@ -11454,11 +11729,54 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
}
- if (!isIE && s.force_p_newlines) {
- ed.onKeyPress.add(function(ed, e) {
- if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
- Event.cancel(e);
- });
+ if (s.force_p_newlines) {
+ if (!isIE) {
+ ed.onKeyPress.add(function(ed, e) {
+ if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
+ Event.cancel(e);
+ });
+ } else {
+ // Ungly hack to for IE to preserve the formatting when you press
+ // enter at the end of a block element with formatted contents
+ // This logic overrides the browsers default logic with
+ // custom logic that enables us to control the output
+ tinymce.addUnload(function() {
+ t._previousFormats = 0; // Fix IE leak
+ });
+
+ ed.onKeyPress.add(function(ed, e) {
+ t._previousFormats = 0;
+
+ // Clone the current formats, this will later be applied to the new block contents
+ if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)
+ t._previousFormats = cloneFormats(ed.selection.getStart());
+ });
+
+ ed.onKeyUp.add(function(ed, e) {
+ // Let IE break the element and the wrap the new caret location in the previous formats
+ if (e.keyCode == 13 && !e.shiftKey) {
+ var parent = ed.selection.getStart(), fmt = t._previousFormats;
+
+ // Parent is an empty block
+ if (!parent.hasChildNodes()) {
+ parent = dom.getParent(parent, dom.isBlock);
+
+ if (parent) {
+ parent.innerHTML = '';
+
+ if (t._previousFormats) {
+ parent.appendChild(fmt.wrapper);
+ fmt.inner.innerHTML = '\uFEFF';
+ } else
+ parent.innerHTML = '\uFEFF';
+
+ selection.select(parent, 1);
+ ed.getDoc().execCommand('Delete', false, null);
+ }
+ }
+ }
+ });
+ }
if (isGecko) {
ed.onKeyDown.add(function(ed, e) {
@@ -11499,7 +11817,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
ed.onKeyPress.add(function(ed, e) {
- if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) {
+ if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {
insertBr(ed);
Event.cancel(e);
}
@@ -11609,6 +11927,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
}
} else {
+ // Force control range into text range
+ if (r.item) {
+ tr = d.body.createTextRange();
+ tr.moveToElementText(r.item(0));
+ r = tr;
+ }
+
tr = d.body.createTextRange();
tr.moveToElementText(b);
tr.collapse(1);
@@ -11953,7 +12278,22 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
backspaceDelete : function(e, bs) {
- var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;
+ var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;
+
+ // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651
+ if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {
+ walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);
+
+ // Walk the dom backwards until we find a text node
+ for (n = sc.lastChild; n; n = walker.prev()) {
+ if (n.nodeType == 3) {
+ r.setStart(n, n.nodeValue.length);
+ r.collapse(true);
+ se.setRng(r);
+ return;
+ }
+ }
+ }
// The caret sometimes gets stuck in Gecko if you delete empty paragraphs
// This workaround removes the element by hand and moves the caret to the previous element
@@ -11984,37 +12324,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
}
}
-
- // Gecko generates BR elements here and there, we don't like those so lets remove them
- function handler(e) {
- var pr;
-
- e = e.target;
-
- // A new BR was created in a block element, remove it
- if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
- pr = e.previousSibling;
-
- Event.remove(b, 'DOMNodeInserted', handler);
-
- // Is there whitespace at the end of the node before then we might need the pesky BR
- // to place the caret at a correct location see bug: #2013943
- if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
- return;
-
- // Only remove BR elements that got inserted in the middle of the text
- if (e.previousSibling || e.nextSibling)
- ed.dom.remove(e);
- }
- };
-
- // Listen for new nodes
- Event._add(b, 'DOMNodeInserted', handler);
-
- // Remove listener
- window.setTimeout(function() {
- Event._remove(b, 'DOMNodeInserted', handler);
- }, 1);
}
});
})(tinymce);
@@ -12630,11 +12939,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Move startContainer/startOffset in to a suitable node
if (container.nodeType == 1 || container.nodeValue === "") {
- walker = new TreeWalker(container.childNodes[offset]);
- for (node = walker.current(); node; node = walker.next()) {
- if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) {
- rng.setStart(node, 0);
- break;
+ container = container.nodeType == 1 ? container.childNodes[offset] : container;
+
+ // Might fail if the offset is behind the last element in it's container
+ if (container) {
+ walker = new TreeWalker(container, container.parentNode);
+ for (node = walker.current(); node; node = walker.next()) {
+ if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
+ rng.setStart(node, 0);
+ break;
+ }
}
}
}
@@ -12713,7 +13027,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
- // Contine processing if a selector match wasn't found and a inline element is defined
+ // Continue processing if a selector match wasn't found and a inline element is defined
if (!format.inline || found) {
currentWrapElm = 0;
return;
@@ -12806,14 +13120,23 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
});
});
+ // Remove child if direct parent is of same type
+ if (matchNode(node.parentNode, name, vars)) {
+ dom.remove(node, 1);
+ node = 0;
+ return TRUE;
+ }
+
// Look for parent with similar style format
- dom.getParent(node.parentNode, function(parent) {
- if (matchNode(parent, name, vars)) {
- dom.remove(node, 1);
- node = 0;
- return TRUE;
- }
- });
+ if (format.merge_with_parents) {
+ dom.getParent(node.parentNode, function(parent) {
+ if (matchNode(parent, name, vars)) {
+ dom.remove(node, 1);
+ node = 0;
+ return TRUE;
+ }
+ });
+ }
// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
if (node) {
@@ -12831,7 +13154,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
rng.setStartBefore(node);
rng.setEndAfter(node);
- applyRngStyle(rng);
+ applyRngStyle(expandRng(rng, formatList));
} else {
if (!selection.isCollapsed() || !format.inline) {
// Apply formatting to selection
@@ -12940,7 +13263,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var node = dom.get(start ? '_start' : '_end'),
out = node[start ? 'firstChild' : 'lastChild'];
- dom.remove(node, 1);
+ // If the end is placed within the start the result will be removed
+ // So this checks if the out node is a bookmark node if it is it
+ // checks for another more suitable node
+ if (isBookmarkNode(out))
+ out = out[start ? 'firstChild' : 'lastChild'];
+
+ dom.remove(node, true);
return out;
};
@@ -13009,7 +13338,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
apply(name, vars, node);
};
- function matchNode(node, name, vars) {
+ function matchNode(node, name, vars, similar) {
var formatList = get(name), format, i, classes;
function matchItems(node, format, item_name) {
@@ -13026,7 +13355,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
else
value = getStyle(node, key);
- if (!isEq(value, replaceVars(items[key], vars)))
+ if (similar && !value && !format.exact)
+ return;
+
+ if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))
return;
}
}
@@ -13069,7 +13401,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
function matchParents(node) {
// Find first node with similar format settings
node = dom.getParent(node, function(node) {
- return !!matchNode(node, name, vars);
+ return !!matchNode(node, name, vars, true);
});
// Do an exact check on the similar format element
@@ -13248,7 +13580,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function isWhiteSpaceNode(node) {
- return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue);
+ return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);
};
function wrap(node, name, attrs) {
@@ -13691,7 +14023,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function isTextBlock(name) {
- return /^(h[1-6]|p|div|pre|address)$/.test(name);
+ return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);
};
function getContainer(rng, start) {
@@ -13757,6 +14089,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Pending apply or remove formats
if (hasPending()) {
ed.getDoc().execCommand('FontName', false, 'mceinline');
+ pendingFormats.lastRng = selection.getRng();
// IE will convert the current word
each(dom.select('font,span'), function(node) {
@@ -13776,21 +14109,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {
ed[event].addToTop(function(ed, e) {
- if (hasPending()) {
+ // Do we have pending formats and is the selection moved has moved
+ if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {
each(dom.select('font,span'), function(node) {
- var bookmark, textNode, rng;
+ var textNode, rng;
// Look for marker
if (isCaretNode(node)) {
textNode = node.firstChild;
- perform(node);
+ if (textNode) {
+ perform(node);
- rng = dom.createRng();
- rng.setStart(textNode, textNode.nodeValue.length);
- rng.setEnd(textNode, textNode.nodeValue.length);
- selection.setRng(rng);
- ed.nodeChanged();
+ rng = dom.createRng();
+ rng.setStart(textNode, textNode.nodeValue.length);
+ rng.setEnd(textNode, textNode.nodeValue.length);
+ selection.setRng(rng);
+ ed.nodeChanged();
+ } else
+ dom.remove(node);
}
});