1
0
Fork 0

update tinymce to 3.5.0.1

This commit is contained in:
friendica 2012-05-15 17:18:46 -07:00
commit e39014a3a7
26 changed files with 5794 additions and 4903 deletions

View file

@ -1,3 +1,46 @@
Version 3.5.0.1 (2012-05-10)
Fixed bug where selection normalization logic would break the selections of parent elements using the element path.
Fixed bug where the autolink plugin would include trailing dots in domain names in the link creation.
Fixed bug where the autolink plugin would produce an error on older IE versions when pressing enter.
Fixed bug where old IE versions would throw an error during initialization when the editor was placed in an size restricted div.
Version 3.5 (2012-05-03)
Fixed menu rendering issue if the document was in rtl mode.
Fixed bug where the hide function would throw an error about a missing variable.
Fixed bug where autolink wouldn't convert URLs when hitting enter on IE due to the new enter key logic.
Fixed bug where formatting using shortcuts like ctrl+b wouldn't work properly the first time.
Fixed bug where selection.setContent after a formatter call wouldn't generate formatted contents.
Fixed bug where whitespace would be removed before/after invalid_elements when they where removed.
Fixed bug where updating styles using the theme image dialog in non inline mode on IE9 would produce errors.
Fixed bug where IE 8 would produce an error when using the contextmenu plugin.
Fixed bug where delete/backspace could remove contents of noneditable elements.
Fixed so background color in style preview gets computed from body element if the current style element is transparent.
Version 3.5b3 (2012-03-29)
Added cancel button to colour picker dialog.
Added figure and figcaption to the html5 visualblocks plugin.
Added default alignment options for the figure element.
Fixed bug where empty inline elements within block elements would sometimes produce a br child element.
Fixed bug where urls pointing to the same domain as the current one would cause undefined errors. Patch contributed by Paul Giberson.
Fixed bug where enter inside an editable element inside an non editable element would split the element.
Fixed bug where cut/copy/paste of noneditable elements didn't work.
Fixed bug where backspace would sometimes produce font elements on WebKit.
Fixed bug where WebKit would produce spans out of various inline elements when using backspace.
Fixed bug where IE9 wouldn't properly update image styles when images where resized.
Fixed bug where drag/drop of noneditable elements didn't work correctly.
Fixed bug where applying formatting to all contents wouldn't work correctly when an end point was inside an empty bock. Patch contributed by Jose Luiz.
Fixed bug where IE10 removed the scopeName from the DOM element interface and there for it produced an undefined string in element path.
Fixed bug where the caret would be placed at an incorrect location if you applied block formatting while having the caret at the end of the block.
Fixed bug where applying column changes using the cell dialog would only update the first column. Patch contributed by krzyko.
Fixed bug where the visualblocks plugin would force editor focus if it was turned on by default.
Fixed bug where the tabfocus plugin would tab to iframes these are now ignored.
Fixed bug where format drop down list wouldn't show the currently active format for a parent element.
Fixed bug where paste of plain text in IE 9 would remove the new line characters from text.
Fixed bug where the menu buttons/split button menus wouldn't be opened at the right location on older IE versions.
Fixed bug where Gecko browsers wouldn't properly display the right format when having the selection as specific places.
Fixed bug where shift+enter inside the body when having forced_root_blocks set to false would throw an error.
Fixed bug where the jQuery plugin would break the attr method of jQuery 1.7.2. Patch contributed by Markus Kemmerling.
Fixed so options like content_css accepts and array as well as a comma separated string as input.
Restructured the internal logic to make it more separate from Editor.js.
Updated the Sizzle engine to the latest version.
Version 3.5b2 (2012-03-15)
Rewrote the enter key logic to normalize browser behavior.
Fixed so enter within PRE elements produces a BR and shift+enter breaks/end the PRE. Can be disabled using the br_in_pre option.
@ -19,15 +62,6 @@ Version 3.5b1 (2012-03-08)
Fixed bug where the advlink dialog would produce an error about the addSelectAccessibility function not being defined.
Fixed bug where the caret would be placed at an incorrect position if span was removed by the invalid_elements setting.
Fixed bug where elements inside a white space preserve element like pre didn't inherit the behavior while parsing.
Version 3.4.9.x (2012-02-xx)
Improved behaviour of backspacing into a table to be consistant across browsers and disable backspace when cursor immediately follows a table.
Improved edit CSS style plugin for single and multiple block selection and provide option to apply style to only selected text.
Fixed bug in Chrome where moving caret down in table and pasting throws errors.
Corrected reference to TinyMCE trim function.
Fixed bug where Ignore All in IE did not remove the underline from the selected word.
Fixed bug in html source editor word wrap option not wrapping text in Webkit browsers.
Fixed bug where it was possible to insert an invalid colour in the color pop-up dialog.
Fixed bug in Webkit where if anchor is on last line by itself caret can not be placed after it.
Version 3.4.9 (2012-02-23)
Added settings to wordcount plugin to configure update rate and checking wordcount on backspace and delete using wordcount_update_rate and wordcount_update_on_delete.
Fixed bug in Webkit and IE where deleting empty paragraphs would remove entire editor contents.

View file

@ -1 +1 @@
(function(){tinymce.create("tinymce.plugins.AutolinkPlugin",{init:function(a,b){var c=this;if(tinyMCE.isIE){return}a.onKeyDown.add(function(d,f){if(f.keyCode==13){return c.handleEnter(d)}});a.onKeyPress.add(function(d,f){if(f.which==41){return c.handleEclipse(d)}});a.onKeyUp.add(function(d,f){if(f.keyCode==32){return c.handleSpacebar(d)}})},handleEclipse:function(a){this.parseCurrentLine(a,-1,"(",true)},handleSpacebar:function(a){this.parseCurrentLine(a,0,"",true)},handleEnter:function(a){this.parseCurrentLine(a,-1,"",false)},parseCurrentLine:function(i,d,b,g){var a,f,c,n,k,m,h,e,j;a=i.selection.getRng().cloneRange();if(a.startOffset<5){e=a.endContainer.previousSibling;if(e==null){if(a.endContainer.firstChild==null||a.endContainer.firstChild.nextSibling==null){return}e=a.endContainer.firstChild.nextSibling}j=e.length;a.setStart(e,j);a.setEnd(e,j);if(a.endOffset<5){return}f=a.endOffset;n=e}else{n=a.endContainer;if(n.nodeType!=3&&n.firstChild){while(n.nodeType!=3&&n.firstChild){n=n.firstChild}a.setStart(n,0);a.setEnd(n,n.nodeValue.length)}if(a.endOffset==1){f=2}else{f=a.endOffset-1-d}}c=f;do{a.setStart(n,f-2);a.setEnd(n,f-1);f-=1}while(a.toString()!=" "&&a.toString()!=""&&a.toString().charCodeAt(0)!=160&&(f-2)>=0&&a.toString()!=b);if(a.toString()==b||a.toString().charCodeAt(0)==160){a.setStart(n,f);a.setEnd(n,c);f+=1}else{if(a.startOffset==0){a.setStart(n,0);a.setEnd(n,c)}else{a.setStart(n,f);a.setEnd(n,c)}}m=a.toString();h=m.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|[A-Z0-9._%+-]+@)(.+)$/i);if(h){if(h[1]=="www."){h[1]="http://www."}else{if(/@$/.test(h[1])){h[1]="mailto:"+h[1]}}k=i.selection.getBookmark();i.selection.setRng(a);tinyMCE.execCommand("createlink",false,h[1]+h[2]);i.selection.moveToBookmark(k);if(tinyMCE.isWebKit){i.selection.collapse(false);var l=Math.min(n.length,c+1);a.setStart(n,l);a.setEnd(n,l);i.selection.setRng(a)}}},getInfo:function(){return{longname:"Autolink",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autolink",tinymce.plugins.AutolinkPlugin)})();
(function(){tinymce.create("tinymce.plugins.AutolinkPlugin",{init:function(a,b){var c=this;a.onKeyDown.addToTop(function(d,f){if(f.keyCode==13){return c.handleEnter(d)}});if(tinyMCE.isIE){return}a.onKeyPress.add(function(d,f){if(f.which==41){return c.handleEclipse(d)}});a.onKeyUp.add(function(d,f){if(f.keyCode==32){return c.handleSpacebar(d)}})},handleEclipse:function(a){this.parseCurrentLine(a,-1,"(",true)},handleSpacebar:function(a){this.parseCurrentLine(a,0,"",true)},handleEnter:function(a){this.parseCurrentLine(a,-1,"",false)},parseCurrentLine:function(i,d,b,g){var a,f,c,n,k,m,h,e,j;a=i.selection.getRng(true).cloneRange();if(a.startOffset<5){e=a.endContainer.previousSibling;if(e==null){if(a.endContainer.firstChild==null||a.endContainer.firstChild.nextSibling==null){return}e=a.endContainer.firstChild.nextSibling}j=e.length;a.setStart(e,j);a.setEnd(e,j);if(a.endOffset<5){return}f=a.endOffset;n=e}else{n=a.endContainer;if(n.nodeType!=3&&n.firstChild){while(n.nodeType!=3&&n.firstChild){n=n.firstChild}a.setStart(n,0);a.setEnd(n,n.nodeValue.length)}if(a.endOffset==1){f=2}else{f=a.endOffset-1-d}}c=f;do{a.setStart(n,f-2);a.setEnd(n,f-1);f-=1}while(a.toString()!=" "&&a.toString()!=""&&a.toString().charCodeAt(0)!=160&&(f-2)>=0&&a.toString()!=b);if(a.toString()==b||a.toString().charCodeAt(0)==160){a.setStart(n,f);a.setEnd(n,c);f+=1}else{if(a.startOffset==0){a.setStart(n,0);a.setEnd(n,c)}else{a.setStart(n,f);a.setEnd(n,c)}}var m=a.toString();if(m.charAt(m.length-1)=="."){a.setEnd(n,c-1)}m=a.toString();h=m.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|[A-Z0-9._%+-]+@)(.+)$/i);if(h){if(h[1]=="www."){h[1]="http://www."}else{if(/@$/.test(h[1])){h[1]="mailto:"+h[1]}}k=i.selection.getBookmark();i.selection.setRng(a);tinyMCE.execCommand("createlink",false,h[1]+h[2]);i.selection.moveToBookmark(k);if(tinyMCE.isWebKit){i.selection.collapse(false);var l=Math.min(n.length,c+1);a.setStart(n,l);a.setEnd(n,l);i.selection.setRng(a)}}},getInfo:function(){return{longname:"Autolink",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autolink",tinymce.plugins.AutolinkPlugin)})();

View file

@ -22,16 +22,16 @@
init : function(ed, url) {
var t = this;
// Internet Explorer has built-in automatic linking
if (tinyMCE.isIE)
return;
// Add a key down handler
ed.onKeyDown.add(function(ed, e) {
ed.onKeyDown.addToTop(function(ed, e) {
if (e.keyCode == 13)
return t.handleEnter(ed);
});
// Internet Explorer has built-in automatic linking for most cases
if (tinyMCE.isIE)
return;
ed.onKeyPress.add(function(ed, e) {
if (e.which == 41)
return t.handleEclipse(ed);
@ -61,7 +61,7 @@
// We need at least five characters to form a URL,
// hence, at minimum, five characters from the beginning of the line.
r = ed.selection.getRng().cloneRange();
r = ed.selection.getRng(true).cloneRange();
if (r.startOffset < 5) {
// During testing, the caret is placed inbetween two text nodes.
// The previous text node contains the URL.
@ -124,6 +124,12 @@
r.setEnd(endContainer, start);
}
// Exclude last . from word like "www.site.com."
var text = r.toString();
if (text.charAt(text.length - 1) == '.') {
r.setEnd(endContainer, start - 1);
}
text = r.toString();
matches = text.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|[A-Z0-9._%+-]+@)(.+)$/i);

View file

@ -1 +1 @@
(function(){var a=tinymce.DOM;tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(d,e){var f=this,g={},c,b;f.editor=d;d.addCommand("mceFullScreen",function(){var i,j=a.doc.documentElement;if(d.getParam("fullscreen_is_enabled")){if(d.getParam("fullscreen_new_window")){closeFullscreen()}else{a.win.setTimeout(function(){tinymce.dom.Event.remove(a.win,"resize",f.resizeFunc);tinyMCE.get(d.getParam("fullscreen_editor_id")).setContent(d.getContent());tinyMCE.remove(d);a.remove("mce_fullscreen_container");j.style.overflow=d.getParam("fullscreen_html_overflow");a.setStyle(a.doc.body,"overflow",d.getParam("fullscreen_overflow"));a.win.scrollTo(d.getParam("fullscreen_scrollx"),d.getParam("fullscreen_scrolly"));tinyMCE.settings=tinyMCE.oldSettings},10)}return}if(d.getParam("fullscreen_new_window")){i=a.win.open(e+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{i.resizeTo(screen.availWidth,screen.availHeight)}catch(h){}}else{tinyMCE.oldSettings=tinyMCE.settings;g.fullscreen_overflow=a.getStyle(a.doc.body,"overflow",1)||"auto";g.fullscreen_html_overflow=a.getStyle(j,"overflow",1);c=a.getViewPort();g.fullscreen_scrollx=c.x;g.fullscreen_scrolly=c.y;if(tinymce.isOpera&&g.fullscreen_overflow=="visible"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&g.fullscreen_overflow=="scroll"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&(g.fullscreen_html_overflow=="visible"||g.fullscreen_html_overflow=="scroll")){g.fullscreen_html_overflow="auto"}if(g.fullscreen_overflow=="0px"){g.fullscreen_overflow=""}a.setStyle(a.doc.body,"overflow","hidden");j.style.overflow="hidden";c=a.getViewPort();a.win.scrollTo(0,0);if(tinymce.isIE){c.h-=1}if(tinymce.isIE6){b="absolute;top:"+c.y}else{b="fixed;top:0"}n=a.add(a.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+b+";left:0;width:"+c.w+"px;height:"+c.h+"px;z-index:200000;"});a.add(n,"div",{id:"mce_fullscreen"});tinymce.each(d.settings,function(k,l){g[l]=k});g.id="mce_fullscreen";g.width=n.clientWidth;g.height=n.clientHeight-15;g.fullscreen_is_enabled=true;g.fullscreen_editor_id=d.id;g.theme_advanced_resizing=false;g.save_onsavecallback=function(){d.setContent(tinyMCE.get(g.id).getContent());d.execCommand("mceSave")};tinymce.each(d.getParam("fullscreen_settings"),function(m,l){g[l]=m});if(g.theme_advanced_toolbar_location==="external"){g.theme_advanced_toolbar_location="top"}f.fullscreenEditor=new tinymce.Editor("mce_fullscreen",g);f.fullscreenEditor.onInit.add(function(){f.fullscreenEditor.setContent(d.getContent());f.fullscreenEditor.focus()});f.fullscreenEditor.render();f.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");f.fullscreenElement.update();f.resizeFunc=tinymce.dom.Event.add(a.win,"resize",function(){var o=tinymce.DOM.getViewPort(),l=f.fullscreenEditor,k,m;k=l.dom.getSize(l.getContainer().firstChild);m=l.dom.getSize(l.getContainer().getElementsByTagName("iframe")[0]);l.theme.resizeTo(o.w-k.w+m.w,o.h-k.h+m.h)})}});d.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});d.onNodeChange.add(function(i,h){h.setActive("fullscreen",i.getParam("fullscreen_is_enabled"))})},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})();
(function(){var a=tinymce.DOM;tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(d,e){var f=this,g={},c,b;f.editor=d;d.addCommand("mceFullScreen",function(){var i,j=a.doc.documentElement;if(d.getParam("fullscreen_is_enabled")){if(d.getParam("fullscreen_new_window")){closeFullscreen()}else{a.win.setTimeout(function(){tinymce.dom.Event.remove(a.win,"resize",f.resizeFunc);tinyMCE.get(d.getParam("fullscreen_editor_id")).setContent(d.getContent());tinyMCE.remove(d);a.remove("mce_fullscreen_container");j.style.overflow=d.getParam("fullscreen_html_overflow");a.setStyle(a.doc.body,"overflow",d.getParam("fullscreen_overflow"));a.win.scrollTo(d.getParam("fullscreen_scrollx"),d.getParam("fullscreen_scrolly"));tinyMCE.settings=tinyMCE.oldSettings},10)}return}if(d.getParam("fullscreen_new_window")){i=a.win.open(e+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{i.resizeTo(screen.availWidth,screen.availHeight)}catch(h){}}else{tinyMCE.oldSettings=tinyMCE.settings;g.fullscreen_overflow=a.getStyle(a.doc.body,"overflow",1)||"auto";g.fullscreen_html_overflow=a.getStyle(j,"overflow",1);c=a.getViewPort();g.fullscreen_scrollx=c.x;g.fullscreen_scrolly=c.y;if(tinymce.isOpera&&g.fullscreen_overflow=="visible"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&g.fullscreen_overflow=="scroll"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&(g.fullscreen_html_overflow=="visible"||g.fullscreen_html_overflow=="scroll")){g.fullscreen_html_overflow="auto"}if(g.fullscreen_overflow=="0px"){g.fullscreen_overflow=""}a.setStyle(a.doc.body,"overflow","hidden");j.style.overflow="hidden";c=a.getViewPort();a.win.scrollTo(0,0);if(tinymce.isIE){c.h-=1}if(tinymce.isIE6||document.compatMode=="BackCompat"){b="absolute;top:"+c.y}else{b="fixed;top:0"}n=a.add(a.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+b+";left:0;width:"+c.w+"px;height:"+c.h+"px;z-index:200000;"});a.add(n,"div",{id:"mce_fullscreen"});tinymce.each(d.settings,function(k,l){g[l]=k});g.id="mce_fullscreen";g.width=n.clientWidth;g.height=n.clientHeight-15;g.fullscreen_is_enabled=true;g.fullscreen_editor_id=d.id;g.theme_advanced_resizing=false;g.save_onsavecallback=function(){d.setContent(tinyMCE.get(g.id).getContent());d.execCommand("mceSave")};tinymce.each(d.getParam("fullscreen_settings"),function(m,l){g[l]=m});if(g.theme_advanced_toolbar_location==="external"){g.theme_advanced_toolbar_location="top"}f.fullscreenEditor=new tinymce.Editor("mce_fullscreen",g);f.fullscreenEditor.onInit.add(function(){f.fullscreenEditor.setContent(d.getContent());f.fullscreenEditor.focus()});f.fullscreenEditor.render();f.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");f.fullscreenElement.update();f.resizeFunc=tinymce.dom.Event.add(a.win,"resize",function(){var o=tinymce.DOM.getViewPort(),l=f.fullscreenEditor,k,m;k=l.dom.getSize(l.getContainer().firstChild);m=l.dom.getSize(l.getContainer().getElementsByTagName("iframe")[0]);l.theme.resizeTo(o.w-k.w+m.w,o.h-k.h+m.h)})}});d.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});d.onNodeChange.add(function(i,h){h.setActive("fullscreen",i.getParam("fullscreen_is_enabled"))})},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})();

View file

@ -79,7 +79,7 @@
vp.h -= 1;
// Use fixed position if it exists
if (tinymce.isIE6)
if (tinymce.isIE6 || document.compatMode == 'BackCompat')
posCss = 'absolute;top:' + vp.y;
else
posCss = 'fixed;top:0';

File diff suppressed because one or more lines are too long

View file

@ -261,12 +261,96 @@
selection.collapse(start);
}
function canDelete(backspace) {
var rng, container, offset, nonEditableParent;
function removeNodeIfNotParent(node) {
var parent = container;
while (parent) {
if (parent === node) {
return;
}
parent = parent.parentNode;
}
dom.remove(node);
moveSelection();
}
function isNextPrevTreeNodeNonEditable() {
var node, walker, nonEmptyElements = ed.schema.getNonEmptyElements();
walker = new tinymce.dom.TreeWalker(container, ed.getBody());
while (node = (backspace ? walker.prev() : walker.next())) {
// Found IMG/INPUT etc
if (nonEmptyElements[node.nodeName.toLowerCase()]) {
break;
}
// Found text node with contents
if (node.nodeType === 3 && tinymce.trim(node.nodeValue).length > 0) {
break;
}
// Found non editable node
if (getContentEditable(node) === "false") {
removeNodeIfNotParent(node);
return true;
}
}
// Check if the content node is within a non editable parent
if (getNonEditableParent(node)) {
return true;
}
return false;
}
if (selection.isCollapsed()) {
rng = selection.getRng(true);
container = rng.startContainer;
offset = rng.startOffset;
container = getParentCaretContainer(container) || container;
// Is in noneditable parent
if (nonEditableParent = getNonEditableParent(container)) {
removeNodeIfNotParent(nonEditableParent);
return false;
}
// Check if the caret is in the middle of a text node
if (container.nodeType == 3 && (backspace ? offset > 0 : offset < container.nodeValue.length)) {
return true;
}
// Resolve container index
if (container.nodeType == 1) {
container = container.childNodes[offset] || container;
}
// Check if previous or next tree node is non editable then block the event
if (isNextPrevTreeNodeNonEditable()) {
return false;
}
}
return true;
}
startElement = selection.getStart()
endElement = selection.getEnd();
// Disable all key presses in contentEditable=false except delete or backspace
nonEditableParent = getNonEditableParent(startElement) || getNonEditableParent(endElement);
if (nonEditableParent && (keyCode < 112 || keyCode > 124) && keyCode != VK.DELETE && keyCode != VK.BACKSPACE) {
// Is Ctrl+c, Ctrl+v or Ctrl+x then use default browser behavior
if ((tinymce.isMac ? e.metaKey : e.ctrlKey) && (keyCode == 67 || keyCode == 88 || keyCode == 86)) {
return;
}
e.preventDefault();
// Arrow left/right select the element and collapse left/right
@ -298,6 +382,7 @@
positionCaretOnElement(nonEditableParent, true);
} else {
dom.remove(nonEditableParent);
return;
}
} else {
removeCaretContainer(caretContainer);
@ -315,23 +400,31 @@
positionCaretOnElement(nonEditableParent, false);
} else {
dom.remove(nonEditableParent);
return;
}
} else {
removeCaretContainer(caretContainer);
}
}
}
if ((keyCode == VK.BACKSPACE || keyCode == VK.DELETE) && !canDelete(keyCode == VK.BACKSPACE)) {
e.preventDefault();
return false;
}
}
}
};
ed.onMouseDown.addToTop(function(ed, e) {
// prevent collapsing selection to caret when clicking in a non-editable section
var node = ed.selection.getNode();
if (getContentEditable(node) === "false" && node == e.target) {
e.preventDefault();
// Expand selection on mouse down we can't block the default event since it's used for drag/drop
moveSelection();
}
});
ed.onMouseUp.addToTop(moveSelection);
ed.onKeyDown.addToTop(handleKey);
ed.onKeyUp.addToTop(moveSelection);
@ -341,6 +434,31 @@
init : function(ed, url) {
var editClass, nonEditClass, nonEditableRegExps;
// Converts configured regexps to noneditable span items
function convertRegExpsToNonEditable(ed, args) {
var i = nonEditableRegExps.length, content = args.content, cls = tinymce.trim(nonEditClass);
// Don't replace the variables when raw is used for example on undo/redo
if (args.format == "raw") {
return;
}
while (i--) {
content = content.replace(nonEditableRegExps[i], function(match) {
var args = arguments, index = args[args.length - 2];
// Is value inside an attribute then don't replace
if (index > 0 && content.charAt(index - 1) == '"') {
return match;
}
return '<span class="' + cls + '" data-mce-content="' + ed.dom.encode(args[0]) + '">' + ed.dom.encode(typeof(args[1]) === "string" ? args[1] : args[0]) + '</span>';
});
}
args.content = content;
};
editClass = " " + tinymce.trim(ed.getParam("noneditable_editable_class", "mceEditable")) + " ";
nonEditClass = " " + tinymce.trim(ed.getParam("noneditable_noneditable_class", "mceNonEditable")) + " ";
@ -354,24 +472,8 @@
handleContentEditableSelection(ed);
if (nonEditableRegExps) {
ed.onBeforeSetContent.add(function(ed, args) {
var i = nonEditableRegExps.length, content = args.content, cls = tinymce.trim(nonEditClass);
// Don't replace the variables when raw is used for example on undo/redo
if (args.format == "raw") {
return;
}
while (i--) {
content = content.replace(nonEditableRegExps[i], function() {
var args = arguments;
return '<span class="' + cls + '" data-mce-content="' + ed.dom.encode(args[0]) + '">' + ed.dom.encode(typeof(args[1]) === "string" ? args[1] : args[0]) + '</span>';
});
}
args.content = content;
});
ed.selection.onBeforeSetContent.add(convertRegExpsToNonEditable);
ed.onBeforeSetContent.add(convertRegExpsToNonEditable);
}
// Apply contentEditable true/false on elements with the noneditable/editable classes

File diff suppressed because one or more lines are too long

View file

@ -359,7 +359,7 @@
}
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
if (tinymce.isIE && document.documentMode >= 9) {
if (tinymce.isIE && document.documentMode >= 9 && /<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(o.content)) {
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
process([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g, '$1']]);

View file

@ -1 +1 @@
(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})();
(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]:not(iframe)");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})();

View file

@ -22,7 +22,7 @@
var x, i, f, el, v;
function find(d) {
el = DOM.select(':input:enabled,*[tabindex]');
el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
function canSelectRecursive(e) {
return e.nodeName==="BODY" || (e.type != 'hidden' &&

View file

@ -137,7 +137,7 @@ function updateAction() {
do {
if (cell == tdElm)
break;
col += cell.getAttribute("colspan");
col += cell.getAttribute("colspan")?cell.getAttribute("colspan"):1;
} while ((cell = nextCell(cell)) != null);
for (var i=0; i<rows.length; i++) {
@ -152,7 +152,7 @@ function updateAction() {
cell = updateCell(cell, true);
break;
}
curr += cell.getAttribute("colspan");
curr += cell.getAttribute("colspan")?cell.getAttribute("colspan"):1;
} while ((cell = nextCell(cell)) != null);
}

View file

@ -1,6 +1,6 @@
p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, blockquote, address, pre {display: block; padding-top: 10px; border: 1px dashed #BBB; background: transparent no-repeat}
p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, address, pre {margin-left: 3px}
section, article, address, hgroup, aside {margin: 1em 0 0 3px}
p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, blockquote, address, pre, figure {display: block; padding-top: 10px; border: 1px dashed #BBB; background: transparent no-repeat}
p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, address, pre, figure {margin-left: 3px}
section, article, address, hgroup, aside, figure {margin: 0 0 1em 3px}
p {background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}
h1 {background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}
@ -17,3 +17,5 @@ address {background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH
pre {background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}
hgroup {background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}
aside {background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}
figure {background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}
figcaption {border: 1px dashed #BBB}

View file

@ -1 +1 @@
(function(){tinymce.create("tinymce.plugins.VisualBlocks",{init:function(a,b){var c;if(!window.NodeList){return}a.addCommand("mceVisualBlocks",function(){var e=a.dom,d;if(!c){c=e.uniqueId();d=e.create("link",{id:c,rel:"stylesheet",href:b+"/css/visualblocks.css"});a.getDoc().getElementsByTagName("head")[0].appendChild(d)}else{d=e.get(c);d.disabled=!d.disabled}a.controlManager.setActive("visualblocks",!d.disabled)});a.addButton("visualblocks",{title:"visualblocks.desc",cmd:"mceVisualBlocks"});a.onInit.add(function(){if(a.settings.visualblocks_default_state){a.execCommand("mceVisualBlocks")}})},getInfo:function(){return{longname:"Visual blocks",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualblocks",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("visualblocks",tinymce.plugins.VisualBlocks)})();
(function(){tinymce.create("tinymce.plugins.VisualBlocks",{init:function(a,b){var c;if(!window.NodeList){return}a.addCommand("mceVisualBlocks",function(){var e=a.dom,d;if(!c){c=e.uniqueId();d=e.create("link",{id:c,rel:"stylesheet",href:b+"/css/visualblocks.css"});a.getDoc().getElementsByTagName("head")[0].appendChild(d)}else{d=e.get(c);d.disabled=!d.disabled}a.controlManager.setActive("visualblocks",!d.disabled)});a.addButton("visualblocks",{title:"visualblocks.desc",cmd:"mceVisualBlocks"});a.onInit.add(function(){if(a.settings.visualblocks_default_state){a.execCommand("mceVisualBlocks",false,null,{skip_focus:true})}})},getInfo:function(){return{longname:"Visual blocks",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualblocks",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("visualblocks",tinymce.plugins.VisualBlocks)})();

View file

@ -42,7 +42,7 @@
ed.onInit.add(function() {
if (ed.settings.visualblocks_default_state) {
ed.execCommand('mceVisualBlocks');
ed.execCommand('mceVisualBlocks', false, null, {skip_focus : true});
}
});
},

View file

@ -62,12 +62,8 @@
<div class="mceActionPanel">
<input type="submit" id="insert" name="insert" value="{#apply}" />
<div id="preview"></div>
<div id="previewblock">
<label for="color">{#advanced_dlg.colorpicker_color}</label> <input id="color" type="text" size="8" class="text mceFocus" aria-required="true" />
</div>
<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();"/>
<div id="preview_wrapper"><div id="previewblock"><label for="color">{#advanced_dlg.colorpicker_color}</label> <input id="color" type="text" size="8" class="text mceFocus" aria-required="true" /></div><span id="preview"></span></div>
</div>
</form>
</body>

File diff suppressed because one or more lines are too long

View file

@ -69,6 +69,16 @@
each(previewStyles.split(' '), function(name) {
var value = dom.getStyle(previewElm, name, true);
// If background is transparent then check if the body has a background color we can use
if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
value = dom.getStyle(ed.getBody(), name, true);
// Ignore white since it's the default color, not the nicest fix
if (dom.toHex(value).toLowerCase() == '#ffffff') {
return;
}
}
// Old IE won't calculate the font size so we need to do that manually
if (name == 'font-size') {
if (/em|%$/.test(value)) {
@ -608,7 +618,7 @@
// TODO: ACC Should have an aria-describedby attribute which is user-configurable to describe what this field is actually for.
// Maybe actually inherit it from the original textara?
n = p = DOM.create('span', {role : 'application', 'aria-labelledby' : ed.id + '_voice', id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '')});
n = p = DOM.create('span', {role : 'application', 'aria-labelledby' : ed.id + '_voice', id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '') + (ed.settings.directionality == "rtl" ? ' mceRtl' : '')});
DOM.add(n, 'span', {'class': 'mceVoiceLabel', 'style': 'display:none;', id: ed.id + '_voice'}, s.aria_label);
if (!DOM.boxModel)
@ -925,7 +935,7 @@
},
_addToolbars : function(c, o) {
var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a, toolbarGroup;
var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a, toolbarGroup, toolbarsExist = false;
toolbarGroup = cf.createToolbarGroup('toolbargroup', {
'name': ed.getLang('advanced.toolbar'),
@ -941,6 +951,7 @@
// Create toolbar and add the controls
for (i=1; (v = s['theme_advanced_buttons' + i]); i++) {
toolbarsExist = true;
tb = cf.createToolbar("toolbar" + i, {'class' : 'mceToolbarRow' + i});
if (s['theme_advanced_buttons' + i + '_add'])
@ -954,6 +965,9 @@
o.deltaHeight -= s.theme_advanced_row_height;
}
// Handle case when there are no toolbar buttons and ensure editor height is adjusted accordingly
if (!toolbarsExist)
o.deltaHeight -= s.theme_advanced_row_height;
h.push(toolbarGroup.renderHTML());
h.push(DOM.createHTML('a', {href : '#', accesskey : 'z', title : ed.getLang("advanced.toolbar_focus"), onfocus : 'tinyMCE.getInstanceById(\'' + ed.id + '\').focus();'}, '<!-- IE -->'));
DOM.setHTML(n, h.join(''));
@ -1112,7 +1126,7 @@
}
if (c = cm.get('formatselect')) {
p = getParent(DOM.isBlock);
p = getParent(ed.dom.isBlock);
if (p)
c.select(p.nodeName.toLowerCase());
@ -1210,7 +1224,7 @@
return;
// Handle prefix
if (tinymce.isIE && n.scopeName !== 'HTML')
if (tinymce.isIE && n.scopeName !== 'HTML' && n.scopeName)
na = n.scopeName + ':' + na;
// Remove internal prefix
@ -1271,7 +1285,7 @@
if (v) {
ti += 'class: ' + v + ' ';
if (DOM.isBlock(n) || na == 'img' || na == 'span')
if (ed.dom.isBlock(n) || na == 'img' || na == 'span')
na += '.' + v;
}
}

View file

@ -104,10 +104,12 @@ var ImageDialog = {
},
updateStyle : function() {
var dom = tinyMCEPopup.dom, st, v, f = document.forms[0];
var dom = tinyMCEPopup.dom, st = {}, v, f = document.forms[0];
if (tinyMCEPopup.editor.settings.inline_styles) {
st = tinyMCEPopup.dom.parseStyle(this.styleVal);
tinymce.each(tinyMCEPopup.dom.parseStyle(this.styleVal), function(value, key) {
st[key] = value;
});
// Handle align
v = getSelectValue(f, 'align');

View file

@ -94,11 +94,12 @@ h3 {font-size:14px;}
#plugintable, #about #plugintable td {border:1px solid #919B9C;}
#plugintable {width:96%; margin-top:10px;}
#pluginscontainer {height:290px; overflow:auto;}
#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px}
#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline}
#colorpicker #preview_wrapper { text-align:center; padding-top:4px; white-space: nowrap}
#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
#colorpicker #light div {overflow:hidden;}
#colorpicker #previewblock {float:right; padding-left:10px; height:20px;}
#colorpicker .panel_wrapper div.current {height:175px;}
#colorpicker #namedcolors {width:150px;}
#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}

View file

@ -58,7 +58,7 @@
/* Menu */
.highcontrastSkin .mceNoIcons span.mceIcon {width:0;}
.highcontrastSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid; }
.highcontrastSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid; direction:ltr}
.highcontrastSkin .mceMenu table {background:white; color: black}
.highcontrastSkin .mceNoIcons a .mceText {padding-left:10px}
.highcontrastSkin .mceMenu a, .highcontrastSkin .mceMenu span, .highcontrastSkin .mceMenu {display:block;background:white; color: black}
@ -90,6 +90,10 @@
.highcontrastSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=50); background:#FFF}
.highcontrastSkin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(../default/img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
/* Rtl */
.mceRtl .mceListBox .mceText {text-align: right; padding: 0 4px 0 0}
.mceRtl .mceMenuItem .mceText {text-align: right}
/* Formats */
.highcontrastSkin .mce_p span.mceText {}
.highcontrastSkin .mce_address span.mceText {font-style:italic}

View file

@ -105,11 +105,12 @@ h3 {font-size:14px;}
#plugintable, #about #plugintable td {border:1px solid #919B9C;}
#plugintable {width:96%; margin-top:10px;}
#pluginscontainer {height:290px; overflow:auto;}
#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px}
#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline}
#colorpicker #preview_wrapper { text-align:center; padding-top:4px; white-space: nowrap}
#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
#colorpicker #light div {overflow:hidden;}
#colorpicker #previewblock {float:right; padding-left:10px; height:20px;}
#colorpicker .panel_wrapper div.current {height:175px;}
#colorpicker #namedcolors {width:150px;}
#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}

View file

@ -86,7 +86,7 @@
.o2k7Skin .mce_forecolor span.mceAction, .o2k7Skin .mce_backcolor span.mceAction {height:15px;overflow:hidden}
/* Menu */
.o2k7Skin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #ABC6DD}
.o2k7Skin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #ABC6DD; direction:ltr}
.o2k7Skin .mceNoIcons span.mceIcon {width:0;}
.o2k7Skin .mceNoIcons a .mceText {padding-left:10px}
.o2k7Skin .mceMenu table {background:#FFF}
@ -112,6 +112,10 @@
.o2k7Skin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=50); background:#FFF}
.o2k7Skin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(../default/img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
/* Rtl */
.mceRtl .mceListBox .mceText {text-align: right; padding: 0 4px 0 0}
.mceRtl .mceMenuItem .mceText {text-align: right}
/* Formats */
.o2k7Skin .mce_formatPreview a {font-size:10px}
.o2k7Skin .mce_p span.mceText {}

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,14 @@
// FILE IS GENERATED BY COMBINING THE SOURCES IN THE "classes" DIRECTORY SO DON'T MODIFY THIS FILE DIRECTLY
(function(win) {
var whiteSpaceRe = /^\s*|\s*$/g,
undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
undef, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
var tinymce = {
majorVersion : '3',
minorVersion : '5b2',
minorVersion : '5.0.1',
releaseDate : '2012-03-15',
releaseDate : '2012-05-10',
_init : function() {
var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@ -50,7 +51,8 @@
// If base element found, add that infront of baseURL
nl = d.getElementsByTagName('base');
for (i=0; i<nl.length; i++) {
if (v = nl[i].href) {
v = nl[i].href;
if (v) {
// Host only value like http://site.com or http://site.com:8008
if (/^https?:\/\/[^\/]+$/.test(v))
v += '/';
@ -103,7 +105,7 @@
is : function(o, t) {
if (!t)
return o !== undefined;
return o !== undef;
if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
return true;
@ -137,7 +139,7 @@
s = s || o;
if (o.length !== undefined) {
if (o.length !== undef) {
// Indexed arrays, needed for Safari
for (n=0, l = o.length; n < l; n++) {
if (cb.call(s, o[n], n, o) === false)
@ -191,19 +193,23 @@
return -1;
},
extend : function(o, e) {
var i, l, a = arguments;
extend : function(obj, ext) {
var i, l, name, args = arguments, value;
for (i = 1, l = a.length; i < l; i++) {
e = a[i];
for (i = 1, l = args.length; i < l; i++) {
ext = args[i];
for (name in ext) {
if (ext.hasOwnProperty(name)) {
value = ext[name];
tinymce.each(e, function(v, n) {
if (v !== undefined)
o[n] = v;
});
if (value !== undef) {
obj[name] = value;
}
}
}
}
return o;
return obj;
},
@ -346,12 +352,9 @@
},
addUnload : function(f, s) {
var t = this;
var t = this, unload;
f = {func : f, scope : s || this};
if (!t.unloads) {
function unload() {
unload = function() {
var li = t.unloads, o, n;
if (li) {
@ -382,8 +385,6 @@
function fakeUnload() {
var d = document;
// Is there things still loading, then do some magic
if (d.readyState == 'interactive') {
function stop() {
// Prevent memory leak
d.detachEvent('onstop', stop);
@ -395,6 +396,8 @@
d = 0;
};
// Is there things still loading, then do some magic
if (d.readyState == 'interactive') {
// Fire unload when the currently loading page is stopped
if (d)
d.attachEvent('onstop', stop);
@ -409,6 +412,9 @@
}
};
f = {func : f, scope : s || this};
if (!t.unloads) {
// Attach unload handler
if (win.attachEvent) {
win.attachEvent('onunload', unload);
@ -439,7 +445,11 @@
},
explode : function(s, d) {
return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
if (!s || tinymce.is(s, 'array')) {
return s;
}
return tinymce.map(s.split(d || ','), tinymce.trim);
},
_addVer : function(u) {
@ -465,7 +475,7 @@
var val = replace, args = arguments, i;
for (i = 0; i < args.length - 2; i++) {
if (args[i] === undefined) {
if (args[i] === undef) {
val = val.replace(new RegExp('\\$' + i, 'g'), '');
} else {
val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
@ -496,52 +506,64 @@
tinymce.create('tinymce.util.Dispatcher', {
scope : null,
listeners : null,
inDispatch: false,
Dispatcher : function(s) {
this.scope = s || this;
Dispatcher : function(scope) {
this.scope = scope || this;
this.listeners = [];
},
add : function(cb, s) {
this.listeners.push({cb : cb, scope : s || this.scope});
add : function(callback, scope) {
this.listeners.push({cb : callback, scope : scope || this.scope});
return cb;
return callback;
},
addToTop : function(cb, s) {
this.listeners.unshift({cb : cb, scope : s || this.scope});
addToTop : function(callback, scope) {
var self = this, listener = {cb : callback, scope : scope || self.scope};
return cb;
// Create new listeners if addToTop is executed in a dispatch loop
if (self.inDispatch) {
self.listeners = [listener].concat(self.listeners);
} else {
self.listeners.unshift(listener);
}
return callback;
},
remove : function(cb) {
var l = this.listeners, o = null;
remove : function(callback) {
var listeners = this.listeners, output = null;
tinymce.each(l, function(c, i) {
if (cb == c.cb) {
o = cb;
l.splice(i, 1);
tinymce.each(listeners, function(listener, i) {
if (callback == listener.cb) {
output = listener;
listeners.splice(i, 1);
return false;
}
});
return o;
return output;
},
dispatch : function() {
var s, a = arguments, i, li = this.listeners, c;
var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener;
self.inDispatch = true;
// Needs to be a real loop since the listener count might change while looping
// And this is also more efficient
for (i = 0; i<li.length; i++) {
c = li[i];
s = c.cb.apply(c.scope, a.length > 0 ? a : [c.scope]);
for (i = 0; i < listeners.length; i++) {
listener = listeners[i];
returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]);
if (s === false)
if (returnValue === false)
break;
}
return s;
self.inDispatch = false;
return returnValue;
}
});
@ -571,7 +593,7 @@ tinymce.create('tinymce.util.Dispatcher', {
u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
// Relative path http:// or protocol relative //path
if (!/^[\w-]*:?\/\//.test(u)) {
if (!/^[\w\-]*:?\/\//.test(u)) {
base_url = s.base_uri ? s.base_uri.path : new tinymce.util.URI(location.href).directory;
u = ((s.base_uri && s.base_uri.protocol) || 'http') + '://mce_host' + t.toAbsPath(base_url, u);
}
@ -589,17 +611,18 @@ tinymce.create('tinymce.util.Dispatcher', {
t[v] = s;
});
if (b = s.base_uri) {
b = s.base_uri;
if (b) {
if (!t.protocol)
t.protocol = b.protocol;
if (!t.userInfo)
t.userInfo = b.userInfo;
if (!t.port && t.host == 'mce_host')
if (!t.port && t.host === 'mce_host')
t.port = b.port;
if (!t.host || t.host == 'mce_host')
if (!t.host || t.host === 'mce_host')
t.host = b.host;
t.source = '';
@ -635,6 +658,12 @@ tinymce.create('tinymce.util.Dispatcher', {
if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
return u.getURI();
var tu = t.getURI(), uu = u.getURI();
// Allow usage of the base_uri when relative_urls = true
if(tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu))
return tu;
o = t.toRelPath(t.path, u.path);
// Add query
@ -649,7 +678,7 @@ tinymce.create('tinymce.util.Dispatcher', {
},
toAbsolute : function(u, nh) {
var u = new tinymce.util.URI(u, {base_uri : this});
u = new tinymce.util.URI(u, {base_uri : this});
return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
},
@ -680,7 +709,7 @@ tinymce.create('tinymce.util.Dispatcher', {
}
}
if (bp == 1)
if (bp === 1)
return path;
for (i = 0, l = base.length - (bp - 1); i < l; i++)
@ -715,11 +744,11 @@ tinymce.create('tinymce.util.Dispatcher', {
// Merge relURLParts chunks
for (i = path.length - 1, o = []; i >= 0; i--) {
// Ignore empty or .
if (path[i].length == 0 || path[i] == ".")
if (path[i].length === 0 || path[i] === ".")
continue;
// Is parent
if (path[i] == '..') {
if (path[i] === '..') {
nb++;
continue;
}
@ -830,7 +859,7 @@ tinymce.create('tinymce.util.Dispatcher', {
if (b == -1) {
b = c.indexOf(p);
if (b != 0)
if (b !== 0)
return null;
} else
b += 2;
@ -863,7 +892,7 @@ tinymce.create('tinymce.util.Dispatcher', {
(function() {
function serialize(o, quote) {
var i, v, t;
var i, v, t, name;
quote = quote || '"';
@ -901,9 +930,9 @@ tinymce.create('tinymce.util.Dispatcher', {
v = '{';
for (i in o) {
if (o.hasOwnProperty(i)) {
v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
for (name in o) {
if (o.hasOwnProperty(name)) {
v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + quote +':' + serialize(o[name], quote) : '';
}
}
@ -931,6 +960,18 @@ tinymce.create('static tinymce.util.XHR', {
send : function(o) {
var x, t, w = window, c = 0;
function ready() {
if (!o.async || x.readyState == 4 || c++ > 10000) {
if (o.success && c < 10000 && x.status == 200)
o.success.call(o.success_scope, '' + x.responseText, x, o);
else if (o.error)
o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
x = null;
} else
w.setTimeout(ready, 10);
};
// Default settings
o.scope = o.scope || this;
o.success_scope = o.success_scope || o.scope;
@ -964,18 +1005,6 @@ tinymce.create('static tinymce.util.XHR', {
x.send(o.data);
function ready() {
if (!o.async || x.readyState == 4 || c++ > 10000) {
if (o.success && c < 10000 && x.status == 200)
o.success.call(o.success_scope, '' + x.responseText, x, o);
else if (o.error)
o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
x = null;
} else
w.setTimeout(ready, 10);
};
// Syncronous request
if (!o.async)
return ready();
@ -1055,13 +1084,13 @@ tinymce.create('static tinymce.util.XHR', {
modifierPressed: function (e) {
return e.shiftKey || e.ctrlKey || e.altKey;
}
}
};
})(tinymce);
(function(tinymce) {
var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;
tinymce.util.Quirks = function(editor) {
var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection, settings = editor.settings;
function setEditorCommandState(editor, cmd, state) {
function setEditorCommandState(cmd, state) {
try {
editor.getDoc().execCommand(cmd, false, state);
} catch (ex) {
@ -1069,19 +1098,10 @@ tinymce.create('static tinymce.util.XHR', {
}
}
function cleanupStylesWhenDeleting(ed) {
var dom = ed.dom, selection = ed.selection;
function cleanupStylesWhenDeleting() {
function removeMergedFormatSpans(isDelete) {
var rng, blockElm, node, clonedSpan;
ed.onKeyDown.add(function(ed, e) {
var rng, blockElm, node, clonedSpan, isDelete;
if (e.isDefaultPrevented()) {
return;
}
isDelete = e.keyCode == DELETE;
if ((isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
e.preventDefault();
rng = selection.getRng();
// Find root block
@ -1096,7 +1116,7 @@ tinymce.create('static tinymce.util.XHR', {
node = blockElm.firstChild;
// Ignore empty text nodes
while (node && node.nodeType == 3 && node.nodeValue.length == 0)
while (node && node.nodeType == 3 && node.nodeValue.length === 0)
node = node.nextSibling;
if (node && node.nodeName === 'SPAN') {
@ -1105,7 +1125,7 @@ tinymce.create('static tinymce.util.XHR', {
}
// Do the backspace/delete action
ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
// Find all odd apple-style-spans
blockElm = dom.getParent(rng.startContainer, dom.isBlock);
@ -1121,55 +1141,120 @@ tinymce.create('static tinymce.util.XHR', {
// Restore the selection
selection.moveToBookmark(bm);
});
}
});
};
function emptyEditorWhenDeleting(ed) {
function serializeRng(rng) {
var body = ed.dom.create("body");
var contents = rng.cloneContents();
body.appendChild(contents);
return ed.selection.serializer.serialize(body, {format: 'html'});
editor.onKeyDown.add(function(editor, e) {
var isDelete;
isDelete = e.keyCode == DELETE;
if (!e.isDefaultPrevented() && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
e.preventDefault();
removeMergedFormatSpans(isDelete);
}
});
editor.addCommand('Delete', function() {removeMergedFormatSpans();});
};
function emptyEditorWhenDeleting() {
function getEndPointNode(rng, start) {
var container, offset, prefix = start ? 'start' : 'end';
container = rng[prefix + 'Container'];
offset = rng[prefix + 'Offset'];
// Resolve indexed container
if (container.nodeType == 1 && container.hasChildNodes()) {
container = container.childNodes[Math.min(start ? offset : (offset > 0 ? offset - 1 : 0), container.childNodes.length - 1)]
}
function allContentsSelected(rng) {
var selection = serializeRng(rng);
return container;
};
var allRng = ed.dom.createRng();
allRng.selectNode(ed.getBody());
function isAtStartEndOfBody(rng, start) {
var container, offset, root, childNode, prefix = start ? 'start' : 'end', isAfter;
var allSelection = serializeRng(allRng);
return selection === allSelection;
container = rng[prefix + 'Container'];
offset = rng[prefix + 'Offset'];
root = dom.getRoot();
// Resolve indexed container
if (container.nodeType == 1) {
isAfter = offset >= container.childNodes.length;
container = getEndPointNode(rng, start);
if (container.nodeType == 3) {
offset = start && !isAfter ? 0 : container.nodeValue.length;
}
}
ed.onKeyDown.addToTop(function(ed, e) {
var keyCode = e.keyCode;
if (keyCode == DELETE || keyCode == BACKSPACE) {
var rng = ed.selection.getRng(true);
if (!rng.collapsed && allContentsSelected(rng)) {
ed.setContent('', {format : 'raw'});
ed.nodeChanged();
// Check if start/end is in the middle of text
if (container.nodeType == 3 && ((start && offset > 0) || (!start && offset < container.nodeValue.length))) {
return false;
}
// Walk up the DOM tree to see if the endpoint is at the beginning/end of body
while (container !== root) {
childNode = container.parentNode[start ? 'firstChild' : 'lastChild'];
// If first/last element is a BR then jump to it's sibling in case: <p>x<br></p>
if (childNode.nodeName == "BR") {
childNode = childNode[start ? 'nextSibling' : 'previousSibling'] || childNode;
}
// If the childNode isn't the container node then break in case <p><span>A</span>[X]</p>
if (childNode !== container) {
return false;
}
container = container.parentNode;
}
return true;
};
editor.onKeyDown.addToTop(function(editor, e) {
var rng, keyCode = e.keyCode;
if (!e.isDefaultPrevented() && (keyCode == DELETE || keyCode == BACKSPACE)) {
rng = selection.getRng(true);
if (isAtStartEndOfBody(rng, true) && isAtStartEndOfBody(rng, false) &&
(rng.collapsed || dom.findCommonAncestor(getEndPointNode(rng, true), getEndPointNode(rng)) === dom.getRoot())) {
editor.setContent('');
editor.nodeChanged();
e.preventDefault();
}
}
});
};
function inputMethodFocus(ed) {
ed.dom.bind(ed.getDoc(), 'focusin', function() {
ed.selection.setRng(ed.selection.getRng());
function inputMethodFocus() {
if (!editor.settings.content_editable) {
// Case 1 IME doesn't initialize if you focus the document
dom.bind(editor.getDoc(), 'focusin', function(e) {
selection.setRng(selection.getRng());
});
// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
dom.bind(editor.getDoc(), 'mousedown', function(e) {
if (e.target == editor.getDoc().documentElement) {
editor.getWin().focus();
selection.setRng(selection.getRng());
}
});
}
};
function removeHrOnBackspace(ed) {
ed.onKeyDown.add(function(ed, e) {
if (e.keyCode === BACKSPACE) {
if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
var node = ed.selection.getNode();
function removeHrOnBackspace() {
editor.onKeyDown.add(function(editor, e) {
if (!e.isDefaultPrevented() && e.keyCode === BACKSPACE) {
if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
var node = selection.getNode();
var previousSibling = node.previousSibling;
if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
ed.dom.remove(previousSibling);
dom.remove(previousSibling);
tinymce.dom.Event.cancel(e);
}
}
@ -1177,13 +1262,13 @@ tinymce.create('static tinymce.util.XHR', {
})
}
function focusBody(ed) {
function focusBody() {
// Fix for a focus bug in FF 3.x where the body element
// wouldn't get proper focus if the user clicked on the HTML element
if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
ed.onMouseDown.add(function(ed, e) {
editor.onMouseDown.add(function(editor, e) {
if (e.target.nodeName === "HTML") {
var body = ed.getBody();
var body = editor.getBody();
// Blur the body it's focused but not correctly focused
body.blur();
@ -1197,33 +1282,33 @@ tinymce.create('static tinymce.util.XHR', {
}
};
function selectControlElements(ed) {
ed.onClick.add(function(ed, e) {
function selectControlElements() {
editor.onClick.add(function(editor, e) {
e = e.target;
// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
// WebKit can't even do simple things like selecting an image
// Needs tobe the setBaseAndExtend or it will fail to select floated images
if (/^(IMG|HR)$/.test(e.nodeName))
ed.selection.getSel().setBaseAndExtent(e, 0, e, 1);
if (/^(IMG|HR)$/.test(e.nodeName)) {
selection.getSel().setBaseAndExtent(e, 0, e, 1);
}
if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor'))
ed.selection.select(e);
if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) {
selection.select(e);
}
ed.nodeChanged();
editor.nodeChanged();
});
};
function removeStylesWhenDeletingAccrossBlockElements(ed) {
var selection = ed.selection, dom = ed.dom;
function removeStylesWhenDeletingAccrossBlockElements() {
function getAttributeApplyFunction() {
var template = dom.getAttribs(selection.getStart().cloneNode(false));
return function() {
var target = selection.getStart();
if (target !== ed.getBody()) {
if (target !== editor.getBody()) {
dom.setAttrib(target, "style", null);
tinymce.each(template, function(attr) {
@ -1237,91 +1322,68 @@ tinymce.create('static tinymce.util.XHR', {
return !selection.isCollapsed() && selection.getStart() != selection.getEnd();
}
function blockEvent(ed, e) {
function blockEvent(editor, e) {
e.preventDefault();
return false;
}
ed.onKeyPress.add(function(ed, e) {
editor.onKeyPress.add(function(editor, e) {
var applyAttributes;
if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
applyAttributes = getAttributeApplyFunction();
ed.getDoc().execCommand('delete', false, null);
editor.getDoc().execCommand('delete', false, null);
applyAttributes();
e.preventDefault();
return false;
}
});
dom.bind(ed.getDoc(), 'cut', function(e) {
dom.bind(editor.getDoc(), 'cut', function(e) {
var applyAttributes;
if (isSelectionAcrossElements()) {
applyAttributes = getAttributeApplyFunction();
ed.onKeyUp.addToTop(blockEvent);
editor.onKeyUp.addToTop(blockEvent);
setTimeout(function() {
applyAttributes();
ed.onKeyUp.remove(blockEvent);
editor.onKeyUp.remove(blockEvent);
}, 0);
}
});
}
/*
function removeStylesOnPTagsInheritedFromHeadingTag(ed) {
ed.onKeyDown.add(function(ed, event) {
function checkInHeadingTag(ed) {
var currentNode = ed.selection.getNode();
var headingTags = 'h1,h2,h3,h4,h5,h6';
return ed.dom.is(currentNode, headingTags) || ed.dom.getParent(currentNode, headingTags) !== null;
}
if (event.keyCode === VK.ENTER && !VK.modifierPressed(event) && checkInHeadingTag(ed)) {
setTimeout(function() {
var currentNode = ed.selection.getNode();
if (ed.dom.is(currentNode, 'p')) {
ed.dom.setAttrib(currentNode, 'style', null);
// While tiny's content is correct after this method call, the content shown is not representative of it and needs to be 'repainted'
ed.execCommand('mceCleanup');
}
}, 0);
}
});
}
*/
function selectionChangeNodeChanged(ed) {
function selectionChangeNodeChanged() {
var lastRng, selectionTimer;
ed.dom.bind(ed.getDoc(), 'selectionchange', function() {
dom.bind(editor.getDoc(), 'selectionchange', function() {
if (selectionTimer) {
clearTimeout(selectionTimer);
selectionTimer = 0;
}
selectionTimer = window.setTimeout(function() {
var rng = ed.selection.getRng();
var rng = selection.getRng();
// Compare the ranges to see if it was a real change or not
if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
ed.nodeChanged();
editor.nodeChanged();
lastRng = rng;
}
}, 50);
});
}
function ensureBodyHasRoleApplication(ed) {
function ensureBodyHasRoleApplication() {
document.body.setAttribute("role", "application");
}
function disableBackspaceIntoATable(ed) {
ed.onKeyDown.add(function(ed, e) {
if (e.keyCode === BACKSPACE) {
if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
var previousSibling = ed.selection.getNode().previousSibling;
function disableBackspaceIntoATable() {
editor.onKeyDown.add(function(editor, e) {
if (!e.isDefaultPrevented() && e.keyCode === BACKSPACE) {
if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
var previousSibling = selection.getNode().previousSibling;
if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
return tinymce.dom.Event.cancel(e);
}
@ -1330,7 +1392,7 @@ tinymce.create('static tinymce.util.XHR', {
})
}
function addNewLinesBeforeBrInPre(editor) {
function addNewLinesBeforeBrInPre() {
var documentMode = editor.getDoc().documentMode;
// IE8+ rendering mode does the right thing with BR in PRE
@ -1340,8 +1402,8 @@ tinymce.create('static tinymce.util.XHR', {
// Enable display: none in area and add a specific class that hides all BR elements in PRE to
// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
setEditorCommandState(editor, 'RespectVisibilityInDesign', true);
editor.dom.addClass(editor.getBody(), 'mceHideBrInPre');
setEditorCommandState('RespectVisibilityInDesign', true);
dom.addClass(editor.getBody(), 'mceHideBrInPre');
// Adds a \n before all BR elements in PRE to get them visual
editor.parser.addNodeFilter('pre', function(nodes, name) {
@ -1382,43 +1444,210 @@ tinymce.create('static tinymce.util.XHR', {
});
}
tinymce.create('tinymce.util.Quirks', {
Quirks: function(ed) {
function removePreSerializedStylesWhenSelectingControls() {
dom.bind(editor.getBody(), 'mouseup', function(e) {
var value, node = selection.getNode();
// Moved styles to attributes on IMG eements
if (node.nodeName == 'IMG') {
// Convert style width to width attribute
if (value = dom.getStyle(node, 'width')) {
dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
dom.setStyle(node, 'width', '');
}
// Convert style height to height attribute
if (value = dom.getStyle(node, 'height')) {
dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
dom.setStyle(node, 'height', '');
}
}
});
}
function keepInlineElementOnDeleteBackspace() {
editor.onKeyDown.add(function(editor, e) {
var isDelete, rng, container, offset, brElm, sibling, collapsed;
isDelete = e.keyCode == DELETE;
if (!e.isDefaultPrevented() && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
rng = selection.getRng();
container = rng.startContainer;
offset = rng.startOffset;
collapsed = rng.collapsed;
// Override delete if the start container is a text node and is at the beginning of text or
// just before/after the last character to be deleted in collapsed mode
if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) || (collapsed && offset === (isDelete ? 0 : 1)))) {
nonEmptyElements = editor.schema.getNonEmptyElements();
// Prevent default logic since it's broken
e.preventDefault();
// Insert a BR before the text node this will prevent the containing element from being deleted/converted
brElm = dom.create('br', {id: '__tmp'});
container.parentNode.insertBefore(brElm, container);
// Do the browser delete
editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
// Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>
container = selection.getRng().startContainer;
sibling = container.previousSibling;
if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) && !nonEmptyElements[sibling.nodeName.toLowerCase()]) {
dom.remove(sibling);
}
// Remove the temp element we inserted
dom.remove('__tmp');
}
}
});
}
function removeBlockQuoteOnBackSpace() {
// Add block quote deletion handler
editor.onKeyDown.add(function(editor, e) {
var rng, container, offset, root, parent;
if (e.isDefaultPrevented() || e.keyCode != VK.BACKSPACE) {
return;
}
rng = selection.getRng();
container = rng.startContainer;
offset = rng.startOffset;
root = dom.getRoot();
parent = container;
if (!rng.collapsed || offset !== 0) {
return;
}
while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
parent = parent.parentNode;
}
// Is the cursor at the beginning of a blockquote?
if (parent.tagName === 'BLOCKQUOTE') {
// Remove the blockquote
editor.formatter.toggle('blockquote', null, parent);
// Move the caret to the beginning of container
rng.setStart(container, 0);
rng.setEnd(container, 0);
selection.setRng(rng);
selection.collapse(false);
}
});
};
function setGeckoEditingOptions() {
function setOpts() {
editor._refreshContentEditable();
setEditorCommandState("StyleWithCSS", false);
setEditorCommandState("enableInlineTableEditing", false);
if (!settings.object_resizing) {
setEditorCommandState("enableObjectResizing", false);
}
};
if (!settings.readonly) {
editor.onBeforeExecCommand.add(setOpts);
editor.onMouseDown.add(setOpts);
}
};
function addBrAfterLastLinks() {
function fixLinks(editor, o) {
tinymce.each(dom.select('a'), function(node) {
var parentNode = node.parentNode, root = dom.getRoot();
if (parentNode.lastChild === node) {
while (parentNode && !dom.isBlock(parentNode)) {
if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
return;
}
parentNode = parentNode.parentNode;
}
dom.add(parentNode, 'br', {'data-mce-bogus' : 1});
}
});
};
editor.onExecCommand.add(function(editor, cmd) {
if (cmd === 'CreateLink') {
fixLinks(editor);
}
});
editor.onSetContent.add(selection.onSetContent.add(fixLinks));
};
function removeGhostSelection() {
function repaint(sender, args) {
if (!sender || !args.initial) {
editor.execCommand('mceRepaint');
}
};
editor.onUndo.add(repaint);
editor.onRedo.add(repaint);
editor.onSetContent.add(repaint);
};
function deleteImageOnBackSpace() {
editor.onKeyDown.add(function(editor, e) {
if (!e.isDefaultPrevented() && e.keyCode == 8 && selection.getNode().nodeName == 'IMG') {
e.preventDefault();
editor.undoManager.beforeChange();
dom.remove(selection.getNode());
editor.undoManager.add();
}
});
};
// All browsers
disableBackspaceIntoATable(ed);
disableBackspaceIntoATable();
removeBlockQuoteOnBackSpace();
emptyEditorWhenDeleting();
// WebKit
if (tinymce.isWebKit) {
cleanupStylesWhenDeleting(ed);
emptyEditorWhenDeleting(ed);
inputMethodFocus(ed);
selectControlElements(ed);
keepInlineElementOnDeleteBackspace();
cleanupStylesWhenDeleting();
inputMethodFocus();
selectControlElements();
// iOS
if (tinymce.isIDevice) {
selectionChangeNodeChanged(ed);
selectionChangeNodeChanged();
}
}
// IE
if (tinymce.isIE) {
removeHrOnBackspace(ed);
emptyEditorWhenDeleting(ed);
ensureBodyHasRoleApplication(ed);
//removeStylesOnPTagsInheritedFromHeadingTag(ed)
addNewLinesBeforeBrInPre(ed);
removeHrOnBackspace();
ensureBodyHasRoleApplication();
addNewLinesBeforeBrInPre();
removePreSerializedStylesWhenSelectingControls();
deleteImageOnBackSpace();
}
// Gecko
if (tinymce.isGecko) {
removeHrOnBackspace(ed);
focusBody(ed);
removeStylesWhenDeletingAccrossBlockElements(ed);
removeHrOnBackspace();
focusBody();
removeStylesWhenDeletingAccrossBlockElements();
setGeckoEditingOptions();
addBrAfterLastLinks();
removeGhostSelection();
}
}
});
})(tinymce);
};
(function(tinymce) {
var namedEntities, baseEntities, reverseEntities,
attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
@ -1511,8 +1740,7 @@ tinymce.create('static tinymce.util.XHR', {
'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'
, 32);
'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
tinymce.html = tinymce.html || {};
@ -1723,8 +1951,28 @@ tinymce.html.Styles = function(settings, schema) {
str = str.replace(/\\([\'\";:])/g, "$1");
return str;
};
function processUrl(match, url, url2, url3, str, str2) {
str = str || str2;
if (str) {
str = decode(str);
// Force strings into single quote format
return "'" + str.replace(/\'/g, "\\'") + "'";
}
url = decode(url || url2 || url3);
// Convert the URL to relative/absolute depending on config
if (urlConverter)
url = urlConverter.call(urlConverterScope, url, 'style');
// Output new URL format
return "url('" + url.replace(/\'/g, "\\'") + "')";
};
if (css) {
// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
@ -1747,26 +1995,7 @@ tinymce.html.Styles = function(settings, schema) {
value = value.replace(rgbRegExp, toHex);
// Convert URLs and force them into url('value') format
value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {
str = str || str2;
if (str) {
str = decode(str);
// Force strings into single quote format
return "'" + str.replace(/\'/g, "\\'") + "'";
}
url = decode(url || url2 || url3);
// Convert the URL to relative/absolute depending on config
if (urlConverter)
url = urlConverter.call(urlConverterScope, url, 'style');
// Output new URL format
return "url('" + url.replace(/\'/g, "\\'") + "')";
});
value = value.replace(urlOrStrRegExp, processUrl);
styles[name] = isEncoded ? decode(value, true) : value;
}
@ -1934,7 +2163,8 @@ tinymce.html.Styles = function(settings, schema) {
'span[A][B]' +
'ins[A|cite|datetime][B]' +
'del[A|cite|datetime][B]' +
'figure[A][C|legend]' +
'figure[A][C|legend|figcaption]' +
'figcaption[A][C]' +
'img[A|alt|src|height|width|usemap|ismap][]' +
'iframe[A|name|src|height|width|sandbox|seamless][]' +
'embed[A|src|height|width|type][]' +
@ -2161,7 +2391,7 @@ tinymce.html.Styles = function(settings, schema) {
nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);
blockElementsMap = createLookupTable('block_elements', 'h1 h2 h3 h4 h5 h6 hr p div address pre form table tbody thead tfoot ' +
'th tr td li ol ul caption blockquote center dl dt dd dir fieldset ' +
'noscript menu isindex samp header footer article section hgroup aside nav');
'noscript menu isindex samp header footer article section hgroup aside nav figure');
// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
function patternToRegExp(str) {
@ -2173,7 +2403,7 @@ tinymce.html.Styles = function(settings, schema) {
function addValidElements(valid_elements) {
var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,
elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
elementRuleRegExp = /^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
hasPatternsRegExp = /[*?+]/;
@ -2546,6 +2776,47 @@ tinymce.html.Styles = function(settings, schema) {
}
};
function parseAttribute(match, name, value, val2, val3) {
var attrRule, i;
name = name.toLowerCase();
value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
// Validate name and value
if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
attrRule = validAttributesMap[name];
// Find rule by pattern matching
if (!attrRule && validAttributePatterns) {
i = validAttributePatterns.length;
while (i--) {
attrRule = validAttributePatterns[i];
if (attrRule.pattern.test(name))
break;
}
// No rule matched
if (i === -1)
attrRule = null;
}
// No attribute rule found
if (!attrRule)
return;
// Validate value
if (attrRule.validValues && !(value in attrRule.validValues))
return;
}
// Add attribute to list and map
attrList.map[name] = value;
attrList.push({
name: name,
value: value
});
};
// Precompile RegExps and map objects
tokenRegExp = new RegExp('<(?:' +
'(?:!--([\\w\\W]*?)-->)|' + // Comment
@ -2553,7 +2824,7 @@ tinymce.html.Styles = function(settings, schema) {
'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
'(?:\\/([^>]+)>)|' + // End element
'(?:([A-Za-z0-9\-\:]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
'(?:([A-Za-z0-9\\-\\:]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
')', 'g');
attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
@ -2620,46 +2891,7 @@ tinymce.html.Styles = function(settings, schema) {
attrList = [];
attrList.map = {};
attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {
var attrRule, i;
name = name.toLowerCase();
value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
// Validate name and value
if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
attrRule = validAttributesMap[name];
// Find rule by pattern matching
if (!attrRule && validAttributePatterns) {
i = validAttributePatterns.length;
while (i--) {
attrRule = validAttributePatterns[i];
if (attrRule.pattern.test(name))
break;
}
// No rule matched
if (i === -1)
attrRule = null;
}
// No attribute rule found
if (!attrRule)
return;
// Validate value
if (attrRule.validValues && !(value in attrRule.validValues))
return;
}
// Add attribute to list and map
attrList.map[name] = value;
attrList.push({
name: name,
value: value
});
});
attribsValue.replace(attrRegExp, parseAttribute);
} else {
attrList = [];
attrList.map = {};
@ -3281,7 +3513,7 @@ tinymce.html.Styles = function(settings, schema) {
self.parse = function(html, args) {
var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,
blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement,
endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;
endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;
args = args || {};
matchedNodes = {};
@ -3296,6 +3528,7 @@ tinymce.html.Styles = function(settings, schema) {
startWhiteSpaceRegExp = /^[ \t\r\n]+/;
endWhiteSpaceRegExp = /[ \t\r\n]+$/;
allWhiteSpaceRegExp = /[ \t\r\n]+/g;
isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
function addRootBlocks() {
var node = rootNode.firstChild, next, rootBlockNode;
@ -3448,10 +3681,12 @@ tinymce.html.Styles = function(settings, schema) {
if (elementRule) {
if (blockElements[name]) {
if (!isInWhiteSpacePreservedElement) {
// Trim whitespace at beginning of block
for (textNode = node.firstChild; textNode && textNode.type === 3; ) {
// Trim whitespace of the first node in a block
textNode = node.firstChild;
if (textNode && textNode.type === 3) {
text = textNode.value.replace(startWhiteSpaceRegExp, '');
// Any characters left after trim or should we remove it
if (text.length > 0) {
textNode.value = text;
textNode = textNode.next;
@ -3460,12 +3695,27 @@ tinymce.html.Styles = function(settings, schema) {
textNode.remove();
textNode = sibling;
}
// Remove any pure whitespace siblings
while (textNode && textNode.type === 3) {
text = textNode.value;
sibling = textNode.next;
if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
textNode.remove();
textNode = sibling;
}
// Trim whitespace at end of block
for (textNode = node.lastChild; textNode && textNode.type === 3; ) {
textNode = sibling;
}
}
// Trim whitespace of the last node in a block
textNode = node.lastChild;
if (textNode && textNode.type === 3) {
text = textNode.value.replace(endWhiteSpaceRegExp, '');
// Any characters left after trim or should we remove it
if (text.length > 0) {
textNode.value = text;
textNode = textNode.prev;
@ -3474,6 +3724,19 @@ tinymce.html.Styles = function(settings, schema) {
textNode.remove();
textNode = sibling;
}
// Remove any pure whitespace siblings
while (textNode && textNode.type === 3) {
text = textNode.value;
sibling = textNode.prev;
if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
textNode.remove();
textNode = sibling;
}
textNode = sibling;
}
}
}
@ -3578,8 +3841,8 @@ tinymce.html.Styles = function(settings, schema) {
// these elements and keep br elements that where intended to be there intact
if (settings.remove_trailing_brs) {
self.addNodeFilter('br', function(nodes, name) {
var i, l = nodes.length, node, blockElements = schema.getBlockElements(),
nonEmptyElements = schema.getNonEmptyElements(), parent, prev, prevName;
var i, l = nodes.length, node, blockElements = tinymce.extend({}, schema.getBlockElements()),
nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
// Remove brs from body element as well
blockElements.body = 1;
@ -3590,7 +3853,7 @@ tinymce.html.Styles = function(settings, schema) {
parent = node.parent;
if (blockElements[node.parent.name] && node === parent.lastChild) {
// Loop all nodes to the right of the current node and check for other BR elements
// Loop all nodes to the left of the current node and check for other BR elements
// excluding bookmarks since they are invisible
prev = node.prev;
while (prev) {
@ -3628,6 +3891,46 @@ tinymce.html.Styles = function(settings, schema) {
}
}
}
} else {
// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> so they become <p><b><i>&nbsp;</i></b></p>
lastParent = node;
while (parent.firstChild === lastParent && parent.lastChild === lastParent) {
lastParent = parent;
if (blockElements[parent.name]) {
break;
}
parent = parent.parent;
}
if (lastParent === parent) {
textNode = new tinymce.html.Node('#text', 3);
textNode.value = '\u00a0';
node.replace(textNode);
}
}
}
});
}
// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
if (!settings.allow_html_in_named_anchor) {
self.addAttributeFilter('name', function(nodes, name) {
var i = nodes.length, sibling, prevSibling, parent, node;
while (i--) {
node = nodes[i];
if (node.name === 'a' && node.firstChild) {
parent = node.parent;
// Move children after current node
sibling = node.lastChild;
do {
prevSibling = sibling.prev;
parent.insert(sibling, node);
sibling = prevSibling;
} while (sibling);
}
}
});
@ -4314,7 +4617,11 @@ tinymce.dom = {};
return self.bind(target, events instanceof Array ? events.join(' ') : events, func, scope);
};
self.remove = function(target, events, func) {
self.remove = function(target, events, func, scope) {
if (!target) {
return self;
}
// Old API supported direct ID assignment
if (typeof(target) === "string") {
target = document.getElementById(target);
@ -4322,7 +4629,7 @@ tinymce.dom = {};
// Old API supported multiple targets
if (target instanceof Array) {
var i = target;
var i = target.length;
while (i--) {
self.remove(target[i], events, func, scope);
@ -4379,6 +4686,46 @@ tinymce.dom = {};
namespace = 0;
})(tinymce.dom, 'data-mce-expando'); // Namespace and expando
tinymce.dom.TreeWalker = function(start_node, root_node) {
var node = start_node;
function findSibling(node, start_name, sibling_name, shallow) {
var sibling, parent;
if (node) {
// Walk into nodes if it has a start
if (!shallow && node[start_name])
return node[start_name];
// Return the sibling if it has one
if (node != root_node) {
sibling = node[sibling_name];
if (sibling)
return sibling;
// Walk up the parents to look for siblings
for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
sibling = parent[sibling_name];
if (sibling)
return sibling;
}
}
}
};
this.current = function() {
return node;
};
this.next = function(shallow) {
return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));
};
this.prev = function(shallow) {
return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));
};
};
(function(tinymce) {
// Shorten names
var each = tinymce.each,
@ -4568,8 +4915,8 @@ tinymce.dom = {};
h = 0;
return {
w : parseInt(w) || e.offsetWidth || e.clientWidth,
h : parseInt(h) || e.offsetHeight || e.clientHeight
w : parseInt(w, 10) || e.offsetWidth || e.clientWidth,
h : parseInt(h, 10) || e.offsetHeight || e.clientHeight
};
},
@ -5093,7 +5440,7 @@ tinymce.dom = {};
if (!u)
u = '';
head = t.select('head')[0];
head = d.getElementsByTagName('head')[0];
each(u.split(','), function(u) {
var link;
@ -5369,7 +5716,7 @@ tinymce.dom = {};
var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
function hex(s) {
s = parseInt(s).toString(16);
s = parseInt(s, 10).toString(16);
return s.length > 1 ? s : '0' + s; // 0 -> 00
};
@ -5622,7 +5969,7 @@ tinymce.dom = {};
// Also keep text nodes with only spaces if surrounded by spans.
// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
var trimmedLength = tinymce.trim(node.nodeValue).length;
if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength == 0 && surroundedBySpans(node))
if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node))
return;
} else if (type == 1) {
// If the only child is a bookmark then move it up
@ -5683,6 +6030,25 @@ tinymce.dom = {};
return this.events.fire(target, name, evt);
},
// Returns the content editable state of a node
getContentEditable: function(node) {
var contentEditable;
// Check type
if (node.nodeType != 1) {
return null;
}
// Check for fake content editable
contentEditable = node.getAttribute("data-mce-contenteditable");
if (contentEditable && contentEditable !== "inherit") {
return contentEditable;
}
// Check for real content editable
return node.contentEditable !== "inherit" ? node.contentEditable : null;
},
_findSib : function(node, selector, name) {
var t = this, f = selector;
@ -6502,7 +6868,7 @@ tinymce.dom = {};
// We need to walk char by char since rng.text or rng.htmlText will trim line endings
offset = 0;
while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
if (checkRng.move('character', 1) == 0 || parent != checkRng.parentElement()) {
if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
break;
}
@ -6515,7 +6881,7 @@ tinymce.dom = {};
// Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one
offset = 0;
while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
if (checkRng.move('character', -1) == 0 || parent != checkRng.parentElement()) {
if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
break;
}
@ -6681,7 +7047,7 @@ tinymce.dom = {};
var rng = selection.getRng(), start, end, bookmark = {};
function getIndexes(node) {
var node, parent, root, children, i, indexes = [];
var parent, root, children, i, indexes = [];
parent = node.parentNode;
root = dom.getRoot().parentNode;
@ -6846,13 +7212,18 @@ tinymce.dom = {};
// If single element selection then try making a control selection out of it
if (startContainer == endContainer && startContainer.nodeType == 1) {
// Trick to place the caret inside an empty block element like <p></p>
if (!startContainer.hasChildNodes()) {
if (startOffset == endOffset && !startContainer.hasChildNodes()) {
if (startContainer.canHaveHTML) {
startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';
ieRng.moveToElementText(startContainer.lastChild);
ieRng.select();
dom.doc.selection.clear();
startContainer.innerHTML = '';
return;
} else {
startOffset = dom.nodeIndex(startContainer);
startContainer = startContainer.parentNode;
}
}
if (startOffset == endOffset - 1) {
@ -6885,18 +7256,22 @@ tinymce.dom = {};
/*
* Sizzle CSS Selector Engine - v1.0
* Copyright 2009, The Dojo Foundation
* Sizzle CSS Selector Engine
* Copyright, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
expando = "sizcache",
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
baseHasDuplicate = true;
baseHasDuplicate = true,
rBackslash = /\\/g,
rReturn = /\r\n/g,
rNonWord = /\W/;
// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
@ -6921,8 +7296,11 @@ var Sizzle = function(selector, context, results, seed) {
return results;
}
var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
soFar = selector, ret, cur, pop, i;
var m, set, checkSet, extra, ret, cur, pop, i,
prune = true,
contextXML = Sizzle.isXML( context ),
parts = [],
soFar = selector;
// Reset the position of the chunker regexp (start from head)
do {
@ -6942,8 +7320,10 @@ var Sizzle = function(selector, context, results, seed) {
} while ( m );
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context );
set = posProcess( parts[0] + parts[1], context, seed );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
@ -6956,26 +7336,34 @@ var Sizzle = function(selector, context, results, seed) {
selector += parts.shift();
}
set = posProcess( selector, set );
set = posProcess( selector, set, seed );
}
}
} else {
// Take a shortcut and set the context if the root selector is an ID
// (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]) ) {
ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
context = ret.expr ?
Sizzle.filter( ret.expr, ret.set )[0] :
ret.set[0];
}
if ( context ) {
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;
set = ret.expr ?
Sizzle.filter( ret.expr, ret.set ) :
ret.set;
if ( parts.length > 0 ) {
checkSet = makeArray( set );
} else {
prune = false;
}
@ -6996,6 +7384,7 @@ var Sizzle = function(selector, context, results, seed) {
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
@ -7012,12 +7401,14 @@ var Sizzle = function(selector, context, results, seed) {
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
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 ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
@ -7025,6 +7416,7 @@ var Sizzle = function(selector, context, results, seed) {
}
}
}
} else {
makeArray( checkSet, results );
}
@ -7058,23 +7450,28 @@ Sizzle.matches = function(expr, set){
return Sizzle( expr, null, null, set );
};
Sizzle.matchesSelector = function( node, expr ) {
return Sizzle( expr, null, null, [node] ).length > 0;
};
Sizzle.find = function( expr, context, isXML ) {
var set;
var set, i, len, match, type, left;
if ( !expr ) {
return [];
}
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
for ( i = 0, len = Expr.order.length; i < len; i++ ) {
type = Expr.order[i];
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
var left = match[1];
left = match[1];
match.splice( 1, 1 );
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
match[1] = (match[1] || "").replace( rBackslash, "" );
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
@ -7084,20 +7481,29 @@ Sizzle.find = function(expr, context, isXML){
}
if ( !set ) {
set = context.getElementsByTagName("*");
set = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( "*" ) :
[];
}
return { set: set, expr: expr };
};
Sizzle.filter = function( expr, set, inplace, not ) {
var old = expr, result = [], curLoop = set, match, anyFound,
var match, anyFound,
type, found, item, filter, left,
i, pass,
old = expr,
result = [],
curLoop = set,
isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
for ( type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
var filter = Expr.filter[ type ], found, item, left = match[1];
filter = Expr.filter[ type ];
left = match[1];
anyFound = false;
match.splice(1,1);
@ -7115,23 +7521,26 @@ Sizzle.filter = function(expr, set, inplace, not){
if ( !match ) {
anyFound = found = true;
} else if ( match === true ) {
continue;
}
}
if ( match ) {
for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
for ( i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
var pass = not ^ !!found;
pass = not ^ found;
if ( inplace && found != null ) {
if ( pass ) {
anyFound = true;
} else {
curLoop[i] = false;
}
} else if ( pass ) {
result.push( item );
anyFound = true;
@ -7160,6 +7569,7 @@ Sizzle.filter = function(expr, set, inplace, not){
if ( expr === old ) {
if ( anyFound == null ) {
Sizzle.error( expr );
} else {
break;
}
@ -7172,35 +7582,78 @@ Sizzle.filter = function(expr, set, inplace, not){
};
Sizzle.error = function( msg ) {
throw "Syntax error, unrecognized expression: " + msg;
throw new Error( "Syntax error, unrecognized expression: " + msg );
};
var getText = Sizzle.getText = function( elem ) {
var i, node,
nodeType = elem.nodeType,
ret = "";
if ( nodeType ) {
if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent || innerText for elements
if ( typeof elem.textContent === 'string' ) {
return elem.textContent;
} else if ( typeof elem.innerText === 'string' ) {
// Replace IE's carriage returns
return elem.innerText.replace( rReturn, '' );
} else {
// Traverse it's children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
} else {
// If no nodeType, this is expected to be an array
for ( i = 0; (node = elem[i]); i++ ) {
// Do not traverse comment nodes
if ( node.nodeType !== 8 ) {
ret += getText( node );
}
}
}
return ret;
};
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*\]/,
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function( elem ) {
return elem.getAttribute( "href" );
},
type: function( elem ) {
return elem.getAttribute( "type" );
}
},
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test(part),
isTag = isPartStr && !rNonWord.test( part ),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag ) {
@ -7221,23 +7674,29 @@ var Expr = Sizzle.selectors = {
Sizzle.filter( part, checkSet, true );
}
},
">": function(checkSet, part){
var isPartStr = typeof part === "string",
elem, i = 0, l = checkSet.length;
if ( isPartStr && !/\W/.test(part) ) {
">": function( checkSet, part ) {
var elem,
isPartStr = typeof part === "string",
i = 0,
l = checkSet.length;
if ( isPartStr && !rNonWord.test( part ) ) {
part = part.toLowerCase();
for ( ; i < l; i++ ) {
elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
} else {
for ( ; i < l; i++ ) {
elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
@ -7250,10 +7709,13 @@ var Expr = Sizzle.selectors = {
}
}
},
"": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck, nodeCheck;
if ( typeof part === "string" && !/\W/.test(part) ) {
"": function(checkSet, part, isXML){
var nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
@ -7261,10 +7723,13 @@ var Expr = Sizzle.selectors = {
checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
},
"~": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck, nodeCheck;
if ( typeof part === "string" && !/\W/.test(part) ) {
"~": function( checkSet, part, isXML ) {
var nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
@ -7273,16 +7738,21 @@ var Expr = Sizzle.selectors = {
checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
}
},
find: {
ID: function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? [m] : [];
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
},
NAME: function( match, context ) {
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [], results = context.getElementsByName(match[1]);
var ret = [],
results = context.getElementsByName( match[1] );
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
@ -7293,13 +7763,16 @@ var Expr = Sizzle.selectors = {
return ret.length === 0 ? null : ret;
}
},
TAG: function( match, context ) {
if ( typeof context.getElementsByTagName !== "undefined" ) {
return context.getElementsByTagName( match[1] );
}
}
},
preFilter: {
CLASS: function( match, curLoop, inplace, result, not, isXML ) {
match = " " + match[1].replace(/\\/g, "") + " ";
match = " " + match[1].replace( rBackslash, "" ) + " ";
if ( isXML ) {
return match;
@ -7307,10 +7780,11 @@ var Expr = Sizzle.selectors = {
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
if ( !inplace ) {
result.push( elem );
}
} else if ( inplace ) {
curLoop[i] = false;
}
@ -7319,16 +7793,25 @@ var Expr = Sizzle.selectors = {
return false;
},
ID: function( match ) {
return match[1].replace(/\\/g, "");
return match[1].replace( rBackslash, "" );
},
TAG: function( match, curLoop ) {
return match[1].toLowerCase();
return match[1].replace( rBackslash, "" ).toLowerCase();
},
CHILD: function( match ) {
if ( match[1] === "nth" ) {
if ( !match[2] ) {
Sizzle.error( match[0] );
}
match[2] = match[2].replace(/^\+|\s*/g, '');
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
@ -7336,141 +7819,196 @@ var Expr = Sizzle.selectors = {
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
else if ( match[2] ) {
Sizzle.error( match[0] );
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
ATTR: function( match, curLoop, inplace, result, not, isXML ) {
var name = match[1].replace(/\\/g, "");
var name = match[1] = match[1].replace( rBackslash, "" );
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
// Handle if an un-quoted value was used
match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
return match;
},
PSEUDO: function( match, curLoop, inplace, result, not ) {
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
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);
if ( !inplace ) {
result.push.apply( result, ret );
}
return false;
}
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
return match;
},
POS: function( match ) {
match.unshift( true );
return match;
}
},
filters: {
enabled: function( elem ) {
return elem.disabled === false && elem.type !== "hidden";
},
disabled: function( elem ) {
return elem.disabled === true;
},
checked: function( elem ) {
return elem.checked === true;
},
selected: function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
parent: function( elem ) {
return !!elem.firstChild;
},
empty: function( elem ) {
return !elem.firstChild;
},
has: function( elem, i, match ) {
return !!Sizzle( match[3], elem ).length;
},
header: function( elem ) {
return (/h\d/i).test( elem.nodeName );
},
text: function( elem ) {
return "text" === elem.type;
var attr = elem.getAttribute( "type" ), type = elem.type;
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
// use getAttribute instead to test this case
return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
},
radio: function( elem ) {
return "radio" === elem.type;
return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
},
checkbox: function( elem ) {
return "checkbox" === elem.type;
return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
},
file: function( elem ) {
return "file" === elem.type;
return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
},
password: function( elem ) {
return "password" === elem.type;
return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
},
submit: function( elem ) {
return "submit" === elem.type;
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && "submit" === elem.type;
},
image: function( elem ) {
return "image" === elem.type;
return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
},
reset: function( elem ) {
return "reset" === elem.type;
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && "reset" === elem.type;
},
button: function( elem ) {
return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
var name = elem.nodeName.toLowerCase();
return name === "input" && "button" === elem.type || name === "button";
},
input: function( elem ) {
return (/input|select|textarea|button/i).test( elem.nodeName );
},
focus: function( elem ) {
return elem === elem.ownerDocument.activeElement;
}
},
setFilters: {
first: function( elem, i ) {
return i === 0;
},
last: function( elem, i, match, array ) {
return i === array.length - 1;
},
even: function( elem, i ) {
return i % 2 === 0;
},
odd: function( elem, i ) {
return i % 2 === 1;
},
lt: function( elem, i, match ) {
return i < match[3] - 0;
},
gt: function( elem, i, match ) {
return i > match[3] - 0;
},
nth: function( elem, i, match ) {
return match[3] - 0 === i;
},
eq: function( elem, i, match ) {
return match[3] - 0 === i;
}
},
filter: {
PSEUDO: function( elem, match, i, array ) {
var name = match[1], filter = Expr.filters[ name ];
var name = match[1],
filter = Expr.filters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
@ -7481,72 +8019,96 @@ var Expr = Sizzle.selectors = {
}
return true;
} else {
Sizzle.error( "Syntax error, unrecognized expression: " + name );
Sizzle.error( name );
}
},
CHILD: function( elem, match ) {
var type = match[1], node = elem;
var first, last,
doneName, parent, cache,
count, diff,
type = match[1],
node = elem;
switch ( type ) {
case 'only':
case 'first':
case "only":
case "first":
while ( (node = node.previousSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
if ( type === "first" ) {
return true;
}
node = elem;
case 'last':
/* falls through */
case "last":
while ( (node = node.nextSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
return true;
case 'nth':
var first = match[2], last = match[3];
case "nth":
first = match[2];
last = match[3];
if ( first === 1 && last === 0 ) {
return true;
}
var doneName = match[0],
doneName = match[0];
parent = elem.parentNode;
if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
var count = 0;
if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
count = 0;
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
}
parent.sizcache = doneName;
parent[ expando ] = doneName;
}
var diff = elem.nodeIndex - last;
diff = elem.nodeIndex - last;
if ( first === 0 ) {
return diff === 0;
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
}
},
ID: function( elem, match ) {
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function( elem, match ) {
return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
},
CLASS: function( elem, match ) {
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
ATTR: function( elem, match ) {
var name = match[1],
result = Expr.attrHandle[ name ] ?
result = Sizzle.attr ?
Sizzle.attr( elem, name ) :
Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
@ -7557,6 +8119,8 @@ var Expr = Sizzle.selectors = {
return result == null ?
type === "!=" :
!type && Sizzle.attr ?
result != null :
type === "=" ?
value === check :
type === "*=" ?
@ -7575,8 +8139,10 @@ var Expr = Sizzle.selectors = {
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
},
POS: function( elem, match, i, array ) {
var name = match[2], filter = Expr.setFilters[ name ];
var name = match[2],
filter = Expr.setFilters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
@ -7594,6 +8160,9 @@ for ( var type in Expr.match ) {
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) );
}
// Expose origPOS
// "global" as in regardless of relation to brackets/parens
Expr.match.globalPOS = origPOS;
var makeArray = function( array, results ) {
array = Array.prototype.slice.call( array, 0 );
@ -7616,15 +8185,18 @@ try {
// Provide a fallback method if it does not work
} catch( e ) {
makeArray = function( array, results ) {
var ret = results || [], i = 0;
var i = 0,
ret = results || [];
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( ; array[i]; i++ ) {
ret.push( array[i] );
@ -7636,90 +8208,113 @@ try {
};
}
var sortOrder;
var sortOrder, siblingCheck;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
if ( a == b ) {
if ( a === b ) {
hasDuplicate = true;
return 0;
}
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
return a.compareDocumentPosition ? -1 : 1;
}
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
return a.compareDocumentPosition(b) & 4 ? -1 : 1;
};
} else if ( "sourceIndex" in document.documentElement ) {
} else {
sortOrder = function( a, b ) {
if ( !a.sourceIndex || !b.sourceIndex ) {
if ( a == b ) {
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
}
return a.sourceIndex ? -1 : 1;
return 0;
// Fallback to using sourceIndex (in IE) if it's available on both nodes
} else if ( a.sourceIndex && b.sourceIndex ) {
return a.sourceIndex - b.sourceIndex;
}
var ret = a.sourceIndex - b.sourceIndex;
if ( ret === 0 ) {
hasDuplicate = true;
var al, bl,
ap = [],
bp = [],
aup = a.parentNode,
bup = b.parentNode,
cur = aup;
// If the nodes are siblings (or identical) we can do a quick check
if ( aup === bup ) {
return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected
} else if ( !aup ) {
return -1;
} else if ( !bup ) {
return 1;
}
return ret;
// Otherwise they're somewhere else in the tree so we need
// to build up a full list of the parentNodes for comparison
while ( cur ) {
ap.unshift( cur );
cur = cur.parentNode;
}
cur = bup;
while ( cur ) {
bp.unshift( cur );
cur = cur.parentNode;
}
al = ap.length;
bl = bp.length;
// Start walking down the tree looking for a discrepancy
for ( var i = 0; i < al && i < bl; i++ ) {
if ( ap[i] !== bp[i] ) {
return siblingCheck( ap[i], bp[i] );
}
}
// We ended someplace up the tree so do a sibling check
return i === al ?
siblingCheck( a, bp[i], -1 ) :
siblingCheck( ap[i], b, 1 );
};
} else if ( document.createRange ) {
sortOrder = function( a, b ) {
if ( !a.ownerDocument || !b.ownerDocument ) {
if ( a == b ) {
hasDuplicate = true;
}
return a.ownerDocument ? -1 : 1;
siblingCheck = function( a, b, ret ) {
if ( a === b ) {
return ret;
}
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.setStart(a, 0);
aRange.setEnd(a, 0);
bRange.setStart(b, 0);
bRange.setEnd(b, 0);
var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
if ( ret === 0 ) {
hasDuplicate = true;
var cur = a.nextSibling;
while ( cur ) {
if ( cur === b ) {
return -1;
}
return ret;
cur = cur.nextSibling;
}
return 1;
};
}
// 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(),
root = document.documentElement;
form.innerHTML = "<a name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
var root = document.documentElement;
root.insertBefore( form, root.firstChild );
// The workaround has to do additional checks after a getElementById
@ -7728,18 +8323,26 @@ Sizzle.getText = function( elems ) {
Expr.find.ID = function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
return m ?
m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
[m] :
undefined :
[];
}
};
Expr.filter.ID = function( elem, match ) {
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
root.removeChild( form );
root = form = null; // release memory in IE
// release memory in IE
root = form = null;
})();
(function(){
@ -7774,19 +8377,25 @@ Sizzle.getText = function( elems ) {
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "<a href='#'></a>";
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function( elem ) {
return elem.getAttribute( "href", 2 );
};
}
div = null; // release memory in IE
// release memory in IE
div = null;
})();
if ( document.querySelectorAll ) {
(function(){
var oldSizzle = Sizzle, div = document.createElement("div");
var oldSizzle = Sizzle,
div = document.createElement("div"),
id = "__sizzle__";
div.innerHTML = "<p class='TEST'></p>";
// Safari can't handle uppercase or unicode characters when
@ -7800,10 +8409,81 @@ if ( document.querySelectorAll ) {
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {
if ( !seed && !Sizzle.isXML(context) ) {
// See if we find a selector to speed up
var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
// Speed-up: Sizzle("TAG")
if ( match[1] ) {
return makeArray( context.getElementsByTagName( query ), extra );
// Speed-up: Sizzle(".CLASS")
} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
return makeArray( context.getElementsByClassName( match[2] ), extra );
}
}
if ( context.nodeType === 9 ) {
// Speed-up: Sizzle("body")
// The body element only exists once, optimize finding it
if ( query === "body" && context.body ) {
return makeArray( [ context.body ], extra );
// Speed-up: Sizzle("#ID")
} else if ( match && match[3] ) {
var elem = context.getElementById( match[3] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id === match[3] ) {
return makeArray( [ elem ], extra );
}
} else {
return makeArray( [], extra );
}
}
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(e){}
} catch(qsaError) {}
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
var oldContext = context,
old = context.getAttribute( "id" ),
nid = old || id,
hasParent = context.parentNode,
relativeHierarchySelector = /^\s*[+~]/.test( query );
if ( !old ) {
context.setAttribute( "id", nid );
} else {
nid = nid.replace( /'/g, "\\$&" );
}
if ( relativeHierarchySelector && hasParent ) {
context = context.parentNode;
}
try {
if ( !relativeHierarchySelector || hasParent ) {
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
}
} catch(pseudoError) {
} finally {
if ( !old ) {
oldContext.removeAttribute( "id" );
}
}
}
}
return oldSizzle(query, context, extra, seed);
@ -7813,10 +8493,55 @@ if ( document.querySelectorAll ) {
Sizzle[ prop ] = oldSizzle[ prop ];
}
div = null; // release memory in IE
// release memory in IE
div = null;
})();
}
(function(){
var html = document.documentElement,
matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
if ( matches ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9 fails this)
var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
pseudoWorks = false;
try {
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call( document.documentElement, "[test!='']:sizzle" );
} catch( pseudoError ) {
pseudoWorks = true;
}
Sizzle.matchesSelector = function( node, expr ) {
// Make sure that attribute selectors are quoted
expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
if ( !Sizzle.isXML( node ) ) {
try {
if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
var ret = matches.call( node, expr );
// IE 9's matchesSelector returns false on disconnected nodes
if ( ret || !disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9, so check for that
node.document && node.document.nodeType !== 11 ) {
return ret;
}
}
} catch(e) {}
}
return Sizzle(expr, null, null, [node]).length > 0;
};
}
})();
(function(){
var div = document.createElement("div");
@ -7842,24 +8567,27 @@ if ( document.querySelectorAll ) {
}
};
div = null; // release memory in IE
// release memory in IE
div = null;
})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
elem = elem[dir];
var match = false;
elem = elem[dir];
while ( elem ) {
if ( elem.sizcache === doneName ) {
if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 && !isXML ){
elem.sizcache = doneName;
elem[ expando ] = doneName;
elem.sizset = i;
}
@ -7879,21 +8607,24 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
elem = elem[dir];
var match = false;
elem = elem[dir];
while ( elem ) {
if ( elem.sizcache === doneName ) {
if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 ) {
if ( !isXML ) {
elem.sizcache = doneName;
elem[ expando ] = doneName;
elem.sizset = i;
}
if ( typeof cur !== "string" ) {
if ( elem === cur ) {
match = true;
@ -7914,21 +8645,34 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
}
}
Sizzle.contains = document.compareDocumentPosition ? function(a, b){
return !!(a.compareDocumentPosition(b) & 16);
} : function(a, b){
if ( document.documentElement.contains ) {
Sizzle.contains = function( a, b ) {
return a !== b && (a.contains ? a.contains(b) : true);
};
} else if ( document.documentElement.compareDocumentPosition ) {
Sizzle.contains = function( a, b ) {
return !!(a.compareDocumentPosition(b) & 16);
};
} else {
Sizzle.contains = function() {
return false;
};
}
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){
var tmpSet = [], later = "", match,
var posProcess = function( selector, context, seed ) {
var match,
tmpSet = [],
later = "",
root = context.nodeType ? [context] : context;
// Position selectors must be done after the filter
@ -7941,7 +8685,7 @@ var posProcess = function(selector, context){
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet );
Sizzle( selector, root[i], tmpSet, seed );
}
return Sizzle.filter( later, tmpSet );
@ -7970,8 +8714,7 @@ window.tinymce.dom.Sizzle = Sizzle;
('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' +
'setAttrib,setAttribs,getAttrib,addClass,removeClass,' +
'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' +
'isHidden,setHTML,get').split(/,/)
, function(k) {
'isHidden,setHTML,get').split(/,/), function(k) {
t[k] = function() {
var a = [id], i;
@ -7983,7 +8726,8 @@ window.tinymce.dom.Sizzle = Sizzle;
return a;
};
});
}
);
tinymce.extend(t, {
on : function(n, f, s) {
@ -8069,7 +8813,7 @@ window.tinymce.dom.Sizzle = Sizzle;
};
// Shorten names
var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;
tinymce.create('tinymce.dom.Selection', {
Selection : function(dom, win, serializer) {
@ -8176,7 +8920,7 @@ window.tinymce.dom.Sizzle = Sizzle;
} else {
rng.deleteContents();
if (doc.body.childNodes.length == 0) {
if (doc.body.childNodes.length === 0) {
doc.body.innerHTML = content;
} else {
// createContextualFragment doesn't exists in IE 9 DOMRanges
@ -8312,7 +9056,29 @@ window.tinymce.dom.Sizzle = Sizzle;
return index;
};
if (type == 2) {
function normalizeTableCellSelection(rng) {
function moveEndPoint(start) {
var container, offset, childNodes, prefix = start ? 'start' : 'end';
container = rng[prefix + 'Container'];
offset = rng[prefix + 'Offset'];
if (container.nodeType == 1 && container.nodeName == "TR") {
childNodes = container.childNodes;
container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
if (container) {
offset = start ? 0 : container.childNodes.length;
rng['set' + (start ? 'Start' : 'End')](container, offset);
}
}
};
moveEndPoint(true);
moveEndPoint();
return rng;
};
function getLocation() {
var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
@ -8352,6 +9118,7 @@ window.tinymce.dom.Sizzle = Sizzle;
return bookmark;
};
if (type == 2) {
if (t.tridentSel)
return t.tridentSel.getBookmark(type);
@ -8384,7 +9151,7 @@ window.tinymce.dom.Sizzle = Sizzle;
// Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>
rng.moveToElementText(rng2.parentElement());
if (rng.compareEndPoints('StartToEnd', rng2) == 0)
if (rng.compareEndPoints('StartToEnd', rng2) === 0)
rng2.move('character', -1);
rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
@ -8406,13 +9173,8 @@ window.tinymce.dom.Sizzle = Sizzle;
if (name == 'IMG')
return {name : name, index : findIndex(name, element)};
// Can't insert a node into the root of document WebKit defaults to document
if (rng.startContainer.nodeType == 9) {
return;
}
// W3C method
rng2 = rng.cloneRange();
rng2 = normalizeTableCellSelection(rng.cloneRange());
// Insert end marker
if (!collapsed) {
@ -8420,6 +9182,7 @@ window.tinymce.dom.Sizzle = Sizzle;
rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));
}
rng = normalizeTableCellSelection(rng);
rng.collapse(true);
rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));
}
@ -8432,11 +9195,6 @@ window.tinymce.dom.Sizzle = Sizzle;
moveToBookmark : function(bookmark) {
var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;
if (bookmark) {
if (bookmark.start) {
rng = dom.createRng();
root = dom.getRoot();
function setEndPoint(start) {
var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
@ -8471,13 +9229,6 @@ window.tinymce.dom.Sizzle = Sizzle;
return true;
};
if (t.tridentSel)
return t.tridentSel.moveToBookmark(bookmark);
if (setEndPoint(true) && setEndPoint()) {
t.setRng(rng);
}
} else if (bookmark.id) {
function restoreEndPoint(suffix) {
var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
@ -8541,13 +9292,25 @@ window.tinymce.dom.Sizzle = Sizzle;
};
function addBogus(node) {
// Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly
if (dom.isBlock(node) && !node.innerHTML)
node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' ';
// Adds a bogus BR element for empty block elements
if (dom.isBlock(node) && !node.innerHTML && !isIE)
node.innerHTML = '<br data-mce-bogus="1" />';
return node;
};
if (bookmark) {
if (bookmark.start) {
rng = dom.createRng();
root = dom.getRoot();
if (t.tridentSel)
return t.tridentSel.moveToBookmark(bookmark);
if (setEndPoint(true) && setEndPoint()) {
t.setRng(rng);
}
} else if (bookmark.id) {
// Restore start/end points
restoreEndPoint('start');
restoreEndPoint('end');
@ -8568,19 +9331,12 @@ window.tinymce.dom.Sizzle = Sizzle;
select : function(node, content) {
var t = this, dom = t.dom, rng = dom.createRng(), idx;
if (node) {
idx = dom.nodeIndex(node);
rng.setStart(node.parentNode, idx);
rng.setEnd(node.parentNode, idx + 1);
// Find first/last text node or BR element
if (content) {
function setPoint(node, start) {
var walker = new tinymce.dom.TreeWalker(node, node);
var walker = new TreeWalker(node, node);
do {
// Text node
if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {
if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length !== 0) {
if (start)
rng.setStart(node, 0);
else
@ -8601,6 +9357,13 @@ window.tinymce.dom.Sizzle = Sizzle;
} while (node = (start ? walker.next() : walker.prev()));
};
if (node) {
idx = dom.nodeIndex(node);
rng.setStart(node.parentNode, idx);
rng.setEnd(node.parentNode, idx + 1);
// Find first/last text node or BR element
if (content) {
setPoint(node, 1);
setPoint(node);
}
@ -8644,48 +9407,58 @@ window.tinymce.dom.Sizzle = Sizzle;
},
getRng : function(w3c) {
var t = this, s, r, elm, doc = t.win.document;
var self = this, selection, rng, elm, doc = self.win.document;
// Found tridentSel object then we need to use that one
if (w3c && t.tridentSel)
return t.tridentSel.getRangeAt(0);
if (w3c && self.tridentSel) {
return self.tridentSel.getRangeAt(0);
}
try {
if (s = t.getSel())
r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());
if (selection = self.getSel()) {
rng = selection.rangeCount > 0 ? selection.getRangeAt(0) : (selection.createRange ? selection.createRange() : doc.createRange());
}
} catch (ex) {
// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
}
// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) {
if (tinymce.isIE && rng && rng.setStart && doc.selection.createRange().item) {
elm = doc.selection.createRange().item(0);
r = doc.createRange();
r.setStartBefore(elm);
r.setEndAfter(elm);
rng = doc.createRange();
rng.setStartBefore(elm);
rng.setEndAfter(elm);
}
// No range found then create an empty one
// This can occur when the editor is placed in a hidden container element on Gecko
// Or on IE when there was an exception
if (!r)
r = doc.createRange ? doc.createRange() : doc.body.createTextRange();
if (!rng) {
rng = doc.createRange ? doc.createRange() : doc.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) {
// If range is at start of document then move it to start of body
if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
elm = self.dom.getRoot();
rng.setStart(elm, 0);
rng.setEnd(elm, 0);
}
if (self.selectedRange && self.explicitRange) {
if (rng.compareBoundaryPoints(rng.START_TO_START, self.selectedRange) === 0 && rng.compareBoundaryPoints(rng.END_TO_END, self.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;
rng = self.explicitRange;
} else {
t.selectedRange = null;
t.explicitRange = null;
self.selectedRange = null;
self.explicitRange = null;
}
}
return r;
return rng;
},
setRng : function(r) {
setRng : function(r, forward) {
var s, t = this;
if (!t.tridentSel) {
@ -8701,6 +9474,13 @@ window.tinymce.dom.Sizzle = Sizzle;
}
s.addRange(r);
// Forward is set to false and we have an extend function
if (forward === false && s.extend) {
s.collapse(r.endContainer, r.endOffset);
s.extend(r.startContainer, r.startOffset);
}
// adding range isn't always successful so we need to check range count otherwise an exception can occur
t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;
}
@ -8735,6 +9515,14 @@ window.tinymce.dom.Sizzle = Sizzle;
getNode : function() {
var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;
function skipEmptyTextNodes(n, forwards) {
var orig = n;
while (n && n.nodeType === 3 && n.length === 0) {
n = forwards ? n.nextSibling : n.previousSibling;
}
return n || orig;
};
// Range maybe lost after the editor is made visible again
if (!rng)
return t.dom.getRoot();
@ -8758,13 +9546,6 @@ window.tinymce.dom.Sizzle = Sizzle;
// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
// This happens when you double click an underlined word in FireFox.
if (start.nodeType === 3 && end.nodeType === 3) {
function skipEmptyTextNodes(n, forwards) {
var orig = n;
while (n && n.nodeType === 3 && n.length === 0) {
n = forwards ? n.nextSibling : n.previousSibling;
}
return n || orig;
}
if (start.length === rng.startOffset) {
start = skipEmptyTextNodes(start.nextSibling, true);
} else {
@ -8802,7 +9583,7 @@ window.tinymce.dom.Sizzle = Sizzle;
if (sb && eb && sb != eb) {
n = sb;
var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());
var walker = new TreeWalker(sb, dom.getRoot());
while ((n = walker.next()) && n != eb) {
if (dom.isBlock(n))
bl.push(n);
@ -8815,54 +9596,120 @@ window.tinymce.dom.Sizzle = Sizzle;
return bl;
},
isForward: function(){
var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
// No support for selection direction then always return true
if (!sel || sel.anchorNode == null || sel.focusNode == null) {
return true;
}
anchorRange = dom.createRng();
anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
anchorRange.collapse(true);
focusRange = dom.createRng();
focusRange.setStart(sel.focusNode, sel.focusOffset);
focusRange.collapse(true);
return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
},
normalize : function() {
var self = this, rng, normalized;
// TODO:
// Retain selection direction.
// Lean left/right on Gecko for inline elements.
// Run this on mouse up/key up when the user manually moves the selection
// Normalize only on non IE browsers for now
if (tinymce.isIE)
return;
var self = this, rng, normalized, collapsed, node, sibling;
function normalizeEndPoint(start) {
var container, offset, walker, dom = self.dom, body = dom.getRoot(), node;
var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, nodeName;
function hasBrBeforeAfter(node, left) {
var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
while (node = walker[left ? 'prev' : 'next']()) {
if (node.nodeName === "BR") {
return true;
}
}
};
// Walks the dom left/right to find a suitable text node to move the endpoint into
// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
function findTextNodeRelative(left, startNode) {
var walker, lastInlineElement;
startNode = startNode || container;
walker = new TreeWalker(startNode, dom.getParent(startNode.parentNode, dom.isBlock) || body);
// Walk left until we hit a text node we can move to or a block/br/img
while (node = walker[left ? 'prev' : 'next']()) {
// Found text node that has a length
if (node.nodeType === 3 && node.nodeValue.length > 0) {
container = node;
offset = left ? node.nodeValue.length : 0;
normalized = true;
return;
}
// Break if we find a block or a BR/IMG/INPUT etc
if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
return;
}
lastInlineElement = node;
}
// Only fetch the last inline element when in caret mode for now
if (collapsed && lastInlineElement) {
container = lastInlineElement;
normalized = true;
offset = 0;
}
};
container = rng[(start ? 'start' : 'end') + 'Container'];
offset = rng[(start ? 'start' : 'end') + 'Offset'];
nonEmptyElementsMap = dom.schema.getNonEmptyElements();
// If the container is a document move it to the body element
if (container.nodeType === 9) {
container = container.body;
container = dom.getRoot();
offset = 0;
}
// If the container is body try move it into the closest text node or position
// TODO: Add more logic here to handle element selection cases
if (container === body) {
// If start is before/after a image, table etc
if (start) {
node = container.childNodes[offset > 0 ? offset - 1 : 0];
if (node) {
nodeName = node.nodeName.toLowerCase();
if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
return;
}
}
}
// Resolve the index
if (container.hasChildNodes()) {
container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];
offset = 0;
// Don't walk into elements that doesn't have any child nodes like a IMG
if (container.hasChildNodes()) {
if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
// Walk the DOM to find a text node to place the caret at or a BR
node = container;
walker = new tinymce.dom.TreeWalker(container, body);
walker = new TreeWalker(container, body);
do {
// Found a text node use that position
if (node.nodeType === 3) {
offset = start ? 0 : node.nodeValue.length - 1;
if (node.nodeType === 3 && node.nodeValue.length > 0) {
offset = start ? 0 : node.nodeValue.length;
container = node;
normalized = true;
break;
}
// Found a BR/IMG element that we can place the caret before
if (/^(BR|IMG)$/.test(node.nodeName)) {
if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
offset = dom.nodeIndex(node);
container = node.parentNode;
@ -8879,23 +9726,61 @@ window.tinymce.dom.Sizzle = Sizzle;
}
}
// Lean the caret to the left if possible
if (collapsed) {
// So this: <b>x</b><i>|x</i>
// Becomes: <b>x|</b><i>x</i>
// Seems that only gecko has issues with this
if (container.nodeType === 3 && offset === 0) {
findTextNodeRelative(true);
}
// Lean left into empty inline elements when the caret is before a BR
// So this: <i><b></b><i>|<br></i>
// Becomes: <i><b>|</b><i><br></i>
// Seems that only gecko has issues with this
if (container.nodeType === 1) {
node = container.childNodes[offset];
if(node && node.nodeName === 'BR' && !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
findTextNodeRelative(true, container.childNodes[offset]);
}
}
}
// Lean the start of the selection right if possible
// So this: x[<b>x]</b>
// Becomes: x<b>[x]</b>
if (start && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
findTextNodeRelative(false);
}
// Set endpoint if it was normalized
if (normalized)
rng['set' + (start ? 'Start' : 'End')](container, offset);
};
// Normalize only on non IE browsers for now
if (tinymce.isIE)
return;
rng = self.getRng();
collapsed = rng.collapsed;
// Normalize the end points
normalizeEndPoint(true);
if (!rng.collapsed)
if (!collapsed)
normalizeEndPoint();
// Set the selection if it was normalized
if (normalized) {
// If it was collapsed then make sure it still is
if (collapsed) {
rng.collapse(true);
}
//console.log(self.dom.dumpRng(rng));
self.setRng(rng);
self.setRng(rng, self.isForward());
}
},
@ -8913,9 +9798,6 @@ window.tinymce.dom.Sizzle = Sizzle;
_fixIESelection : function() {
var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;
// Make HTML element unselectable since we are going to handle selection by hand
doc.documentElement.unselectable = true;
// Return range from point or null if it failed
function rngFromPoint(x, y) {
var rng = body.createTextRange();
@ -8965,6 +9847,9 @@ window.tinymce.dom.Sizzle = Sizzle;
startRng = started = 0;
};
// Make HTML element unselectable since we are going to handle selection by hand
doc.documentElement.unselectable = true;
// Detect when user selects outside BODY
dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {
if (e.target.nodeName === 'HTML') {
@ -9039,13 +9924,13 @@ window.tinymce.dom.Sizzle = Sizzle;
}
});
// Remove internal classes mceItem<..>
// Remove internal classes mceItem<..> or mceSelected
htmlParser.addAttributeFilter('class', function(nodes, name) {
var i = nodes.length, node, value;
while (i--) {
node = nodes[i];
value = node.attr('class').replace(/\s*mce(Item\w+|Selected)\s*/g, '');
value = node.attr('class').replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g, '');
node.attr('class', value.length > 0 ? value : null);
}
});
@ -9223,7 +10108,7 @@ window.tinymce.dom.Sizzle = Sizzle;
// Parse and serialize HTML
args.content = htmlSerializer.serialize(
htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)
htmlParser.parse(tinymce.trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
);
// Replace all BOM characters for now until we can find a better solution
@ -9263,7 +10148,7 @@ window.tinymce.dom.Sizzle = Sizzle;
scriptLoadedCallbacks = {},
queueLoadedCallbacks = [],
loading = 0,
undefined;
undef;
function loadScript(url, callback) {
var t = this, dom = tinymce.DOM, elm, uri, loc, id;
@ -9372,7 +10257,7 @@ window.tinymce.dom.Sizzle = Sizzle;
var item, state = states[url];
// Add url to load queue
if (state == undefined) {
if (state == undef) {
queue.push(url);
states[url] = QUEUED;
}
@ -9402,7 +10287,7 @@ window.tinymce.dom.Sizzle = Sizzle;
callback.func.call(callback.scope);
});
scriptLoadedCallbacks[url] = undefined;
scriptLoadedCallbacks[url] = undef;
};
queueLoadedCallbacks.push({
@ -9459,46 +10344,6 @@ window.tinymce.dom.Sizzle = Sizzle;
tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
})(tinymce);
tinymce.dom.TreeWalker = function(start_node, root_node) {
var node = start_node;
function findSibling(node, start_name, sibling_name, shallow) {
var sibling, parent;
if (node) {
// Walk into nodes if it has a start
if (!shallow && node[start_name])
return node[start_name];
// Return the sibling if it has one
if (node != root_node) {
sibling = node[sibling_name];
if (sibling)
return sibling;
// Walk up the parents to look for siblings
for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
sibling = parent[sibling_name];
if (sibling)
return sibling;
}
}
}
};
this.current = function() {
return node;
};
this.next = function(shallow) {
return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));
};
this.prev = function(shallow) {
return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));
};
};
(function(tinymce) {
tinymce.dom.RangeUtils = function(dom) {
var INVISIBLE_CHAR = '\uFEFF';
@ -10159,8 +11004,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
update : function() {
var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
tw = s.max_width ? Math.min(tb.offsetWidth, s.max_width) : tb.offsetWidth;
th = s.max_height ? Math.min(tb.offsetHeight, s.max_height) : tb.offsetHeight;
if (!DOM.boxModel)
t.element.setStyles({width : tw + 2, height : th + 2});
@ -10519,7 +11364,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
})(tinymce);
(function(tinymce) {
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
ListBox : function(id, s, ed) {
@ -10546,7 +11391,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
t.marked = {};
if (va == undefined)
if (va == undef)
return t.selectByIndex(-1);
// Is string or number make function selector
@ -10638,7 +11483,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
showMenu : function() {
var t = this, p2, e = DOM.get(this.id), m;
if (t.isDisabled() || t.items.length == 0)
if (t.isDisabled() || t.items.length === 0)
return;
if (t.menu && t.menu.isMenuVisible)
@ -10705,7 +11550,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
menu_line : 1,
'class' : t.classPrefix + 'Menu mceNoIcons',
max_width : 150,
max_width : 250,
max_height : 150
});
@ -10725,7 +11570,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
each(t.items, function(o) {
// No value then treat it as a title
if (o.value === undefined) {
if (o.value === undef) {
m.add({
title : o.title,
role : "option",
@ -10815,7 +11660,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
})(tinymce);
(function(tinymce) {
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
NativeListBox : function(id, s) {
@ -10835,7 +11680,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
select : function(va) {
var t = this, fv, f;
if (va == undefined)
if (va == undef)
return t.selectByIndex(-1);
// Is string or number make function selector
@ -10988,7 +11833,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
m.settings.vp_offset_x = p2.x;
m.settings.vp_offset_y = p2.y;
m.settings.keyboard_focus = t._focused;
m.showMenu(0, e.clientHeight);
m.showMenu(0, e.firstChild.clientHeight);
Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
t.setState('Selected', 1);
@ -11172,7 +12017,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
p2 = DOM.getPos(e);
DOM.setStyles(t.id + '_menu', {
left : p2.x,
top : p2.y + e.clientHeight,
top : p2.y + e.firstChild.clientHeight,
zIndex : 200000
});
e = 0;
@ -11215,7 +12060,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
renderMenu : function() {
var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;
w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s.menu_class + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
DOM.add(m, 'span', {'class' : 'mceMenuLine'});
@ -11244,7 +12089,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
// adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.
if (!tinymce.isIE ) {
settings['role']= 'option';
settings.role = 'option';
}
n = DOM.add(n, 'a', settings);
@ -11540,7 +12385,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (typeof u === "object")
url = u.prefix + u.resource + u.suffix;
if (url.indexOf('/') != 0 && url.indexOf('://') == -1)
if (url.indexOf('/') !== 0 && url.indexOf('://') == -1)
url = tinymce.baseURL + '/' + url;
t.urls[n] = url.substring(0, url.lastIndexOf('/'));
@ -11564,7 +12409,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
DOM = tinymce.DOM, Event = tinymce.dom.Event,
ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
explode = tinymce.explode,
Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;
Dispatcher = tinymce.util.Dispatcher, undef, instanceCounter = 0;
// Setup some URLs where the editor API is located and where the document is
tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
@ -11634,6 +12479,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
};
function hasClass(n, c) {
return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
};
s = extend({
theme : "simple",
language : "en"
@ -11677,10 +12526,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
case "textareas":
case "specific_textareas":
function hasClass(n, c) {
return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
};
each(DOM.select('textarea'), function(elm) {
if (s.editor_deselector && hasClass(elm, s.editor_deselector))
return;
@ -11741,7 +12586,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
get : function(id) {
if (id === undefined)
if (id === undef)
return this.editors;
return this.editors[id];
@ -11794,6 +12639,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
execCommand : function(c, u, v) {
var t = this, ed = t.get(v), w;
function clr() {
ed.destroy();
w.detachEvent('onunload', clr);
w = w.tinyMCE = w.tinymce = null; // IE leak
};
// Manager commands
switch (c) {
case "mceFocus":
@ -11822,12 +12673,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Fix IE memory leaks
if (tinymce.isIE) {
function clr() {
ed.destroy();
w.detachEvent('onunload', clr);
w = w.tinyMCE = w.tinymce = null; // IE leak
};
w.attachEvent('onunload', clr);
}
@ -11910,108 +12755,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
(function(tinymce) {
// Shorten these names
var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,
Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,
each = tinymce.each, isGecko = tinymce.isGecko,
isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode, VK = tinymce.VK;
explode = tinymce.explode;
tinymce.create('tinymce.Editor', {
Editor : function(id, s) {
var t = this;
Editor : function(id, settings) {
var self = this, TRUE = true;
t.id = t.editorId = id;
t.execCommands = {};
t.queryStateCommands = {};
t.queryValueCommands = {};
t.isNotDirty = false;
t.plugins = {};
// Add events to the editor
each([
'onPreInit',
'onBeforeRenderUI',
'onPostRender',
'onLoad',
'onInit',
'onRemove',
'onActivate',
'onDeactivate',
'onClick',
'onEvent',
'onMouseUp',
'onMouseDown',
'onDblClick',
'onKeyDown',
'onKeyUp',
'onKeyPress',
'onContextMenu',
'onSubmit',
'onReset',
'onPaste',
'onPreProcess',
'onPostProcess',
'onBeforeSetContent',
'onBeforeGetContent',
'onSetContent',
'onGetContent',
'onLoadContent',
'onSaveContent',
'onNodeChange',
'onChange',
'onBeforeExecCommand',
'onExecCommand',
'onUndo',
'onRedo',
'onVisualAid',
'onSetProgressState',
'onSetAttrib'
], function(e) {
t[e] = new Dispatcher(t);
});
t.settings = s = extend({
self.settings = settings = extend({
id : id,
language : 'en',
docs_language : 'en',
theme : 'simple',
skin : 'default',
delta_width : 0,
@ -12019,58 +12774,63 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
popup_css : '',
plugins : '',
document_base_url : tinymce.documentBaseURL,
add_form_submit_trigger : 1,
submit_patch : 1,
add_unload_trigger : 1,
convert_urls : 1,
relative_urls : 1,
remove_script_host : 1,
table_inline_editing : 0,
object_resizing : 1,
cleanup : 1,
accessibility_focus : 1,
custom_shortcuts : 1,
custom_undo_redo_keyboard_shortcuts : 1,
custom_undo_redo_restore_selection : 1,
custom_undo_redo : 1,
add_form_submit_trigger : TRUE,
submit_patch : TRUE,
add_unload_trigger : TRUE,
convert_urls : TRUE,
relative_urls : TRUE,
remove_script_host : TRUE,
table_inline_editing : false,
object_resizing : TRUE,
accessibility_focus : TRUE,
doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll
visual_table_class : 'mceItemTable',
visual : 1,
visual : TRUE,
font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
apply_source_formatting : 1,
apply_source_formatting : TRUE,
directionality : 'ltr',
forced_root_block : 'p',
hidden_input : 1,
padd_empty_editor : 1,
render_ui : 1,
init_theme : 1,
force_p_newlines : 1,
hidden_input : TRUE,
padd_empty_editor : TRUE,
render_ui : TRUE,
indentation : '30px',
keep_styles : 1,
fix_table_elements : 1,
inline_styles : 1,
convert_fonts_to_spans : true,
fix_table_elements : TRUE,
inline_styles : TRUE,
convert_fonts_to_spans : TRUE,
indent : 'simple',
indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside',
indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside',
validate : true,
indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure',
indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure',
validate : TRUE,
entity_encoding : 'named',
url_converter : t.convertURL,
url_converter_scope : t,
ie7_compat : true
}, s);
url_converter : self.convertURL,
url_converter_scope : self,
ie7_compat : TRUE
}, settings);
t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
self.id = self.editorId = id;
self.isNotDirty = false;
self.plugins = {};
self.documentBaseURI = new tinymce.util.URI(settings.document_base_url || tinymce.documentBaseURL, {
base_uri : tinyMCE.baseURI
});
t.baseURI = tinymce.baseURI;
self.baseURI = tinymce.baseURI;
t.contentCSS = [];
self.contentCSS = [];
// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
self.setupEvents();
// Internal command handler objects
self.execCommands = {};
self.queryStateCommands = {};
self.queryValueCommands = {};
// Call setup
t.execCallback('setup', t);
self.execCallback('setup', self);
},
render : function(nst) {
@ -12091,8 +12851,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return;
// Is a iPad/iPhone and not on iOS5, 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
// here since the browser says it has contentEditable support but there is no visible caret.
if (tinymce.isIDevice && !tinymce.isIOS5)
return;
@ -12171,9 +12930,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var dependencies = PluginManager.dependencies(p);
each(dependencies, function(dep) {
var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};
var dep = PluginManager.createUrl(defaultSettings, dep);
dep = PluginManager.createUrl(defaultSettings, dep);
PluginManager.load(dep.resource, dep);
});
} else {
// Skip safari plugin, since it is removed as of 3.3b1
@ -12207,7 +12965,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
o = ThemeManager.get(s.theme);
t.theme = new o();
if (t.theme.init && s.init_theme)
if (t.theme.init)
t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
}
function initPlugin(p) {
@ -12243,51 +13001,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.controlManager = new tinymce.ControlManager(t);
if (s.custom_undo_redo) {
t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {
if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
t.undoManager.beforeChange();
});
t.onExecCommand.add(function(ed, cmd, ui, val, a) {
if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
t.undoManager.add();
});
}
t.onExecCommand.add(function(ed, c) {
// Don't refresh the select lists until caret move
if (!/^(FontName|FontSize)$/.test(c))
t.nodeChanged();
});
// Remove ghost selections on images and tables in Gecko
if (isGecko) {
function repaint(a, o) {
if (!o || !o.initial)
t.execCommand('mceRepaint');
};
t.onUndo.add(repaint);
t.onRedo.add(repaint);
t.onSetContent.add(repaint);
}
// Enables users to override the control factory
t.onBeforeRenderUI.dispatch(t, t.controlManager);
// Measure box
if (s.render_ui) {
if (s.render_ui && t.theme) {
w = s.width || e.style.width || e.offsetWidth;
h = s.height || e.style.height || e.offsetHeight;
t.orgDisplay = e.style.display;
re = /^[0-9\.]+(|px)$/i;
if (re.test('' + w))
w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
if (re.test('' + h))
h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), 100);
// Render UI
o = t.theme.renderUI({
@ -12301,6 +13035,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.editorContainer = o.editorContainer;
}
// Load specified content CSS last
if (s.content_css) {
each(explode(s.content_css), function(u) {
t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
});
}
// Content editable mode ends here
if (s.content_editable) {
e = n = o = null; // Fix IE leak
return t.initContentBody();
}
// User specified a document.domain value
if (document.domain && location.hostname != document.domain)
@ -12312,13 +13058,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
height : h
});
// Load specified content CSS last
if (s.content_css) {
tinymce.each(explode(s.content_css), function(u) {
t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
});
}
h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
if (h < 100)
h = 100;
@ -12362,7 +13101,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Domain relaxing enabled, then set document domain
if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
// We need to write the contents here in IE since multiple writes messes up refresh button and back button
u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';
u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()';
}
// Create iframe
@ -12386,73 +13125,61 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
DOM.setAttrib(t.id, 'aria-hidden', true);
if (!tinymce.relaxedDomain || !u)
t.setupIframe();
t.initContentBody();
e = n = o = null; // Cleanup
},
setupIframe : function() {
var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
initContentBody : function() {
var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body;
// Setup iframe body
if (!isIE || !tinymce.relaxedDomain) {
d.open();
d.write(t.iframeHTML);
d.close();
if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {
doc.open();
doc.write(self.iframeHTML);
doc.close();
if (tinymce.relaxedDomain)
d.domain = tinymce.relaxedDomain;
doc.domain = tinymce.relaxedDomain;
}
if (settings.content_editable) {
DOM.addClass(targetElm, 'mceContentBody');
self.contentDocument = doc = settings.content_document || document;
self.contentWindow = settings.content_window || window;
self.bodyElement = targetElm;
// Prevent leak in IE
settings.content_document = settings.content_window = null;
}
// It will not steal focus while setting contentEditable
b = t.getBody();
b.disabled = true;
body = self.getBody();
body.disabled = true;
if (!s.readonly)
b.contentEditable = true;
if (!settings.readonly)
body.contentEditable = self.getParam('content_editable_state', true);
b.disabled = false;
body.disabled = false;
t.schema = new tinymce.html.Schema(s);
self.schema = new tinymce.html.Schema(settings);
t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
self.dom = new tinymce.dom.DOMUtils(doc, {
keep_values : true,
url_converter : t.convertURL,
url_converter_scope : t,
hex_colors : s.force_hex_style_colors,
class_filter : s.class_filter,
update_styles : 1,
fix_ie_paragraphs : 1,
schema : t.schema
url_converter : self.convertURL,
url_converter_scope : self,
hex_colors : settings.force_hex_style_colors,
class_filter : settings.class_filter,
update_styles : true,
root_element : settings.content_editable ? self.id : null,
schema : self.schema
});
t.parser = new tinymce.html.DomParser(s, t.schema);
// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
if (!t.settings.allow_html_in_named_anchor) {
t.parser.addAttributeFilter('name', function(nodes, name) {
var i = nodes.length, sibling, prevSibling, parent, node;
while (i--) {
node = nodes[i];
if (node.name === 'a' && node.firstChild) {
parent = node.parent;
// Move children after current node
sibling = node.lastChild;
do {
prevSibling = sibling.prev;
parent.insert(sibling, node);
sibling = prevSibling;
} while (sibling);
}
}
});
}
self.parser = new tinymce.html.DomParser(settings, self.schema);
// Convert src and href into data-mce-src, data-mce-href and data-mce-style
t.parser.addAttributeFilter('src,href,style', function(nodes, name) {
var i = nodes.length, node, dom = t.dom, value, internalName;
self.parser.addAttributeFilter('src,href,style', function(nodes, name) {
var i = nodes.length, node, dom = self.dom, value, internalName;
while (i--) {
node = nodes[i];
@ -12464,13 +13191,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (name === "style")
node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
else
node.attr(internalName, t.convertURL(value, name, node.name));
node.attr(internalName, self.convertURL(value, name, node.name));
}
}
});
// Keep scripts from executing
t.parser.addNodeFilter('script', function(nodes, name) {
self.parser.addNodeFilter('script', function(nodes, name) {
var i = nodes.length, node;
while (i--) {
@ -12479,7 +13206,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
t.parser.addNodeFilter('#cdata', function(nodes, name) {
self.parser.addNodeFilter('#cdata', function(nodes, name) {
var i = nodes.length, node;
while (i--) {
@ -12490,8 +13217,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();
self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
while (i--) {
node = nodes[i];
@ -12501,306 +13228,89 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);
self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);
t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer);
t.formatter = new tinymce.Formatter(this);
self.formatter = new tinymce.Formatter(self);
// Register default formats
t.formatter.register({
alignleft : [
{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},
{selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
],
self.undoManager = new tinymce.UndoManager(self);
aligncenter : [
{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},
{selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
{selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
],
alignright : [
{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},
{selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
],
alignfull : [
{selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}
],
bold : [
{inline : 'strong', remove : 'all'},
{inline : 'span', styles : {fontWeight : 'bold'}},
{inline : 'b', remove : 'all'}
],
italic : [
{inline : 'em', remove : 'all'},
{inline : 'span', styles : {fontStyle : 'italic'}},
{inline : 'i', remove : 'all'}
],
underline : [
{inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
{inline : 'u', remove : 'all'}
],
strikethrough : [
{inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
{inline : 'strike', remove : 'all'}
],
forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
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'},
subscript : {inline : 'sub'},
superscript : {inline : 'sup'},
link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
onmatch : function(node) {
return true;
},
onformat : function(elm, fmt, vars) {
each(vars, function(value, key) {
t.dom.setAttrib(elm, key, value);
});
}
},
removeformat : [
{selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
{selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
{selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
]
});
// Register default block formats
each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
t.formatter.register(name, {block : name, remove : 'all'});
});
// Register user defined formats
t.formatter.register(t.settings.formats);
t.undoManager = new tinymce.UndoManager(t);
self.forceBlocks = new tinymce.ForceBlocks(self);
self.enterKey = new tinymce.EnterKey(self);
self.editorCommands = new tinymce.EditorCommands(self);
// Pass through
t.undoManager.onAdd.add(function(um, l) {
if (um.hasUndo())
return t.onChange.dispatch(t, l, um);
self.serializer.onPreProcess.add(function(se, o) {
return self.onPreProcess.dispatch(self, o, se);
});
t.undoManager.onUndo.add(function(um, l) {
return t.onUndo.dispatch(t, l, um);
self.serializer.onPostProcess.add(function(se, o) {
return self.onPostProcess.dispatch(self, o, se);
});
t.undoManager.onRedo.add(function(um, l) {
return t.onRedo.dispatch(t, l, um);
});
self.onPreInit.dispatch(self);
t.forceBlocks = new tinymce.ForceBlocks(t);
t.enterKey = new tinymce.EnterKey(t);
if (!settings.gecko_spellcheck)
doc.body.spellcheck = false;
t.editorCommands = new tinymce.EditorCommands(t);
// Pass through
t.serializer.onPreProcess.add(function(se, o) {
return t.onPreProcess.dispatch(t, o, se);
});
t.serializer.onPostProcess.add(function(se, o) {
return t.onPostProcess.dispatch(t, o, se);
});
t.onPreInit.dispatch(t);
if (!s.gecko_spellcheck)
t.getBody().spellcheck = 0;
if (!s.readonly)
t._addEvents();
t.controlManager.onPostRender.dispatch(t, t.controlManager);
t.onPostRender.dispatch(t);
t.quirks = new tinymce.util.Quirks(this);
if (s.directionality)
t.getBody().dir = s.directionality;
if (s.nowrap)
t.getBody().style.whiteSpace = "nowrap";
if (s.handle_node_change_callback) {
t.onNodeChange.add(function(ed, cm, n) {
t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
});
if (!settings.readonly) {
self.bindNativeEvents();
}
if (s.save_callback) {
t.onSaveContent.add(function(ed, o) {
var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
self.controlManager.onPostRender.dispatch(self, self.controlManager);
self.onPostRender.dispatch(self);
if (h)
o.content = h;
});
}
self.quirks = tinymce.util.Quirks(self);
if (s.onchange_callback) {
t.onChange.add(function(ed, l) {
t.execCallback('onchange_callback', t, l);
});
}
if (settings.directionality)
body.dir = settings.directionality;
if (s.protect) {
t.onBeforeSetContent.add(function(ed, o) {
if (s.protect) {
each(s.protect, function(pattern) {
if (settings.nowrap)
body.style.whiteSpace = "nowrap";
if (settings.protect) {
self.onBeforeSetContent.add(function(ed, o) {
each(settings.protect, function(pattern) {
o.content = o.content.replace(pattern, function(str) {
return '<!--mce:protected ' + escape(str) + '-->';
});
});
}
});
}
if (s.convert_newlines_to_brs) {
t.onBeforeSetContent.add(function(ed, o) {
if (o.initial)
o.content = o.content.replace(/\r?\n/g, '<br />');
});
}
if (s.preformatted) {
t.onPostProcess.add(function(ed, o) {
o.content = o.content.replace(/^\s*<pre.*?>/, '');
o.content = o.content.replace(/<\/pre>\s*$/, '');
if (o.set)
o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
});
}
if (s.verify_css_classes) {
t.serializer.attribValueFilter = function(n, v) {
var s, cl;
if (n == 'class') {
// Build regexp for classes
if (!t.classesRE) {
cl = t.dom.getClasses();
if (cl.length > 0) {
s = '';
each (cl, function(o) {
s += (s ? '|' : '') + o['class'];
});
t.classesRE = new RegExp('(' + s + ')', 'gi');
}
}
return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
}
return v;
};
}
if (s.cleanup_callback) {
t.onBeforeSetContent.add(function(ed, o) {
o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
});
t.onPreProcess.add(function(ed, o) {
if (o.set)
t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
if (o.get)
t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
});
t.onPostProcess.add(function(ed, o) {
if (o.set)
o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
if (o.get)
o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
});
}
if (s.save_callback) {
t.onGetContent.add(function(ed, o) {
if (o.save)
o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
});
}
if (s.handle_event_callback) {
t.onEvent.add(function(ed, e, o) {
if (t.execCallback('handle_event_callback', e, ed, o) === false)
Event.cancel(e);
});
}
// Add visual aids when new contents is added
t.onSetContent.add(function() {
t.addVisual(t.getBody());
self.onSetContent.add(function() {
self.addVisual(self.getBody());
});
// Remove empty contents
if (s.padd_empty_editor) {
t.onPostProcess.add(function(ed, o) {
if (settings.padd_empty_editor) {
self.onPostProcess.add(function(ed, o) {
o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
});
}
if (isGecko) {
// Fix gecko link bug, when a link is placed at the end of block elements there is
// no way to move the caret behind the link. This fix adds a bogus br element after the link
function fixLinks(ed, o) {
each(ed.dom.select('a'), function(n) {
var pn = n.parentNode;
self.load({initial : true, format : 'html'});
self.startContent = self.getContent({format : 'raw'});
if (ed.dom.isBlock(pn) && pn.lastChild === n)
ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});
});
};
self.initialized = true;
t.onExecCommand.add(function(ed, cmd) {
if (cmd === 'CreateLink')
fixLinks(ed);
});
t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
}
t.load({initial : true, format : 'html'});
t.startContent = t.getContent({format : 'raw'});
t.undoManager.add();
t.initialized = true;
t.onInit.dispatch(t);
t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
t.execCallback('init_instance_callback', t);
t.focus(true);
t.nodeChanged({initial : 1});
self.onInit.dispatch(self);
self.execCallback('setupcontent_callback', self.id, body, doc);
self.execCallback('init_instance_callback', self);
self.focus(true);
self.nodeChanged({initial : true});
// Load specified content CSS last
each(t.contentCSS, function(u) {
t.dom.loadCSS(u);
each(self.contentCSS, function(url) {
self.dom.loadCSS(url);
});
// Handle auto focus
if (s.auto_focus) {
if (settings.auto_focus) {
setTimeout(function () {
var ed = tinymce.get(s.auto_focus);
var ed = tinymce.get(settings.auto_focus);
ed.selection.select(ed.getBody(), 1);
ed.selection.collapse(1);
@ -12809,29 +13319,41 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}, 100);
}
e = null;
// Clean up references for IE
targetElm = doc = body = null;
},
focus : function(skip_focus) {
var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;
focus : function(sf) {
var oed, t = this, selection = t.selection, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
if (!sf) {
if (!skip_focus) {
// Get selected control element
ieRng = selection.getRng();
if (ieRng.item) {
controlElm = ieRng.item(0);
}
t._refreshContentEditable();
self._refreshContentEditable();
// Is not content editable
if (!ce)
t.getWin().focus();
// Focus the window iframe
if (!contentEditable) {
self.getWin().focus();
}
// Focus the body as well since it's contentEditable
if (tinymce.isGecko) {
t.getBody().focus();
if (tinymce.isGecko || contentEditable) {
body = self.getBody();
// Check for setActive since it doesn't scroll to the element
if (body.setActive) {
body.setActive();
} else {
body.focus();
}
if (contentEditable) {
selection.normalize();
}
}
// Restore selected control element
@ -12842,17 +13364,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
ieRng.addElement(controlElm);
ieRng.select();
}
}
if (tinymce.activeEditor != t) {
if (tinymce.activeEditor != self) {
if ((oed = tinymce.activeEditor) != null)
oed.onDeactivate.dispatch(oed, t);
oed.onDeactivate.dispatch(oed, self);
t.onActivate.dispatch(t, oed);
self.onActivate.dispatch(self, oed);
}
tinymce._setActive(t);
tinymce._setActive(self);
},
execCallback : function(n) {
@ -12884,7 +13405,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!s)
return '';
return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
return i18n[c + '.' + s] || s.replace(/\{\#([^\}]+)\}/g, function(a, b) {
return i18n[c + '.' + b] || '{#' + b + '}';
});
},
@ -12918,37 +13439,40 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
nodeChanged : function(o) {
var t = this, s = t.selection, n = s.getStart() || t.getBody();
var self = this, selection = self.selection, node;
// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
if (t.initialized) {
if (self.initialized) {
o = o || {};
n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state
// Get start node
node = selection.getStart() || self.getBody();
node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
// Get parents and add them to object
o.parents = [];
t.dom.getParent(n, function(node) {
self.dom.getParent(node, function(node) {
if (node.nodeName == 'BODY')
return true;
o.parents.push(node);
});
t.onNodeChange.dispatch(
t,
o ? o.controlManager || t.controlManager : t.controlManager,
n,
s.isCollapsed(),
self.onNodeChange.dispatch(
self,
o ? o.controlManager || self.controlManager : self.controlManager,
node,
selection.isCollapsed(),
o
);
}
},
addButton : function(n, s) {
var t = this;
addButton : function(name, settings) {
var self = this;
t.buttons = t.buttons || {};
t.buttons[n] = s;
self.buttons = self.buttons || {};
self.buttons[name] = settings;
},
addCommand : function(name, callback, scope) {
@ -12966,7 +13490,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
addShortcut : function(pa, desc, cmd_func, sc) {
var t = this, c;
if (!t.settings.custom_shortcuts)
if (t.settings.custom_shortcuts === false)
return false;
t.shortcuts = t.shortcuts || {};
@ -12991,7 +13515,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var o = {
func : cmd_func,
scope : sc || this,
desc : desc,
desc : t.translate(desc),
alt : false,
ctrl : false,
shift : false
@ -13133,24 +13657,24 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
show : function() {
var t = this;
var self = this;
DOM.show(t.getContainer());
DOM.hide(t.id);
t.load();
DOM.show(self.getContainer());
DOM.hide(self.id);
self.load();
},
hide : function() {
var t = this, d = t.getDoc();
var self = this, doc = self.getDoc();
// Fixed bug where IE has a blinking cursor left from the editor
if (isIE && d)
d.execCommand('SelectAll');
if (isIE && doc)
doc.execCommand('SelectAll');
// We must save before we hide so Safari doesn't crash
t.save();
DOM.hide(t.getContainer());
DOM.setStyle(t.id, 'display', t.orgDisplay);
self.save();
DOM.hide(self.getContainer());
DOM.setStyle(self.id, 'display', self.orgDisplay);
},
isHidden : function() {
@ -13192,12 +13716,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
o = o || {};
o.save = true;
// Add undo level will trigger onchange event
if (!o.no_events) {
t.undoManager.typing = false;
t.undoManager.add();
}
o.element = e;
h = o.content = t.getContent(o);
@ -13283,6 +13801,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
args = args || {};
args.format = args.format || 'html';
args.get = true;
args.getInner = true;
// Do preprocessing
if (!args.no_events)
@ -13310,12 +13829,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
getContainer : function() {
var t = this;
var self = this;
if (!t.container)
t.container = DOM.get(t.editorContainer || t.id + '_parent');
if (!self.container)
self.container = DOM.get(self.editorContainer || self.id + '_parent');
return t.container;
return self.container;
},
getContentAreaContainer : function() {
@ -13327,125 +13846,125 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
getWin : function() {
var t = this, e;
var self = this, elm;
if (!t.contentWindow) {
e = DOM.get(t.id + "_ifr");
if (!self.contentWindow) {
elm = DOM.get(self.id + "_ifr");
if (e)
t.contentWindow = e.contentWindow;
if (elm)
self.contentWindow = elm.contentWindow;
}
return t.contentWindow;
return self.contentWindow;
},
getDoc : function() {
var t = this, w;
var self = this, win;
if (!t.contentDocument) {
w = t.getWin();
if (!self.contentDocument) {
win = self.getWin();
if (w)
t.contentDocument = w.document;
if (win)
self.contentDocument = win.document;
}
return t.contentDocument;
return self.contentDocument;
},
getBody : function() {
return this.bodyElement || this.getDoc().body;
},
convertURL : function(u, n, e) {
var t = this, s = t.settings;
convertURL : function(url, name, elm) {
var self = this, settings = self.settings;
// Use callback instead
if (s.urlconverter_callback)
return t.execCallback('urlconverter_callback', u, e, true, n);
if (settings.urlconverter_callback)
return self.execCallback('urlconverter_callback', url, elm, true, name);
// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
return u;
if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0)
return url;
// Convert to relative
if (s.relative_urls)
return t.documentBaseURI.toRelative(u);
if (settings.relative_urls)
return self.documentBaseURI.toRelative(url);
// Convert to absolute
u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
return u;
return url;
},
addVisual : function(e) {
var t = this, s = t.settings;
addVisual : function(elm) {
var self = this, settings = self.settings, dom = self.dom, cls;
e = e || t.getBody();
elm = elm || self.getBody();
if (!is(t.hasVisual))
t.hasVisual = s.visual;
if (!is(self.hasVisual))
self.hasVisual = settings.visual;
each(t.dom.select('table,a', e), function(e) {
var v;
each(dom.select('table,a', elm), function(elm) {
var value;
switch (e.nodeName) {
switch (elm.nodeName) {
case 'TABLE':
v = t.dom.getAttrib(e, 'border');
cls = settings.visual_table_class || 'mceItemTable';
value = dom.getAttrib(elm, 'border');
if (!v || v == '0') {
if (t.hasVisual)
t.dom.addClass(e, s.visual_table_class);
if (!value || value == '0') {
if (self.hasVisual)
dom.addClass(elm, cls);
else
t.dom.removeClass(e, s.visual_table_class);
dom.removeClass(elm, cls);
}
return;
case 'A':
v = t.dom.getAttrib(e, 'name');
value = dom.getAttrib(elm, 'name');
cls = 'mceItemAnchor';
if (v) {
if (t.hasVisual)
t.dom.addClass(e, 'mceItemAnchor');
if (value) {
if (self.hasVisual)
dom.addClass(elm, cls);
else
t.dom.removeClass(e, 'mceItemAnchor');
dom.removeClass(elm, cls);
}
return;
}
});
t.onVisualAid.dispatch(t, e, t.hasVisual);
self.onVisualAid.dispatch(self, elm, self.hasVisual);
},
remove : function() {
var t = this, e = t.getContainer();
var self = this, elm = self.getContainer();
if (!t.removed) {
t.removed = 1; // Cancels post remove event execution
t.hide();
// Remove all events
if (!self.removed) {
self.removed = 1; // Cancels post remove event execution
self.hide();
// Don't clear the window or document if content editable
// is enabled since other instances might still be present
if (!t.settings.content_editable) {
Event.clear(t.getWin());
Event.clear(t.getDoc());
if (!self.settings.content_editable) {
Event.clear(self.getWin());
Event.clear(self.getDoc());
}
Event.clear(t.getBody());
Event.clear(t.formElement);
Event.unbind(e);
Event.clear(self.getBody());
Event.clear(self.formElement);
Event.unbind(elm);
t.execCallback('remove_instance_callback', t);
t.onRemove.dispatch(t);
self.execCallback('remove_instance_callback', self);
self.onRemove.dispatch(self);
// Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
t.onExecCommand.listeners = [];
self.onExecCommand.listeners = [];
tinymce.remove(t);
DOM.remove(e);
tinymce.remove(self);
DOM.remove(elm);
}
},
@ -13492,343 +14011,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Internal functions
_addEvents : function() {
// 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
var t = this, i, s = t.settings, dom = t.dom, lo = {
mouseup : 'onMouseUp',
mousedown : 'onMouseDown',
click : 'onClick',
keyup : 'onKeyUp',
keydown : 'onKeyDown',
keypress : 'onKeyPress',
submit : 'onSubmit',
reset : 'onReset',
contextmenu : 'onContextMenu',
dblclick : 'onDblClick',
paste : 'onPaste' // Doesn't work in all browsers yet
};
function eventHandler(e, o) {
var ty = e.type;
// Don't fire events when it's removed
if (t.removed)
return;
// Generic event handler
if (t.onEvent.dispatch(t, e, o) !== false) {
// Specific event handler
t[lo[e.fakeType || e.type]].dispatch(t, e, o);
}
};
// Add DOM events
each(lo, function(v, k) {
switch (k) {
case 'contextmenu':
dom.bind(t.getDoc(), k, eventHandler);
break;
case 'paste':
dom.bind(t.getBody(), k, function(e) {
eventHandler(e);
});
break;
case 'submit':
case 'reset':
dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
break;
default:
dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
}
});
dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
t.focus(true);
});
// Fixes bug where a specified document_base_uri could result in broken images
// This will also fix drag drop of images in Gecko
if (tinymce.isGecko) {
dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
var v;
e = e.target;
if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))
e.src = t.documentBaseURI.toAbsolute(v);
});
}
// Set various midas options in Gecko
if (isGecko) {
function setOpts() {
var t = this, d = t.getDoc(), s = t.settings;
if (isGecko && !s.readonly) {
t._refreshContentEditable();
try {
// Try new Gecko method
d.execCommand("styleWithCSS", 0, false);
} catch (ex) {
// Use old method
if (!t._isHidden())
try {d.execCommand("useCSS", 0, true);} catch (ex) {}
}
if (!s.table_inline_editing)
try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
if (!s.object_resizing)
try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
}
};
t.onBeforeExecCommand.add(setOpts);
t.onMouseDown.add(setOpts);
}
// Add node change handlers
t.onMouseUp.add(t.nodeChanged);
//t.onClick.add(t.nodeChanged);
t.onKeyUp.add(function(ed, e) {
var c = e.keyCode;
if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)
t.nodeChanged();
});
// Add block quote deletion handler
t.onKeyDown.add(function(ed, e) {
if (e.keyCode != VK.BACKSPACE)
return;
var rng = ed.selection.getRng();
if (!rng.collapsed)
return;
var n = rng.startContainer;
var offset = rng.startOffset;
while (n && n.nodeType && n.nodeType != 1 && n.parentNode)
n = n.parentNode;
// Is the cursor at the beginning of a blockquote?
if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {
// Remove the blockquote
ed.formatter.toggle('blockquote', null, n.parentNode);
// Move the caret to the beginning of n
rng.setStart(n, 0);
rng.setEnd(n, 0);
ed.selection.setRng(rng);
ed.selection.collapse(false);
}
});
// Add reset handler
t.onReset.add(function() {
t.setContent(t.startContent, {format : 'raw'});
});
// Add shortcuts
if (s.custom_shortcuts) {
if (s.custom_undo_redo_keyboard_shortcuts) {
t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
}
// Add default shortcuts for gecko
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++)
t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
t.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
t.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
t.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
function find(e) {
var v = null;
if (!e.altKey && !e.ctrlKey && !e.metaKey)
return v;
each(t.shortcuts, function(o) {
if (tinymce.isMac && o.ctrl != e.metaKey)
return;
else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
return;
if (o.alt != e.altKey)
return;
if (o.shift != e.shiftKey)
return;
if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
v = o;
return false;
}
});
return v;
};
t.onKeyUp.add(function(ed, e) {
var o = find(e);
if (o)
return Event.cancel(e);
});
t.onKeyPress.add(function(ed, e) {
var o = find(e);
if (o)
return Event.cancel(e);
});
t.onKeyDown.add(function(ed, e) {
var o = find(e);
if (o) {
o.func.call(o.scope);
return Event.cancel(e);
}
});
}
if (tinymce.isIE) {
// Fix so resize will only update the width and height attributes not the styles of an image
// It will also block mceItemNoResize items
dom.bind(t.getDoc(), 'controlselect', function(e) {
var re = t.resizeInfo, cb;
e = e.target;
// Don't do this action for non image elements
if (e.nodeName !== 'IMG')
return;
if (re)
dom.unbind(re.node, re.ev, re.cb);
if (!dom.hasClass(e, 'mceItemNoResize')) {
ev = 'resizeend';
cb = dom.bind(e, ev, function(e) {
var v;
e = e.target;
if (v = dom.getStyle(e, 'width')) {
dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
dom.setStyle(e, 'width', '');
}
if (v = dom.getStyle(e, 'height')) {
dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
dom.setStyle(e, 'height', '');
}
});
} else {
ev = 'resizestart';
cb = dom.bind(e, 'resizestart', Event.cancel, Event);
}
re = t.resizeInfo = {
node : e,
ev : ev,
cb : cb
};
});
}
if (tinymce.isOpera) {
t.onClick.add(function(ed, e) {
Event.prevent(e);
});
}
// Add custom undo/redo handlers
if (s.custom_undo_redo) {
function addUndo() {
t.undoManager.typing = false;
t.undoManager.add();
};
var focusLostFunc = tinymce.isGecko ? 'blur' : 'focusout';
dom.bind(t.getDoc(), focusLostFunc, function(e){
if (!t.removed && t.undoManager.typing)
addUndo();
});
// Add undo level when contents is drag/dropped within the editor
t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {
addUndo();
});
t.onKeyUp.add(function(ed, e) {
var keyCode = e.keyCode;
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || e.ctrlKey)
addUndo();
});
t.onKeyDown.add(function(ed, e) {
var keyCode = e.keyCode, sel;
if (keyCode == 8) {
sel = t.getDoc().selection;
// Fix IE control + backspace browser bug
if (sel && sel.createRange && sel.createRange().item) {
t.undoManager.beforeChange();
ed.dom.remove(sel.createRange().item(0));
addUndo();
return Event.cancel(e);
}
}
// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {
// Add position before enter key is pressed, used by IE since it still uses the default browser behavior
// Todo: Remove this once we normalize enter behavior on IE
if (tinymce.isIE && keyCode == 13)
t.undoManager.beforeChange();
if (t.undoManager.typing)
addUndo();
return;
}
// If key isn't shift,ctrl,alt,capslock,metakey
if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {
t.undoManager.beforeChange();
t.undoManager.typing = true;
t.undoManager.add();
}
});
t.onMouseDown.add(function() {
if (t.undoManager.typing)
addUndo();
});
}
},
_refreshContentEditable : function() {
var self = this, body, parent;
@ -13852,14 +14034,294 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Weird, wheres that cursor selection?
s = this.selection.getSel();
return (!s || !s.rangeCount || s.rangeCount == 0);
return (!s || !s.rangeCount || s.rangeCount === 0);
}
});
})(tinymce);
(function(tinymce) {
var each = tinymce.each;
tinymce.Editor.prototype.setupEvents = function() {
var self = this, settings = self.settings;
// Add events to the editor
each([
'onPreInit',
'onBeforeRenderUI',
'onPostRender',
'onLoad',
'onInit',
'onRemove',
'onActivate',
'onDeactivate',
'onClick',
'onEvent',
'onMouseUp',
'onMouseDown',
'onDblClick',
'onKeyDown',
'onKeyUp',
'onKeyPress',
'onContextMenu',
'onSubmit',
'onReset',
'onPaste',
'onPreProcess',
'onPostProcess',
'onBeforeSetContent',
'onBeforeGetContent',
'onSetContent',
'onGetContent',
'onLoadContent',
'onSaveContent',
'onNodeChange',
'onChange',
'onBeforeExecCommand',
'onExecCommand',
'onUndo',
'onRedo',
'onVisualAid',
'onSetProgressState',
'onSetAttrib'
], function(name) {
self[name] = new tinymce.util.Dispatcher(self);
});
// Handle legacy cleanup_callback option
if (settings.cleanup_callback) {
self.onBeforeSetContent.add(function(ed, o) {
o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
});
self.onPreProcess.add(function(ed, o) {
if (o.set)
ed.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
if (o.get)
ed.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
});
self.onPostProcess.add(function(ed, o) {
if (o.set)
o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
if (o.get)
o.content = ed.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
});
}
// Handle legacy save_callback option
if (settings.save_callback) {
self.onGetContent.add(function(ed, o) {
if (o.save)
o.content = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
});
}
// Handle legacy handle_event_callback option
if (settings.handle_event_callback) {
self.onEvent.add(function(ed, e, o) {
if (self.execCallback('handle_event_callback', e, ed, o) === false)
Event.cancel(e);
});
}
// Handle legacy handle_node_change_callback option
if (settings.handle_node_change_callback) {
self.onNodeChange.add(function(ed, cm, n) {
ed.execCallback('handle_node_change_callback', ed.id, n, -1, -1, true, ed.selection.isCollapsed());
});
}
// Handle legacy save_callback option
if (settings.save_callback) {
self.onSaveContent.add(function(ed, o) {
var h = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
if (h)
o.content = h;
});
}
// Handle legacy onchange_callback option
if (settings.onchange_callback) {
self.onChange.add(function(ed, l) {
ed.execCallback('onchange_callback', ed, l);
});
}
};
tinymce.Editor.prototype.bindNativeEvents = function() {
// 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
var self = this, i, settings = self.settings, dom = self.dom, nativeToDispatcherMap;
nativeToDispatcherMap = {
mouseup : 'onMouseUp',
mousedown : 'onMouseDown',
click : 'onClick',
keyup : 'onKeyUp',
keydown : 'onKeyDown',
keypress : 'onKeyPress',
submit : 'onSubmit',
reset : 'onReset',
contextmenu : 'onContextMenu',
dblclick : 'onDblClick',
paste : 'onPaste' // Doesn't work in all browsers yet
};
// Handler that takes a native event and sends it out to a dispatcher like onKeyDown
function eventHandler(evt, args) {
var type = evt.type;
// Don't fire events when it's removed
if (self.removed)
return;
// Sends the native event out to a global dispatcher then to the specific event dispatcher
if (self.onEvent.dispatch(self, evt, args) !== false) {
self[nativeToDispatcherMap[evt.fakeType || evt.type]].dispatch(self, evt, args);
}
};
// Opera doesn't support focus event for contentEditable elements so we need to fake it
function doOperaFocus(e) {
self.focus(true);
};
function nodeChanged() {
// Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i>
self.selection.normalize();
self.nodeChanged();
}
// Add DOM events
each(nativeToDispatcherMap, function(dispatcherName, nativeName) {
var root = settings.content_editable ? self.getBody() : self.getDoc();
switch (nativeName) {
case 'contextmenu':
dom.bind(root, nativeName, eventHandler);
break;
case 'paste':
dom.bind(self.getBody(), nativeName, eventHandler);
break;
case 'submit':
case 'reset':
dom.bind(self.getElement().form || tinymce.DOM.getParent(self.id, 'form'), nativeName, eventHandler);
break;
default:
dom.bind(root, nativeName, eventHandler);
}
});
// Set the editor as active when focused
dom.bind(settings.content_editable ? self.getBody() : (tinymce.isGecko ? self.getDoc() : self.getWin()), 'focus', function(e) {
self.focus(true);
});
if (settings.content_editable && tinymce.isOpera) {
dom.bind(self.getBody(), 'click', doOperaFocus);
dom.bind(self.getBody(), 'keydown', doOperaFocus);
}
// Add node change handler
self.onMouseUp.add(nodeChanged);
self.onKeyUp.add(function(ed, e) {
var keyCode = e.keyCode;
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)
nodeChanged();
});
// Add reset handler
self.onReset.add(function() {
self.setContent(self.startContent, {format : 'raw'});
});
// Add shortcuts
function handleShortcut(e, execute) {
if (e.altKey || e.ctrlKey || e.metaKey) {
each(self.shortcuts, function(shortcut) {
var ctrlState = tinymce.isMac ? e.metaKey : e.ctrlKey;
if (shortcut.ctrl != ctrlState || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey)
return;
if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
e.preventDefault();
if (execute) {
shortcut.func.call(shortcut.scope);
}
return true;
}
});
}
};
self.onKeyUp.add(function(ed, e) {
handleShortcut(e);
});
self.onKeyPress.add(function(ed, e) {
handleShortcut(e);
});
self.onKeyDown.add(function(ed, e) {
handleShortcut(e, true);
});
if (tinymce.isOpera) {
self.onClick.add(function(ed, e) {
e.preventDefault();
});
}
};
})(tinymce);
(function(tinymce) {
// Added for compression purposes
var each = tinymce.each, undefined, TRUE = true, FALSE = false;
var each = tinymce.each, undef, TRUE = true, FALSE = false;
tinymce.EditorCommands = function(editor) {
var dom = editor.dom,
@ -13922,10 +14384,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Private methods
function execNativeCommand(command, ui, value) {
if (ui === undefined)
if (ui === undef)
ui = FALSE;
if (value === undefined)
if (value === undef)
value = null;
return editor.getDoc().execCommand(command, ui, value);
@ -13936,7 +14398,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function toggleFormat(name, value) {
formatter.toggle(name, value ? {value : value} : undefined);
formatter.toggle(name, value ? {value : value} : undef);
};
function storeSelection(type) {
@ -14336,9 +14798,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Override justify commands
'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
var name = 'align' + command.substring(7);
// Use Formatter.matchNode instead of Formatter.match so that we don't match on parent node. This fixes bug where for both left
// and right align buttons can be active. This could occur when selected nodes have align right and the parent has align left.
var nodes = selection.isCollapsed() ? [selection.getNode()] : selection.getSelectedBlocks();
var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
var matches = tinymce.map(nodes, function(node) {
return !!formatter.matchNode(node, name);
});
@ -14389,7 +14849,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}, 'value');
// Add undo manager logic
if (settings.custom_undo_redo) {
addCommands({
Undo : function() {
editor.undoManager.undo();
@ -14399,7 +14858,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
editor.undoManager.redo();
}
});
}
};
})(tinymce);
@ -14407,21 +14865,116 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var Dispatcher = tinymce.util.Dispatcher;
tinymce.UndoManager = function(editor) {
var self, index = 0, data = [], beforeBookmark;
var self, index = 0, data = [], beforeBookmark, onAdd, onUndo, onRedo;
function getContent() {
// Remove whitespace before/after and remove pure bogus nodes
return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));
};
return self = {
function addNonTypingUndoLevel() {
self.typing = false;
self.add();
};
// Create event instances
onAdd = new Dispatcher(self);
onUndo = new Dispatcher(self);
onRedo = new Dispatcher(self);
// Pass though onAdd event from UndoManager to Editor as onChange
onAdd.add(function(undoman, level) {
if (undoman.hasUndo())
return editor.onChange.dispatch(editor, level, undoman);
});
// Pass though onUndo event from UndoManager to Editor
onUndo.add(function(undoman, level) {
return editor.onUndo.dispatch(editor, level, undoman);
});
// Pass though onRedo event from UndoManager to Editor
onRedo.add(function(undoman, level) {
return editor.onRedo.dispatch(editor, level, undoman);
});
// Add initial undo level when the editor is initialized
editor.onInit.add(function() {
self.add();
});
// Get position before an execCommand is processed
editor.onBeforeExecCommand.add(function(ed, cmd, ui, val, args) {
if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
self.beforeChange();
}
});
// Add undo level after an execCommand call was made
editor.onExecCommand.add(function(ed, cmd, ui, val, args) {
if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
self.add();
}
});
// Add undo level on save contents, drag end and blur/focusout
editor.onSaveContent.add(addNonTypingUndoLevel);
editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);
editor.dom.bind(editor.getDoc(), tinymce.isGecko ? 'blur' : 'focusout', function(e) {
if (!editor.removed && self.typing) {
addNonTypingUndoLevel();
}
});
editor.onKeyUp.add(function(editor, e) {
var keyCode = e.keyCode;
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
addNonTypingUndoLevel();
}
});
editor.onKeyDown.add(function(editor, e) {
var keyCode = e.keyCode;
// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
if (self.typing) {
addNonTypingUndoLevel();
}
return;
}
// If key isn't shift,ctrl,alt,capslock,metakey
if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
self.beforeChange();
self.typing = true;
self.add();
}
});
editor.onMouseDown.add(function(editor, e) {
if (self.typing) {
addNonTypingUndoLevel();
}
});
// Add keyboard shortcuts for undo/redo keys
editor.addShortcut('ctrl+z', 'undo_desc', 'Undo');
editor.addShortcut('ctrl+y', 'redo_desc', 'Redo');
self = {
// Explose for debugging reasons
data : data,
typing : false,
onAdd : new Dispatcher(self),
onAdd : onAdd,
onUndo : new Dispatcher(self),
onUndo : onUndo,
onRedo : new Dispatcher(self),
onRedo : onRedo,
beforeChange : function() {
beforeBookmark = editor.selection.getBookmark(2, true);
@ -14518,22 +15071,22 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return index < data.length - 1 && !this.typing;
}
};
return self;
};
})(tinymce);
tinymce.ForceBlocks = function(editor) {
var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();
// Force root blocks
if (settings.forced_root_block) {
function addRootBlocks() {
var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF;
var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped;
if (!node || node.nodeType !== 1 || !settings.forced_root_block)
return;
// Check if node is wrapped in block
while (node != rootNode) {
while (node && node != rootNode) {
if (blockElements[node.nodeName])
return;
@ -14567,11 +15120,13 @@ tinymce.ForceBlocks = function(editor) {
}
// Wrap non block elements and text nodes
for (node = rootNode.firstChild; node; node) {
node = rootNode.firstChild;
while (node) {
if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {
if (!rootBlockNode) {
rootBlockNode = dom.create(settings.forced_root_block);
node.parentNode.insertBefore(rootBlockNode, node);
wrapped = true;
}
tempNode = node;
@ -14603,11 +15158,16 @@ tinymce.ForceBlocks = function(editor) {
}
}
// Only trigger nodeChange when we wrapped nodes to prevent a forever loop
if (wrapped) {
editor.nodeChanged();
}
};
// Force root blocks
if (settings.forced_root_block) {
editor.onKeyUp.add(addRootBlocks);
editor.onClick.add(addRootBlocks);
editor.onNodeChange.add(addRootBlocks);
}
};
@ -14703,6 +15263,8 @@ tinymce.ForceBlocks = function(editor) {
if (v = ed.getParam('skin_variant'))
s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
s['class'] += ed.settings.directionality == "rtl" ? ' mceRtl' : '';
id = t.prefix + id;
cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
c = t.controls[id] = new cls(id, s);
@ -15121,20 +15683,8 @@ tinymce.ForceBlocks = function(editor) {
MCE_ATTR_RE = /^(src|href|style)$/,
FALSE = false,
TRUE = true,
undefined;
// Returns the content editable state of a node
function getContentEditable(node) {
var contentEditable = node.getAttribute("data-mce-contenteditable");
// Check for fake content editable
if (contentEditable && contentEditable !== "inherit") {
return contentEditable;
}
// Check for real content editable
return node.contentEditable !== "inherit" ? node.contentEditable : null;
};
undef,
getContentEditable = dom.getContentEditable;
function isArray(obj) {
return obj instanceof Array;
@ -15148,6 +15698,103 @@ tinymce.ForceBlocks = function(editor) {
return node.nodeType === 1 && node.id === '_mce_caret';
};
function defaultFormats() {
register({
alignleft : [
{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},
{selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
],
aligncenter : [
{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},
{selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
{selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
],
alignright : [
{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},
{selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
],
alignfull : [
{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}
],
bold : [
{inline : 'strong', remove : 'all'},
{inline : 'span', styles : {fontWeight : 'bold'}},
{inline : 'b', remove : 'all'}
],
italic : [
{inline : 'em', remove : 'all'},
{inline : 'span', styles : {fontStyle : 'italic'}},
{inline : 'i', remove : 'all'}
],
underline : [
{inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
{inline : 'u', remove : 'all'}
],
strikethrough : [
{inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
{inline : 'strike', remove : 'all'}
],
forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
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'},
subscript : {inline : 'sub'},
superscript : {inline : 'sup'},
link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
onmatch : function(node) {
return true;
},
onformat : function(elm, fmt, vars) {
each(vars, function(value, key) {
dom.setAttrib(elm, key, value);
});
}
},
removeformat : [
{selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
{selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
{selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
]
});
// Register default block formats
each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
register(name, {block : name, remove : 'all'});
});
// Register user defined formats
register(ed.settings.formats);
};
function addKeyboardShortcuts() {
// Add some inline shortcuts
ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
// BlockFormat shortcuts keys
for (var i = 1; i <= 6; i++) {
ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
}
ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
};
// Public functions
function get(name) {
@ -15167,15 +15814,15 @@ tinymce.ForceBlocks = function(editor) {
each(format, function(format) {
// Set deep to false by default on selector formats this to avoid removing
// alignment on images inside paragraphs when alignment is changed on paragraphs
if (format.deep === undefined)
if (format.deep === undef)
format.deep = !format.selector;
// Default to true
if (format.split === undefined)
if (format.split === undef)
format.split = !format.selector || format.inline;
// Default to true
if (format.remove === undefined && format.selector && !format.inline)
if (format.remove === undef && format.selector && !format.inline)
format.remove = 'none';
// Mark format as a mixed format inline + block level
@ -15248,7 +15895,7 @@ tinymce.ForceBlocks = function(editor) {
function findSelectionEnd(start, end) {
var walker = new TreeWalker(end);
for (node = walker.current(); node; node = walker.prev()) {
if (node.childNodes.length > 1 || node == start) {
if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
return node;
}
}
@ -15260,7 +15907,7 @@ tinymce.ForceBlocks = function(editor) {
var start = rng.startContainer;
var end = rng.endContainer;
if (start != end && rng.endOffset == 0) {
if (start != end && rng.endOffset === 0) {
var newEnd = findSelectionEnd(start, end);
var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
@ -15720,7 +16367,7 @@ tinymce.ForceBlocks = function(editor) {
};
function removeRngStyle(rng) {
var startContainer, endContainer;
var startContainer, endContainer, node;
rng = expandRng(rng, formatList, TRUE);
@ -15729,6 +16376,12 @@ tinymce.ForceBlocks = function(editor) {
endContainer = getContainer(rng);
if (startContainer != endContainer) {
// WebKit will render the table incorrectly if we wrap a TD in a SPAN so lets see if the can use the first child instead
// This will happen if you tripple click a table cell and use remove formatting
if (/^(TR|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
startContainer = (startContainer.nodeName == "TD" ? startContainer.firstChild : startContainer.firstChild.firstChild) || startContainer;
}
// Wrap start/end nodes in span element since these might be cloned/moved
startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});
endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});
@ -15790,20 +16443,12 @@ tinymce.ForceBlocks = function(editor) {
ed.nodeChanged();
} else
performCaretAction('remove', name, vars);
// Removed this logic since it breaks unit tests and produces empty caret elements since they will be destroyed in the cleanup process
// Also there must be a better way to rerender a table and I couldn't reproduce the case causing this might be some old WebKit
/*
// When you remove formatting from a table cell in WebKit (cell, not the contents of a cell) there is a rendering issue with column width
if (tinymce.isWebKit) {
ed.execCommand('mceCleanup');
}*/
};
function toggle(name, vars, node) {
var fmt = get(name);
if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0]['toggle']))
if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle))
remove(name, vars, node);
else
apply(name, vars, node);
@ -15823,7 +16468,7 @@ tinymce.ForceBlocks = function(editor) {
// Check all items
if (items) {
// Non indexed object
if (items.length === undefined) {
if (items.length === undef) {
for (key in items) {
if (items.hasOwnProperty(key)) {
if (item_name === 'attributes')
@ -15961,6 +16606,10 @@ tinymce.ForceBlocks = function(editor) {
canApply : canApply
});
// Initialize
defaultFormats();
addKeyboardShortcuts();
// Private functions
function matchName(node, format) {
@ -16027,15 +16676,15 @@ tinymce.ForceBlocks = function(editor) {
};
function expandRng(rng, format, remove) {
var sibling, lastIdx, leaf,
var sibling, lastIdx, leaf, endPoint,
startContainer = rng.startContainer,
startOffset = rng.startOffset,
endContainer = rng.endContainer,
endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;
endOffset = rng.endOffset;
// This function walks up the tree if there is no siblings before/after the node
function findParentContainer(start) {
var container, parent, child, sibling, siblingName;
var container, parent, child, sibling, siblingName, root;
container = parent = start ? startContainer : endContainer;
siblingName = start ? 'previousSibling' : 'nextSibling';
@ -16075,7 +16724,7 @@ tinymce.ForceBlocks = function(editor) {
// This function walks down the tree to find the leaf at the selection.
// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
function findLeaf(node, offset) {
if (offset === undefined)
if (offset === undef)
offset = node.nodeType === 3 ? node.length : node.childNodes.length;
while (node && node.hasChildNodes()) {
node = node.childNodes[offset];
@ -16118,29 +16767,6 @@ tinymce.ForceBlocks = function(editor) {
return node;
};
// Expand to closest contentEditable element
startContainer = findParentContentEditable(startContainer);
endContainer = findParentContentEditable(endContainer);
// Exclude bookmark nodes if possible
if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
startContainer = startContainer.nextSibling || startContainer;
if (startContainer.nodeType == 3)
startOffset = 0;
}
if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
endContainer = endContainer.previousSibling || endContainer;
if (endContainer.nodeType == 3)
endOffset = endContainer.length;
}
if (format[0].inline) {
if (rng.collapsed) {
function findWordEndPoint(container, offset, start) {
var walker, node, pos, lastTextNode;
@ -16203,63 +16829,12 @@ tinymce.ForceBlocks = function(editor) {
return {container: lastTextNode, offset: offset};
}
}
};
// Expand left to closest word boundery
endPoint = findWordEndPoint(startContainer, startOffset, true);
if (endPoint) {
startContainer = endPoint.container;
startOffset = endPoint.offset;
}
// Expand right to closest word boundery
endPoint = findWordEndPoint(endContainer, endOffset);
if (endPoint) {
endContainer = endPoint.container;
endOffset = endPoint.offset;
}
}
// Avoid applying formatting to a trailing space.
leaf = findLeaf(endContainer, endOffset);
if (leaf.node) {
while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)
leaf = findLeaf(leaf.node.previousSibling);
if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
if (leaf.offset > 1) {
endContainer = leaf.node;
endContainer.splitText(leaf.offset - 1);
} else if (leaf.node.previousSibling) {
// TODO: Figure out why this is in here
//endContainer = leaf.node.previousSibling;
}
}
}
}
// Move start/end point up the tree if the leaves are sharp and if we are in different containers
// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
// This will reduce the number of wrapper elements that needs to be created
// Move start point up the tree
if (format[0].inline || format[0].block_expand) {
if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
startContainer = findParentContainer(true);
}
if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
endContainer = findParentContainer();
}
}
// Expand start/end container to matching selector
if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
function findSelectorEndPoint(container, sibling_name) {
var parents, i, y, curFormat;
if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])
if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name])
container = container[sibling_name];
parents = getParents(container);
@ -16279,13 +16854,6 @@ tinymce.ForceBlocks = function(editor) {
return container;
};
// Find new startContainer/endContainer if there is better one
startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
}
// Expand start/end container to matching block element or text node
if (format[0].block || format[0].selector) {
function findBlockEndPoint(container, sibling_name, sibling_name2) {
var node;
@ -16318,6 +16886,84 @@ tinymce.ForceBlocks = function(editor) {
return node || container;
};
// Expand to closest contentEditable element
startContainer = findParentContentEditable(startContainer);
endContainer = findParentContentEditable(endContainer);
// Exclude bookmark nodes if possible
if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
startContainer = startContainer.nextSibling || startContainer;
if (startContainer.nodeType == 3)
startOffset = 0;
}
if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
endContainer = endContainer.previousSibling || endContainer;
if (endContainer.nodeType == 3)
endOffset = endContainer.length;
}
if (format[0].inline) {
if (rng.collapsed) {
// Expand left to closest word boundery
endPoint = findWordEndPoint(startContainer, startOffset, true);
if (endPoint) {
startContainer = endPoint.container;
startOffset = endPoint.offset;
}
// Expand right to closest word boundery
endPoint = findWordEndPoint(endContainer, endOffset);
if (endPoint) {
endContainer = endPoint.container;
endOffset = endPoint.offset;
}
}
// Avoid applying formatting to a trailing space.
leaf = findLeaf(endContainer, endOffset);
if (leaf.node) {
while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)
leaf = findLeaf(leaf.node.previousSibling);
if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
if (leaf.offset > 1) {
endContainer = leaf.node;
endContainer.splitText(leaf.offset - 1);
}
}
}
}
// Move start/end point up the tree if the leaves are sharp and if we are in different containers
// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
// This will reduce the number of wrapper elements that needs to be created
// Move start point up the tree
if (format[0].inline || format[0].block_expand) {
if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
startContainer = findParentContainer(true);
}
if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
endContainer = findParentContainer();
}
}
// Expand start/end container to matching selector
if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
// Find new startContainer/endContainer if there is better one
startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
}
// Expand start/end container to matching block element or text node
if (format[0].block || format[0].selector) {
// Find new startContainer/endContainer if there is better one
startContainer = findBlockEndPoint(startContainer, 'previousSibling');
endContainer = findBlockEndPoint(endContainer, 'nextSibling');
@ -16454,14 +17100,14 @@ tinymce.ForceBlocks = function(editor) {
function removeNode(node, format) {
var parentNode = node.parentNode, rootBlockElm;
if (format.block) {
if (!forcedRootBlock) {
function find(node, next, inc) {
node = getNonWhiteSpaceSibling(node, next, inc);
return !node || (node.nodeName == 'BR' || isBlock(node));
};
if (format.block) {
if (!forcedRootBlock) {
// Append BR elements if needed before we remove the block
if (isBlock(node) && !isBlock(parentNode)) {
if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))
@ -16541,7 +17187,7 @@ tinymce.ForceBlocks = function(editor) {
value = obj2[name];
// Obj2 doesn't have obj1 item
if (value === undefined)
if (value === undef)
return FALSE;
// Obj2 item has a different value
@ -16574,8 +17220,6 @@ tinymce.ForceBlocks = function(editor) {
return TRUE;
};
// Check if next/prev exists and that they are elements
if (prev && next) {
function findElementSibling(node, sibling_name) {
for (sibling = node; sibling; sibling = sibling[sibling_name]) {
if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)
@ -16588,6 +17232,8 @@ tinymce.ForceBlocks = function(editor) {
return node;
};
// Check if next/prev exists and that they are elements
if (prev && next) {
// If previous sibling is empty then jump over it
prev = findElementSibling(prev, 'previousSibling');
next = findElementSibling(next, 'nextSibling');
@ -16641,7 +17287,7 @@ tinymce.ForceBlocks = function(editor) {
}
// If end text node is excluded then walk to the previous node
if (container.nodeType === 3 && !start && offset == 0) {
if (container.nodeType === 3 && !start && offset === 0) {
container = new TreeWalker(container, ed.getBody()).prev() || container;
}
@ -16894,6 +17540,15 @@ tinymce.ForceBlocks = function(editor) {
}
});
// Remove bogus state if they got filled by contents using editor.selection.setContent
selection.onSetContent.add(function() {
dom.getParent(selection.getStart(), function(node) {
if (node.id !== caretContainerId && dom.getAttrib(node, 'data-mce-bogus') && !dom.isEmpty(node)) {
dom.setAttrib(node, 'data-mce-bogus', null);
}
});
});
self._hasCaretEvents = true;
}
@ -16907,14 +17562,15 @@ tinymce.ForceBlocks = function(editor) {
function moveStart(rng) {
var container = rng.startContainer,
offset = rng.startOffset,
offset = rng.startOffset, isAtEndOfText,
walker, node, nodes, tmpNode;
// Convert text node into index if possible
if (container.nodeType == 3 && offset >= container.nodeValue.length) {
// Get the parent container location and walk from there
offset = nodeIndex(container);
container = container.parentNode;
offset = nodeIndex(container) + 1;
isAtEndOfText = true;
}
// Move startContainer/startOffset in to a suitable node
@ -16924,7 +17580,7 @@ tinymce.ForceBlocks = function(editor) {
walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
// If offset is at end of the parent node walk to the next one
if (offset > nodes.length - 1)
if (offset > nodes.length - 1 || isAtEndOfText)
walker.next();
for (node = walker.current(); node; node = walker.next()) {
@ -16950,9 +17606,6 @@ tinymce.ForceBlocks = function(editor) {
tinymce.onAddEditor.add(function(tinymce, ed) {
var filters, fontSizes, dom, settings = ed.settings;
if (settings.inline_styles) {
fontSizes = tinymce.explode(settings.font_size_legacy_values);
function replaceWithSpan(node, styles) {
tinymce.each(styles, function(value, name) {
if (value)
@ -16962,13 +17615,26 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
dom.rename(node, 'span');
};
function convert(editor, params) {
dom = editor.dom;
if (settings.convert_fonts_to_spans) {
tinymce.each(dom.select('font,u,strike', params.node), function(node) {
filters[node.nodeName.toLowerCase()](ed.dom, node);
});
}
};
if (settings.inline_styles) {
fontSizes = tinymce.explode(settings.font_size_legacy_values);
filters = {
font : function(dom, node) {
replaceWithSpan(node, {
backgroundColor : node.style.backgroundColor,
color : node.color,
fontFamily : node.face,
fontSize : fontSizes[parseInt(node.size) - 1]
fontSize : fontSizes[parseInt(node.size, 10) - 1]
});
},
@ -16985,16 +17651,6 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
};
function convert(editor, params) {
dom = editor.dom;
if (settings.convert_fonts_to_spans) {
tinymce.each(dom.select('font,u,strike', params.node), function(node) {
filters[node.nodeName.toLowerCase()](ed.dom, node);
});
}
};
ed.onPreProcess.add(convert);
ed.onSetContent.add(convert);
@ -17011,16 +17667,21 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;
function handleEnterKey(evt) {
var rng = selection.getRng(true), tmpRng, container, offset, parentBlock, newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName;
var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode,
newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
// Returns true if the block can be split into two blocks or not
function canSplitBlock(node) {
return node && dom.isBlock(node) && !/^(TD|TH|CAPTION)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position);
return node &&
dom.isBlock(node) &&
!/^(TD|TH|CAPTION)$/.test(node.nodeName) &&
!/^(fixed|absolute)/i.test(node.style.position) &&
dom.getContentEditable(node) !== "true";
};
// Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
function moveToCaretPosition(root) {
var walker, node, rng, y, viewPort, lastNode = root;
var walker, node, rng, y, viewPort, lastNode = root, tempElm;
rng = dom.createRng();
@ -17050,8 +17711,19 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
} else {
if (root.nodeName == 'BR') {
if (root.nextSibling && dom.isBlock(root.nextSibling)) {
// Trick on older IE versions to render the caret before the BR between two lists
if (!documentMode || documentMode < 9) {
tempElm = dom.create('br');
root.parentNode.insertBefore(tempElm, root);
}
rng.setStartBefore(root);
rng.setEndBefore(root);
} else {
rng.setStartAfter(root);
rng.setEndAfter(root);
}
} else {
rng.setStart(root, 0);
rng.setEnd(root, 0);
@ -17060,6 +17732,9 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
selection.setRng(rng);
// Remove tempElm created for old IE:s
dom.remove(tempElm);
viewPort = dom.getViewPort(editor.getWin());
// scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
@ -17074,10 +17749,11 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
function createNewBlock(name) {
var node = container, block, clonedNode, caretNode;
block = name ? dom.create(name) : parentBlock.cloneNode(false);
block = name || parentBlockName == "TABLE" ? dom.create(name || newBlockName) : parentBlock.cloneNode(false);
caretNode = block;
// Clone any parent styles
if (settings.keep_styles !== false) {
do {
if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
clonedNode = node.cloneNode(false);
@ -17092,6 +17768,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
}
} while (node = node.parentNode);
}
// BR is needed in empty blocks on non IE browsers
if (!tinymce.isIE) {
@ -17103,13 +17780,23 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
// Returns true/false if the caret is at the start/end of the parent block element
function isCaretAtStartOrEndOfBlock(start) {
var walker, node;
var walker, node, name;
// Caret is in the middle of a text node like "a|b"
if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
return false;
}
// If after the last element in block node edge case for #5091
if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
return true;
}
// Caret can be before/after a table
if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
}
// Walk the DOM and look for text nodes or non empty elements
walker = new TreeWalker(container, parentBlock);
while (node = (start ? walker.prev() : walker.next())) {
@ -17134,15 +17821,15 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
// Wraps any text nodes or inline elements in the specified forced root block name
function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
var newBlock, parentBlock, startNode, node, next;
var newBlock, parentBlock, startNode, node, next, blockName = newBlockName || 'P';
// Not in a block element or in a table cell or caption
parentBlock = dom.getParent(container, dom.isBlock);
if (newBlockName && !evt.shiftKey && (!parentBlock || !canSplitBlock(parentBlock))) {
parentBlock = parentBlock || dom.getRoot();
if (!parentBlock || !canSplitBlock(parentBlock)) {
parentBlock = parentBlock || editableRoot;
if (!parentBlock.hasChildNodes()) {
newBlock = dom.create(newBlockName);
newBlock = dom.create(blockName);
parentBlock.appendChild(newBlock);
rng.setStart(newBlock, 0);
rng.setEnd(newBlock, 0);
@ -17162,7 +17849,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
if (startNode) {
newBlock = dom.create(newBlockName);
newBlock = dom.create(blockName);
startNode.parentNode.insertBefore(newBlock, startNode);
// Start wrapping until we hit a block
@ -17241,7 +17928,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
// Inserts a BR element if the forced_root_block option is set to false or empty string
function insertBr() {
var brElm, extraBr, documentMode;
var brElm, extraBr;
if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
// Insert extra BR element at the end block elements
@ -17258,7 +17945,6 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
rng.insertNode(brElm);
// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
documentMode = dom.doc.documentMode;
if (tinymce.isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
}
@ -17286,6 +17972,22 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
} while (node);
};
function getEditableRoot(node) {
var root = dom.getRoot(), parent, editableRoot;
// Get all parents until we hit a non editable parent or the root
parent = node;
while (parent !== root && dom.getContentEditable(parent) !== "false") {
if (dom.getContentEditable(parent) === "true") {
editableRoot = parent;
}
parent = parent.parentNode;
}
return parent !== root ? editableRoot : root;
};
// Delete any selected contents
if (!rng.collapsed) {
editor.execCommand('Delete');
@ -17302,18 +18004,40 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
offset = rng.startOffset;
newBlockName = settings.forced_root_block;
newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
documentMode = dom.doc.documentMode;
// Resolve node index
if (container.nodeType == 1 && container.hasChildNodes()) {
isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
offset = 0;
}
// Get editable root node normaly the body element but sometimes a div or span
editableRoot = getEditableRoot(container);
// If there is no editable root then enter is done inside a contentEditable false element
if (!editableRoot) {
return;
}
undoManager.beforeChange();
// If editable root isn't block nor the root of the editor
if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
if (!newBlockName || evt.shiftKey) {
insertBr();
}
return;
}
// Wrap the current node and it's sibling in a default block if it's needed.
// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
// This won't happen if root blocks are disabled or the shiftKey is pressed
if ((newBlockName && !evt.shiftKey) || (!newBlockName && evt.shiftKey)) {
container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
}
// Find parent block and setup empty block paddings
parentBlock = dom.getParent(container, dom.isBlock);
@ -17394,3 +18118,4 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
});
};
})(tinymce);