TinyMCE: update to 4.0.28+. Includes all changes until 09-06-2014: 32cb108d41. Changelog: 32cb108d41/changelog.txt.

See #28391.

git-svn-id: https://develop.svn.wordpress.org/trunk@28768 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2014-06-17 23:49:00 +00:00
parent f1172ba195
commit b8fd9d997e
42 changed files with 2693 additions and 566 deletions

View File

@ -40,14 +40,27 @@ tinymce.PluginManager.add('image', function(editor) {
img.src = url;
}
function applyPreview(items) {
tinymce.each(items, function(item) {
item.textStyle = function() {
return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
};
});
function buildListItems(inputList, itemCallback, startItems) {
function appendItems(values, output) {
output = output || [];
return items;
tinymce.each(values, function(item) {
var menuItem = {text: item.text || item.title};
if (item.menu) {
menuItem.menu = appendItems(item.menu);
} else {
menuItem.value = item.value;
itemCallback(menuItem);
}
output.push(menuItem);
});
return output;
}
return appendItems(inputList, startItems || []);
}
function createImageList(callback) {
@ -71,53 +84,7 @@ tinymce.PluginManager.add('image', function(editor) {
function showDialog(imageList) {
var win, data = {}, dom = editor.dom, imgElm = editor.selection.getNode();
var width, height, imageListCtrl, classListCtrl;
function buildValues(listSettingName, dataItemName, defaultItems) {
var selectedItem, items = [];
tinymce.each(editor.settings[listSettingName] || defaultItems, function(target) {
var item = {
text: target.text || target.title,
value: target.value
};
items.push(item);
if (data[dataItemName] === target.value || (!selectedItem && target.selected)) {
selectedItem = item;
}
});
if (selectedItem && !data[dataItemName]) {
data[dataItemName] = selectedItem.value;
selectedItem.selected = true;
}
return items;
}
function buildImageList() {
function appendItems(values, output) {
output = output || [];
tinymce.each(values, function(value) {
var item = {text: value.text || value.title};
if (value.menu) {
item.menu = appendItems(value.menu);
} else {
item.value = editor.convertURL(value.value || value.url, 'src');
}
output.push(item);
});
return output;
}
return appendItems(imageList, [{text: 'None', value: ''}]);
}
var width, height, imageListCtrl, classListCtrl, imageDimensions = editor.settings.image_dimensions !== false;
function recalcSize() {
var widthCtrl, heightCtrl, newWidth, newHeight;
@ -125,6 +92,10 @@ tinymce.PluginManager.add('image', function(editor) {
widthCtrl = win.find('#width')[0];
heightCtrl = win.find('#height')[0];
if (!widthCtrl || !heightCtrl) {
return;
}
newWidth = widthCtrl.value();
newHeight = heightCtrl.value();
@ -146,12 +117,15 @@ tinymce.PluginManager.add('image', function(editor) {
function waitLoad(imgElm) {
function selectImage() {
imgElm.onload = imgElm.onerror = null;
editor.selection.select(imgElm);
editor.nodeChanged();
if (editor.selection) {
editor.selection.select(imgElm);
editor.nodeChanged();
}
}
imgElm.onload = function() {
if (!data.width && !data.height) {
if (!data.width && !data.height && imageDimensions) {
dom.setAttribs(imgElm, {
width: imgElm.clientWidth,
height: imgElm.clientHeight
@ -188,6 +162,7 @@ tinymce.PluginManager.add('image', function(editor) {
data.style = null;
}
// Setup new data excluding style properties
data = {
src: data.src,
alt: data.alt,
@ -197,10 +172,6 @@ tinymce.PluginManager.add('image', function(editor) {
"class": data["class"]
};
if (!data["class"]) {
delete data["class"];
}
editor.undoManager.transact(function() {
// WP
var eventData = { node: imgElm, data: data, caption: caption };
@ -251,7 +222,7 @@ tinymce.PluginManager.add('image', function(editor) {
}
getImageSize(this.value(), function(data) {
if (data.width && data.height) {
if (data.width && data.height && imageDimensions) {
width = data.width;
height = data.height;
@ -283,7 +254,13 @@ tinymce.PluginManager.add('image', function(editor) {
imageListCtrl = {
type: 'listbox',
label: 'Image list',
values: buildImageList(),
values: buildListItems(
imageList,
function(item) {
item.value = editor.convertURL(item.value || item.url, 'src');
},
[{text: 'None', value: ''}]
),
value: data.src && editor.convertURL(data.src, 'src'),
onselect: function(e) {
var altCtrl = win.find('#alt');
@ -305,7 +282,16 @@ tinymce.PluginManager.add('image', function(editor) {
name: 'class',
type: 'listbox',
label: 'Class',
values: applyPreview(buildValues('image_class_list', 'class'))
values: buildListItems(
editor.settings.image_class_list,
function(item) {
if (item.value) {
item.textStyle = function() {
return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
};
}
}
)
};
}
@ -319,7 +305,7 @@ tinymce.PluginManager.add('image', function(editor) {
generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'});
}
if (editor.settings.image_dimensions !== false) {
if (imageDimensions) {
generalFormItems.push({
type: 'container',
label: 'Dimensions',
@ -430,11 +416,6 @@ tinymce.PluginManager.add('image', function(editor) {
}
}
// WP
editor.addCommand( 'mceImage', function() {
createImageList( showDialog )();
});
editor.addButton('image', {
icon: 'image',
tooltip: 'Insert/edit image',
@ -449,4 +430,6 @@ tinymce.PluginManager.add('image', function(editor) {
context: 'insert',
prependToContext: true
});
editor.addCommand('mceImage', createImageList(showDialog));
});

File diff suppressed because one or more lines are too long

View File

@ -162,7 +162,23 @@ tinymce.PluginManager.add('media', function(editor, url) {
}
],
onSubmit: function() {
var beforeObjects, afterObjects, i, y;
beforeObjects = editor.dom.select('img[data-mce-object]');
editor.insertContent(dataToHtml(this.toJSON()));
afterObjects = editor.dom.select('img[data-mce-object]');
// Find new image placeholder so we can select it
for (i = 0; i < beforeObjects.length; i++) {
for (y = afterObjects.length - 1; y >= 0; y--) {
if (beforeObjects[i] == afterObjects[y]) {
afterObjects.splice(y, 1);
}
}
}
editor.selection.select(afterObjects[0]);
editor.nodeChanged();
}
});
}
@ -223,7 +239,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
if (data.embed) {
html = updateHtml(data.embed, data, true);
} else {
} else {
var videoScript = getVideoScriptMatch(data.source1);
if (videoScript) {
data.type = 'script';

File diff suppressed because one or more lines are too long

View File

@ -361,7 +361,7 @@ define("tinymce/pasteplugin/Clipboard", [
pasteBinElm = dom.add(editor.getBody(), 'div', {
id: "mcepastebin",
contentEditable: true,
"data-mce-bogus": "1",
"data-mce-bogus": "all",
style: 'position: absolute; top: ' + top + 'px;' +
'width: 10px; height: 10px; overflow: hidden; opacity: 0'
}, pasteBinDefaultContent);
@ -471,42 +471,44 @@ define("tinymce/pasteplugin/Clipboard", [
* Checks if the clipboard contains image data if it does it will take that data
* and convert it into a data url image and paste that image at the caret location.
*
* @param {ClipboardEvent} e Paste event object.
* @param {Object} clipboardContent Collection of clipboard contents.
* @param {ClipboardEvent} e Paste/drop event object.
* @param {DOMRange} rng Optional rng object to move selection to.
* @return {Boolean} true/false if the image data was found or not.
*/
function pasteImageData(e, clipboardContent) {
function pasteImage(item) {
if (items[i].type == 'image/png') {
var reader = new FileReader();
function pasteImageData(e, rng) {
var dataTransfer = e.clipboardData || e.dataTransfer;
reader.onload = function() {
pasteHtml('<img src="' + reader.result + '">');
};
function processItems(items) {
var i, item, reader;
reader.readAsDataURL(item.getAsFile());
function pasteImage() {
if (rng) {
editor.selection.setRng(rng);
rng = null;
}
return true;
pasteHtml('<img src="' + reader.result + '">');
}
}
// If paste data images are disabled or there is HTML or plain text
// contents then proceed with the normal paste process
if (!editor.settings.paste_data_images || "text/html" in clipboardContent || "text/plain" in clipboardContent) {
return;
}
if (e.clipboardData) {
var items = e.clipboardData.items;
if (items) {
for (var i = 0; i < items.length; i++) {
if (pasteImage(items[i])) {
for (i = 0; i < items.length; i++) {
item = items[i];
if (/^image\/(jpeg|png|gif)$/.test(item.type)) {
reader = new FileReader();
reader.onload = pasteImage;
reader.readAsDataURL(item.getAsFile ? item.getAsFile() : item);
e.preventDefault();
return true;
}
}
}
}
if (editor.settings.paste_data_images && dataTransfer) {
return processItems(dataTransfer.items) || processItems(dataTransfer.files);
}
}
/**
@ -546,6 +548,13 @@ define("tinymce/pasteplugin/Clipboard", [
function registerEventHandlers() {
editor.on('keydown', function(e) {
function removePasteBinOnKeyUp(e) {
// Ctrl+V or Shift+Insert
if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
removePasteBin();
}
}
// Ctrl+V or Shift+Insert
if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86;
@ -571,13 +580,13 @@ define("tinymce/pasteplugin/Clipboard", [
removePasteBin();
createPasteBin();
}
});
editor.on('keyup', function(e) {
// Ctrl+V or Shift+Insert
if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
removePasteBin();
// Remove pastebin if we get a keyup and no paste event
// For example pasting a file in IE 11 will not produce a paste event
editor.once('keyup', removePasteBinOnKeyUp);
editor.once('paste', function() {
editor.off('keyup', removePasteBinOnKeyUp);
});
}
});
@ -593,7 +602,7 @@ define("tinymce/pasteplugin/Clipboard", [
return;
}
if (pasteImageData(e, clipboardContent)) {
if (pasteImageData(e)) {
removePasteBin();
return;
}
@ -683,7 +692,15 @@ define("tinymce/pasteplugin/Clipboard", [
editor.on('drop', function(e) {
var rng = getCaretRangeFromEvent(e);
if (rng && !e.isDefaultPrevented()) {
if (e.isDefaultPrevented()) {
return;
}
if (pasteImageData(e, rng)) {
return;
}
if (rng) {
var dropContent = getDataTransferItems(e.dataTransfer);
var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain'];
@ -706,6 +723,20 @@ define("tinymce/pasteplugin/Clipboard", [
}
}
});
editor.on('dragover dragend', function(e) {
var i, dataTransfer = e.dataTransfer;
if (editor.settings.paste_data_images && dataTransfer) {
for (i = 0; i < dataTransfer.types.length; i++) {
// Prevent default if we have files dragged into the editor since the pasteImageData handles that
if (dataTransfer.types[i] == "Files") {
e.preventDefault();
return false;
}
}
}
});
}
self.pasteHtml = pasteHtml;
@ -722,7 +753,10 @@ define("tinymce/pasteplugin/Clipboard", [
while (i--) {
var src = nodes[i].attributes.map.src;
if (src && src.indexOf('data:image') === 0) {
// Some browsers automatically produce data uris on paste
// Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141
if (src && /^(data:image|webkit\-fake\-url)/.test(src)) {
if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) {
nodes[i].remove();
}
@ -731,14 +765,6 @@ define("tinymce/pasteplugin/Clipboard", [
}
});
});
editor.on('BeforeAddUndo', function(e) {
// Remove pastebin HTML incase it should be added to an undo
// level for example when you paste a file on older IE
if (e.level.content) {
e.level.content = e.level.content.replace(/<div id="?mcepastebin"?[^>]+>%MCEPASTEBIN%<\/div>/gi, '');
}
});
};
});

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,8 @@
/*eslint consistent-this:0 */
tinymce.PluginManager.add('textcolor', function(editor) {
var VK = tinymce.util.VK;
function mapColors() {
var i, colors = [], colorMap;
@ -105,6 +107,29 @@ tinymce.PluginManager.add('textcolor', function(editor) {
html += '</tr>';
}
if (editor.settings.textcolor_enable_hex) {
var hexIdN = last + 1;
var hexInputColSpan = cols - 1;
html += (
'<tr>' +
'<td>' +
'<div id="' + ctrl._id + '-' + hexIdN + '"' +
'data-mce-color=""' +
'style="background-color: #FFFFFF"' +
'data-mce-hex-picker="true"' +
'role="option" ' +
'>' +
'</div>' +
'</td>' +
'<td colspan="' + hexInputColSpan + '">' +
'# <input type="text" class="mce-textcolor-hexpicker"' +
'role="textbox" name="mce-hexcolorpicker"' +
'id="' + ctrl._id + '-hexcolorpicker" maxlength="6" >' +
'</td>' +
'</tr>'
);
}
html += '</tbody></table>';
return html;
@ -112,7 +137,10 @@ tinymce.PluginManager.add('textcolor', function(editor) {
function onPanelClick(e) {
var buttonCtrl = this.parent(), value;
if (e.target.getAttribute('disabled')) {
return;
}
if ((value = e.target.getAttribute('data-mce-color'))) {
if (this.lastId) {
document.getElementById(this.lastId).setAttribute('aria-selected', false);
@ -136,6 +164,95 @@ tinymce.PluginManager.add('textcolor', function(editor) {
}
}
/**
* isValidHex checks if the provided string is valid hex color string
*
* @param {string} hexString 3 or 6 chars string representing a color.
* @return {Boolean} [true] the string is valid hex color
* [false] the string is not valid hex color
*/
function isValidHex(hexString) {
return /(^[0-9A-F]{3,6}$)/i.test(hexString);
}
/**
* isSpecialStroke checks if the keyCode is currently a special one:
* backspace, delete, arrow keys (left/right)
* or if it's a special ctrl+x/c/v
*
* @param {string} keyCode
* @return {Boolean}
*/
function isSpecialStroke(e) {
var keyCode = e.keyCode;
// Allow delete and backspace
if (keyCode === VK.BACKSPACE || keyCode === VK.DELETE ) {
return true;
}
// Allow arrow movements
if (keyCode === VK.LEFT || keyCode === VK.RIGHT) {
return true;
}
// Allow CTRL/CMD + C/V/X
if ((tinymce.isMac ? e.metaKey : e.ctrlKey) && (keyCode == 67 || keyCode == 88 || keyCode == 86)) {
return true;
}
return false;
}
function initHexPicker(e) {
if (!editor.settings.textcolor_enable_hex) {
return;
}
var wrapper = document.querySelector('#' + e.target._id);
var input = wrapper.querySelector('[name="mce-hexcolorpicker"]');
var hexcolorDiv = wrapper.querySelector('[data-mce-hex-picker]');
var inputEvent = 'input';
editor.dom.events.bind(input, 'keydown', function(e){
var keyCode = e.keyCode;
if (isSpecialStroke(e)) {
return;
}
// Look for anything which is not A-Z or 0-9 and it is not a special char.
if (!((keyCode >= 48 && keyCode <= 57) || (keyCode >= 65 && keyCode <= 70) || (keyCode >= 96 && keyCode <= 105)) ) {
e.preventDefault();
}
// On Enter, take it like a click on the hexcolorDiv
if ( (keyCode === VK.ENTER && isValidHex(input.value) ) ) {
hexcolorDiv.click();
}
});
// If IE8 we can't use the input event, so we have to
// listen for keypress and paste events.
// In IE9 the input implementation is buggy so
// we use the same events as we'd like on IE8
if (tinymce.Env.ie && tinymce.Env.ie <= 9) {
inputEvent = 'keypress paste blur keydown keyup propertychange';
}
editor.dom.events.bind(input, inputEvent, function(){
if (isValidHex(input.value)) {
hexcolorDiv.setAttribute('data-mce-color', input.value);
hexcolorDiv.setAttribute('style', 'background-color:#' + input.value);
hexcolorDiv.removeAttribute('disabled');
} else {
hexcolorDiv.setAttribute('disabled', 'disabled');
}
});
}
editor.addButton('forecolor', {
type: 'colorbutton',
tooltip: 'Text color',
@ -144,7 +261,8 @@ tinymce.PluginManager.add('textcolor', function(editor) {
role: 'application',
ariaRemember: true,
html: renderColorPicker,
onclick: onPanelClick
onclick: onPanelClick,
onPostRender: initHexPicker
},
onclick: onButtonClick
});
@ -157,7 +275,8 @@ tinymce.PluginManager.add('textcolor', function(editor) {
role: 'application',
ariaRemember: true,
html: renderColorPicker,
onclick: onPanelClick
onclick: onPanelClick,
onPostRender: initHexPicker
},
onclick: onButtonClick
});

View File

@ -1 +1 @@
tinymce.PluginManager.add("textcolor",function(e){function t(){var t,o,l=[];for(o=e.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","C0C0C0","Silver","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum","FFFFFF","White"],t=0;t<o.length;t+=2)l.push({text:o[t+1],color:o[t]});return l}function o(){var o,l,r,a,c,i,n,F,d,s=this;for(o=t(),r='<table class="mce-grid mce-grid-border mce-colorbutton-grid" role="list" cellspacing="0"><tbody>',a=o.length-1,c=e.settings.textcolor_rows||5,i=e.settings.textcolor_cols||8,F=0;c>F;F++){for(r+="<tr>",n=0;i>n;n++)d=F*i+n,d>a?r+="<td></td>":(l=o[d],r+='<td><div id="'+s._id+"-"+d+'" data-mce-color="'+l.color+'" role="option" tabIndex="-1" style="'+(l?"background-color: #"+l.color:"")+'" title="'+l.text+'"></div></td>');r+="</tr>"}return r+="</tbody></table>"}function l(t){var o,l=this.parent();(o=t.target.getAttribute("data-mce-color"))&&(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),t.target.setAttribute("aria-selected",!0),this.lastId=t.target.id,l.hidePanel(),o="#"+o,l.color(o),e.execCommand(l.settings.selectcmd,!1,o))}function r(){var t=this;t._color&&e.execCommand(t.settings.selectcmd,!1,t._color)}e.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",selectcmd:"ForeColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r}),e.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",selectcmd:"HiliteColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r})});
tinymce.PluginManager.add("textcolor",function(e){function t(){var t,o,r=[];for(o=e.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","C0C0C0","Silver","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum","FFFFFF","White"],t=0;t<o.length;t+=2)r.push({text:o[t+1],color:o[t]});return r}function o(){var o,r,l,c,i,a,n,d,s,u=this;for(o=t(),l='<table class="mce-grid mce-grid-border mce-colorbutton-grid" role="list" cellspacing="0"><tbody>',c=o.length-1,i=e.settings.textcolor_rows||5,a=e.settings.textcolor_cols||8,d=0;i>d;d++){for(l+="<tr>",n=0;a>n;n++)s=d*a+n,s>c?l+="<td></td>":(r=o[s],l+='<td><div id="'+u._id+"-"+s+'" data-mce-color="'+r.color+'" role="option" tabIndex="-1" style="'+(r?"background-color: #"+r.color:"")+'" title="'+r.text+'"></div></td>');l+="</tr>"}if(e.settings.textcolor_enable_hex){var F=c+1,m=a-1;l+='<tr><td><div id="'+u._id+"-"+F+'"data-mce-color=""style="background-color: #FFFFFF"data-mce-hex-picker="true"role="option" ></div></td><td colspan="'+m+'"># <input type="text" class="mce-textcolor-hexpicker"role="textbox" name="mce-hexcolorpicker"id="'+u._id+'-hexcolorpicker" maxlength="6" ></td></tr>'}return l+="</tbody></table>"}function r(t){var o,r=this.parent();t.target.getAttribute("disabled")||(o=t.target.getAttribute("data-mce-color"))&&(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),t.target.setAttribute("aria-selected",!0),this.lastId=t.target.id,r.hidePanel(),o="#"+o,r.color(o),e.execCommand(r.settings.selectcmd,!1,o))}function l(){var t=this;t._color&&e.execCommand(t.settings.selectcmd,!1,t._color)}function c(e){return/(^[0-9A-F]{3,6}$)/i.test(e)}function i(e){var t=e.keyCode;return t===n.BACKSPACE||t===n.DELETE?!0:t===n.LEFT||t===n.RIGHT?!0:(tinymce.isMac?e.metaKey:e.ctrlKey)&&(67==t||88==t||86==t)?!0:!1}function a(t){if(e.settings.textcolor_enable_hex){var o=document.querySelector("#"+t.target._id),r=o.querySelector('[name="mce-hexcolorpicker"]'),l=o.querySelector("[data-mce-hex-picker]"),a="input";e.dom.events.bind(r,"keydown",function(e){var t=e.keyCode;i(e)||(t>=48&&57>=t||t>=65&&70>=t||t>=96&&105>=t||e.preventDefault(),t===n.ENTER&&c(r.value)&&l.click())}),tinymce.Env.ie&&tinymce.Env.ie<=9&&(a="keypress paste blur keydown keyup propertychange"),e.dom.events.bind(r,a,function(){c(r.value)?(l.setAttribute("data-mce-color",r.value),l.setAttribute("style","background-color:#"+r.value),l.removeAttribute("disabled")):l.setAttribute("disabled","disabled")})}}var n=tinymce.util.VK;e.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",selectcmd:"ForeColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:r,onPostRender:a},onclick:l}),e.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",selectcmd:"HiliteColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:r,onPostRender:a},onclick:l})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6154,7 +6154,7 @@ define("tinymce/dom/DOMUtils", [
outHtml += '<' + name;
for (key in attrs) {
if (attrs.hasOwnProperty(key) && attrs[key] !== null) {
if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') {
outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
}
}
@ -6390,70 +6390,72 @@ define("tinymce/dom/DOMUtils", [
* // Sets class attribute on a specific element in the current page
* tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
*/
setAttrib: function(e, n, v) {
setAttrib: function(elm, name, value) {
var self = this;
// What's the point
if (!e || !n) {
if (!elm || !name) {
return;
}
return this.run(e, function(e) {
var s = self.settings;
var originalValue = e.getAttribute(n);
if (v !== null) {
switch (n) {
return this.run(elm, function(elm) {
var settings = self.settings;
var originalValue = elm.getAttribute(name);
if (value !== null) {
switch (name) {
case "style":
if (!is(v, 'string')) {
each(v, function(v, n) {
self.setStyle(e, n, v);
if (!is(value, 'string')) {
each(value, function(value, name) {
self.setStyle(elm, name, value);
});
return;
}
// No mce_style for elements with these since they might get resized by the user
if (s.keep_values) {
if (v) {
e.setAttribute('data-mce-style', v, 2);
if (settings.keep_values) {
if (value) {
elm.setAttribute('data-mce-style', value, 2);
} else {
e.removeAttribute('data-mce-style', 2);
elm.removeAttribute('data-mce-style', 2);
}
}
e.style.cssText = v;
elm.style.cssText = value;
break;
case "class":
e.className = v || ''; // Fix IE null bug
elm.className = value || ''; // Fix IE null bug
break;
case "src":
case "href":
if (s.keep_values) {
if (s.url_converter) {
v = s.url_converter.call(s.url_converter_scope || self, v, n, e);
if (settings.keep_values) {
if (settings.url_converter) {
value = settings.url_converter.call(settings.url_converter_scope || self, value, name, elm);
}
self.setAttrib(e, 'data-mce-' + n, v, 2);
self.setAttrib(elm, 'data-mce-' + name, value, 2);
}
break;
case "shape":
e.setAttribute('data-mce-style', v);
elm.setAttribute('data-mce-style', value);
break;
}
}
if (is(v) && v !== null && v.length !== 0) {
e.setAttribute(n, '' + v, 2);
if (is(value) && value !== null && value.length !== 0) {
elm.setAttribute(name, '' + value, 2);
} else {
e.removeAttribute(n, 2);
elm.removeAttribute(name, 2);
}
// fire onChangeAttrib event for attributes that have changed
if (originalValue != v && s.onSetAttrib) {
s.onSetAttrib({attrElm: e, attrName: n, attrValue: v});
if (originalValue != value && settings.onSetAttrib) {
settings.onSetAttrib({attrElm: elm, attrName: name, attrValue: value});
}
});
},
@ -9828,15 +9830,24 @@ define("tinymce/html/SaxParser", [
/**
* Returns the index of the end tag for a specific start tag. This can be
* used to skip all children of a parent element from being processed.
*
* @private
* @method findEndTag
* @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements.
* @param {String} html HTML string to find the end tag in.
* @param {Number} startIndex Indext to start searching at should be after the start tag.
* @return {Number} Index of the end tag.
*/
function skipUntilEndTag(schema, html, startIndex) {
var count = 1, matches, tokenRegExp, shortEndedElements;
function findEndTag(schema, html, startIndex) {
var count = 1, index, matches, tokenRegExp, shortEndedElements;
shortEndedElements = schema.getShortEndedElements();
tokenRegExp = /<([!?\/])?([A-Za-z0-9\-\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
tokenRegExp.lastIndex = startIndex;
tokenRegExp.lastIndex = index = startIndex;
while ((matches = tokenRegExp.exec(html))) {
index = tokenRegExp.lastIndex;
if (matches[1] === '/') { // End element
count--;
} else if (!matches[1]) { // Start element
@ -9852,7 +9863,7 @@ define("tinymce/html/SaxParser", [
}
}
return tokenRegExp.lastIndex;
return index;
}
/**
@ -9863,7 +9874,7 @@ define("tinymce/html/SaxParser", [
* @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
* @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
*/
return function(settings, schema) {
function SaxParser(settings, schema) {
var self = this;
function noop() {}
@ -10138,7 +10149,7 @@ define("tinymce/html/SaxParser", [
// Invalidate element if it's marked as bogus
if ((attr = attrList.map['data-mce-bogus'])) {
if (attr === 'all') {
index = skipUntilEndTag(schema, html, tokenRegExp.lastIndex);
index = findEndTag(schema, html, tokenRegExp.lastIndex);
tokenRegExp.lastIndex = index;
continue;
}
@ -10225,7 +10236,11 @@ define("tinymce/html/SaxParser", [
}
}
};
};
}
SaxParser.findEndTag = findEndTag;
return SaxParser;
});
// Included from: js/tinymce/classes/html/DomParser.js
@ -11490,8 +11505,12 @@ define("tinymce/dom/Serializer", [
while (i--) {
node = nodes[i];
value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
node.attr('class', value.length > 0 ? value : null);
value = node.attr('class');
if (value) {
value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
node.attr('class', value.length > 0 ? value : null);
}
}
});
@ -12423,6 +12442,7 @@ define("tinymce/dom/ControlSelection", [
'margin: 5px 10px;' +
'padding: 5px;' +
'position: absolute;' +
'z-index: 10001' +
'}'
);
@ -12463,7 +12483,7 @@ define("tinymce/dom/ControlSelection", [
if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) {
proportional = !VK.modifierPressed(e);
} else {
proportional = VK.modifierPressed(e) || selectedHandle[2] * selectedHandle[3] !== 0;
proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0);
}
// Constrain proportions
@ -12604,6 +12624,7 @@ define("tinymce/dom/ControlSelection", [
selectedElmGhost = selectedElm.cloneNode(true);
dom.addClass(selectedElmGhost, 'mce-clonedresizable');
dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');
selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
selectedElmGhost.unSelectabe = true;
dom.setStyles(selectedElmGhost, {
@ -12645,7 +12666,7 @@ define("tinymce/dom/ControlSelection", [
handleElm = dom.add(handlerContainerElm, 'div', {
id: 'mceResizeHandle' + name,
'data-mce-bogus': true,
'data-mce-bogus': 'all',
'class': 'mce-resizehandle',
unselectable: true,
style: 'cursor:' + name + '-resize; margin:0; padding:0'
@ -17154,7 +17175,16 @@ define("tinymce/Formatter", [
child = findFirstTextNode(node);
if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
child = child.deleteData(0, 1);
child.deleteData(0, 1);
// Fix for bug #6976
if (rng.startContainer == child) {
rng.startOffset--;
}
if (rng.endContainer == child) {
rng.endOffset--;
}
}
dom.remove(node, 1);
@ -17424,22 +17454,52 @@ define("tinymce/Formatter", [
*/
define("tinymce/UndoManager", [
"tinymce/Env",
"tinymce/util/Tools"
], function(Env, Tools) {
"tinymce/util/Tools",
"tinymce/html/SaxParser"
], function(Env, Tools, SaxParser) {
var trim = Tools.trim, trimContentRegExp;
trimContentRegExp = new RegExp([
'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
'<div[^>]+data-mce-bogus[^>]+>[^>]*<\\/div>', // Trim bogus divs like resize handles/resize helper
'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
].join('|'), 'gi');
return function(editor) {
var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
// Returns a trimmed version of the current editor contents
/**
* Returns a trimmed version of the editor contents to be used for the undo level. This
* will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
* remove the data-mce-selected attributes used for selection of objects and caret containers.
* It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
* be removed by the serialization logic when you save.
*
* @return {String} HTML contents of the editor excluding some internal bogus elements.
*/
function getContent() {
return trim(editor.getContent({format: 'raw', no_events: 1}).replace(trimContentRegExp, ''));
var content = trim(editor.getContent({format: 'raw', no_events: 1}));
var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema;
content = content.replace(trimContentRegExp, '');
shortEndedElements = schema.getShortEndedElements();
// Remove all bogus elements marked with "all"
while ((matches = bogusAllRegExp.exec(content))) {
index = bogusAllRegExp.lastIndex;
matchLength = matches[0].length;
if (shortEndedElements[matches[1]]) {
endTagIndex = index;
} else {
endTagIndex = SaxParser.findEndTag(schema, content, index);
}
content = content.substring(0, index - matchLength) + content.substring(endTagIndex);
bogusAllRegExp.lastIndex = index - matchLength;
}
return content;
}
function addNonTypingUndoLevel(e) {
@ -17538,12 +17598,44 @@ define("tinymce/UndoManager", [
editor.addShortcut('ctrl+z', '', 'Undo');
editor.addShortcut('ctrl+y,ctrl+shift+z', '', 'Redo');
editor.on('AddUndo Undo Redo ClearUndos MouseUp', function(e) {
editor.on('AddUndo Undo Redo ClearUndos', function(e) {
if (!e.isDefaultPrevented()) {
editor.nodeChanged();
}
});
// Selection range isn't updated until after the click events default handler is executed
// so we need to wait for the selection to update on Gecko/WebKit it happens right away.
// On IE it might take a while so we listen for the SelectionChange event.
//
// We can't use the SelectionChange on all browsers event since Gecko doesn't support that.
if (Env.ie) {
editor.on('MouseUp', function(e) {
if (!e.isDefaultPrevented()) {
editor.once('SelectionChange', function() {
// Selection change might fire when focus is lost
if (editor.dom.isChildOf(editor.selection.getStart(), editor.getBody())) {
editor.nodeChanged();
}
});
editor.nodeChanged();
}
});
} else {
editor.on('MouseUp', function() {
editor.nodeChanged();
});
editor.on('Click', function(e) {
if (!e.isDefaultPrevented()) {
setTimeout(function() {
editor.nodeChanged();
}, 0);
}
});
}
self = {
// Explose for debugging reasons
data: data,
@ -18176,56 +18268,9 @@ define("tinymce/EnterKey", [
undoManager.add();
}
// Walks the parent block to the right and look for BR elements
function hasRightSideContent() {
var walker = new TreeWalker(container, parentBlock), node;
while ((node = walker.next())) {
if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
return true;
}
}
}
// Inserts a BR element if the forced_root_block option is set to false or empty string
function insertBr() {
var brElm, extraBr, marker;
if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
// Insert extra BR element at the end block elements
if (!isIE && !hasRightSideContent()) {
brElm = dom.create('br');
rng.insertNode(brElm);
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
extraBr = true;
}
}
brElm = dom.create('br');
rng.insertNode(brElm);
// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
if (isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
}
// Insert temp marker and scroll to that
marker = dom.create('span', {}, '&nbsp;');
brElm.parentNode.insertBefore(marker, brElm);
selection.scrollIntoView(marker);
dom.remove(marker);
if (!extraBr) {
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
} else {
rng.setStartBefore(brElm);
rng.setEndBefore(brElm);
}
selection.setRng(rng);
undoManager.add();
editor.execCommand("InsertLineBreak", false, evt);
}
// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
@ -18593,12 +18638,14 @@ define("tinymce/EditorCommands", [
"tinymce/html/Serializer",
"tinymce/Env",
"tinymce/util/Tools",
"tinymce/dom/ElementUtils"
], function(Serializer, Env, Tools, ElementUtils) {
"tinymce/dom/ElementUtils",
"tinymce/dom/RangeUtils",
"tinymce/dom/TreeWalker"
], function(Serializer, Env, Tools, ElementUtils, RangeUtils, TreeWalker) {
// Added for compression purposes
var each = Tools.each, extend = Tools.extend;
var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
var isGecko = Env.gecko, isIE = Env.ie;
var isGecko = Env.gecko, isIE = Env.ie, isOldIE = Env.ie && Env.ie < 11;
var TRUE = true, FALSE = false;
return function(editor) {
@ -19248,6 +19295,93 @@ define("tinymce/EditorCommands", [
mceNewDocument: function() {
editor.setContent('');
},
"InsertLineBreak": function (command, ui, value) {
// We load the current event in from EnterKey.js when appropriate to heed
// certain event-specific variations such as ctrl-enter in a list
var evt = value;
var brElm, extraBr, marker;
var rng = selection.getRng(true);
new RangeUtils(dom).normalize(rng);
var offset = rng.startOffset;
var container = rng.startContainer;
// Resolve node index
if (container.nodeType == 1 && container.hasChildNodes()) {
var isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
if (isAfterLastNodeInContainer && container.nodeType == 3) {
offset = container.nodeValue.length;
} else {
offset = 0;
}
}
var parentBlock = dom.getParent(container, dom.isBlock);
var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
// Enter inside block contained within a LI then split or insert before/after LI
var isControlKey = evt && evt.ctrlKey;
if (containerBlockName == 'LI' && !isControlKey) {
parentBlock = containerBlock;
parentBlockName = containerBlockName;
}
// Walks the parent block to the right and look for BR elements
function hasRightSideContent() {
var walker = new TreeWalker(container, parentBlock), node;
var nonEmptyElementsMap = editor.schema.getNonEmptyElements();
while ((node = walker.next())) {
if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
return true;
}
}
}
if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
// Insert extra BR element at the end block elements
if (!isOldIE && !hasRightSideContent()) {
brElm = dom.create('br');
rng.insertNode(brElm);
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
extraBr = true;
}
}
brElm = dom.create('br');
rng.insertNode(brElm);
// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
var documentMode = dom.doc.documentMode;
if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
}
// Insert temp marker and scroll to that
marker = dom.create('span', {}, '&nbsp;');
brElm.parentNode.insertBefore(marker, brElm);
selection.scrollIntoView(marker);
dom.remove(marker);
if (!extraBr) {
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
} else {
rng.setStartBefore(brElm);
rng.setEndBefore(brElm);
}
selection.setRng(rng);
editor.undoManager.add();
return TRUE;
}
});
@ -20000,6 +20134,11 @@ define("tinymce/util/EventDispatcher", [
for (i = 0, l = handlers.length; i < l; i++) {
handlers[i] = callback = handlers[i];
// Unbind handlers marked with "once"
if (callback.once) {
off(name, callback);
}
// Stop immediate propagation if needed
if (args.isImmediatePropagationStopped()) {
args.stopPropagation();
@ -20105,7 +20244,8 @@ define("tinymce/util/EventDispatcher", [
hi = handlers.length;
while (hi--) {
if (handlers[hi] === callback) {
handlers.splice(hi, 1);
handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1));
bindings[name] = handlers;
}
}
}
@ -20127,6 +20267,25 @@ define("tinymce/util/EventDispatcher", [
return self;
}
/**
* Binds an event listener to a specific event by name
* and automatically unbind the event once the callback fires.
*
* @method once
* @param {String} name Event name or space separated list of events to bind.
* @param {callback} callback Callback to be executed when the event occurs.
* @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
* @return {Object} Current class instance.
* @example
* instance.once('event', function(e) {
* // Callback logic
* });
*/
function once(name, callback, prepend) {
callback.once = true;
return on(name, callback, prepend);
}
/**
* Returns true/false if the dispatcher has a event of the specified name.
*
@ -20143,6 +20302,7 @@ define("tinymce/util/EventDispatcher", [
self.fire = fire;
self.on = on;
self.off = off;
self.once = once;
self.has = has;
}
@ -25855,6 +26015,11 @@ define("tinymce/util/Quirks", [
function removeHrOnBackspace() {
editor.on('keydown', function(e) {
if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
// Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow
if (!editor.getBody().getElementsByTagName('hr').length) {
return;
}
if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
var node = selection.getNode();
var previousSibling = node.previousSibling;
@ -26548,7 +26713,6 @@ define("tinymce/util/Quirks", [
}
// All browsers
disableBackspaceIntoATable();
removeBlockQuoteOnBackSpace();
emptyEditorWhenDeleting();
normalizeSelection();
@ -26560,6 +26724,7 @@ define("tinymce/util/Quirks", [
selectControlElements();
setDefaultBlockType();
blockFormSubmitInsideEditor();
disableBackspaceIntoATable();
// iOS
if (Env.iOS) {
@ -26587,6 +26752,7 @@ define("tinymce/util/Quirks", [
if (Env.ie >= 11) {
bodyHeight();
doubleTrailingBrElements();
disableBackspaceIntoATable();
}
if (Env.ie) {
@ -26604,6 +26770,7 @@ define("tinymce/util/Quirks", [
removeGhostSelection();
showBrokenImageIcon();
blockCmdArrowNavigation();
disableBackspaceIntoATable();
}
};
});
@ -26715,6 +26882,18 @@ define("tinymce/util/Observable", [
return getEventDispatcher(this).off(name, callback);
},
/**
* Bind the event callback and once it fires the callback is removed.
*
* @method once
* @param {String} name Name of the event to bind.
* @param {callback} callback Callback to bind only once.
* @return {Object} Current class instance.
*/
once: function(name, callback) {
return getEventDispatcher(this).once(name, callback);
},
/**
* Returns true/false if the object has a event of the specified name.
*
@ -26760,7 +26939,7 @@ define("tinymce/EditorObservable", [
// Need to bind mousedown/mouseup etc to document not body in iframe mode
// Since the user might click on the HTML element not the BODY
if (!editor.inline && /^mouse|click|contextmenu|drop/.test(eventName)) {
if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
return editor.getDoc();
}
@ -28150,8 +28329,9 @@ define("tinymce/Editor", [
* need to update the UI states or element path etc.
*
* @method nodeChanged
* @param {Object} args Optional args to pass to NodeChange event handlers.
*/
nodeChanged: function() {
nodeChanged: function(args) {
var self = this, selection = self.selection, node, parents, root;
// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
@ -28176,7 +28356,11 @@ define("tinymce/Editor", [
parents.push(node);
});
self.fire('NodeChange', {element: node, parents: parents});
args = args || {};
args.element = node;
args.parents = parents;
self.fire('NodeChange', args);
}
},
@ -29022,8 +29206,8 @@ define("tinymce/Editor", [
var self = this;
if (!self.removed) {
self.removed = 1;
self.save();
self.removed = 1;
// Remove any hidden input
if (self.hasHiddenInput) {
@ -29620,9 +29804,9 @@ define("tinymce/EditorManager", [
// Get base URL for the current document
documentBaseURL = document.location.href;
// Check if the URL is a document based format like: http://site/dir/file
// Check if the URL is a document based format like: http://site/dir/file and file:///
// leave other formats like applewebdata://... intact
if (/^[^:]+:\/\/[^\/]+\//.test(documentBaseURL)) {
if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
if (!/[\/\\]$/.test(documentBaseURL)) {
@ -30246,6 +30430,9 @@ define("tinymce/util/XHR", [], function() {
xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
if (settings.crossDomain) {
xhr.withCredentials = true;
}
if (settings.content_type) {
xhr.setRequestHeader('Content-Type', settings.content_type);
}
@ -34484,7 +34671,9 @@ define("tinymce/ui/ListBox", [
self._values = values = settings.values;
if (values) {
setSelected(values);
if (typeof settings.value != "undefined") {
setSelected(values);
}
// Default with first item
if (!selected && values.length > 0) {

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ $wp_db_version = 27916;
*
* @global string $tinymce_version
*/
$tinymce_version = '4028-20140528';
$tinymce_version = '4028-20140617';
/**
* Holds the required PHP version

View File

@ -9,6 +9,7 @@
*/
/*jshint loopfunc:true */
/*eslint no-loop-func:0 */
/*global tinymce:true */
tinymce.PluginManager.add('noneditable', function(editor) {
@ -184,7 +185,7 @@ tinymce.PluginManager.add('noneditable', function(editor) {
if (offset < container.childNodes.length) {
// Browser represents caret position as the offset at the start of an element. When moving right
// this is the element we are moving into so we consider our container to be child node at offset-1
var pos = !left && offset > 0 ? offset-1 : offset;
var pos = !left && offset > 0 ? offset - 1 : offset;
container = container.childNodes[pos];
if (container.hasChildNodes()) {
container = container.firstChild;

View File

@ -61,9 +61,11 @@
<script src="tinymce/ui/Window.js"></script>
<!-- tinymce.util.* -->
<script src="tinymce/util/EventDispatcher.js"></script>
<script src="tinymce/util/JSON.js"></script>
<script src="tinymce/util/JSONRequest.js"></script>
<script src="tinymce/util/LocalStorage.js"></script>
<script src="tinymce/util/Observable.js"></script>
<script src="tinymce/util/Quirks_webkit.js"></script>
<script src="tinymce/util/URI.js"></script>
<script src="tinymce/util/XHR.js"></script>
@ -78,13 +80,18 @@
<script src="tinymce/Formatter_apply.js"></script>
<script src="tinymce/Formatter_check.js"></script>
<script src="tinymce/Formatter_remove.js"></script>
<script src="tinymce/Shortcuts.js"></script>
<script src="tinymce/UndoManager.js"></script>
<!-- tinymce.plugins.* -->
<!--<script src="plugins/autosave.js"></script>
<script src="plugins/fullpage.js"></script>
<script src="plugins/autosave.js"></script>
<script src="plugins/fullpage.js"></script> -->
<script src="plugins/image.js"></script>
<!--<script src="plugins/importcss.js"></script>
<script src="plugins/jquery_plugin.js"></script>
<script src="plugins/legacyoutput.js"></script>
<script src="plugins/lists.js"></script>
<script src="plugins/lists.js"></script> -->
<script src="plugins/media.js"></script>
<!--<script src="plugins/noneditable.js"></script> -->

View File

@ -0,0 +1,80 @@
(function() {
module("tinymce.plugins.Autolink", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
skin: false,
plugins: 'autolink',
autosave_ask_before_unload: false,
indent: false,
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
}
});
function typeUrl(url) {
editor.setContent('<p>' + url + '</p>');
Utils.setSelection('p', url.length);
Utils.type(' ');
return editor.getContent();
}
function typeAnEclipsedURL(url) {
url = "(" + url;
editor.setContent('<p>' + url + '</p>');
Utils.setSelection('p', url.length);
Utils.type(')');
return editor.getContent();
}
function typeNewlineURL(url) {
editor.setContent('<p>' + url + '</p>');
Utils.setSelection('p', url.length);
Utils.type('\n');
return editor.getContent();
}
if (tinymce.Env.ie) {
test("Skipped on IE since it has built in logic for autolink", function() {
ok(true);
});
return;
}
test("Urls ended with space", function() {
equal(typeUrl('http://www.tinymce.com'), '<p><a href="http://www.tinymce.com">http://www.tinymce.com</a></p>');
equal(typeUrl('https://www.tinymce.com'), '<p><a href="https://www.tinymce.com">https://www.tinymce.com</a></p>');
equal(typeUrl('ssh://www.tinymce.com'), '<p><a href="ssh://www.tinymce.com">ssh://www.tinymce.com</a></p>');
equal(typeUrl('ftp://www.tinymce.com'), '<p><a href="ftp://www.tinymce.com">ftp://www.tinymce.com</a></p>');
equal(typeUrl('www.tinymce.com'), '<p><a href="http://www.tinymce.com">www.tinymce.com</a></p>');
equal(typeUrl('www.tinymce.com.'), '<p><a href="http://www.tinymce.com">www.tinymce.com</a>.</p>');
equal(typeUrl('user@domain.com'), '<p><a href="mailto:user@domain.com">user@domain.com</a></p>');
equal(typeUrl('mailto:user@domain.com'), '<p><a href="mailto:user@domain.com">mailto:user@domain.com</a></p>');
equal(typeUrl('first-last@domain.com'), '<p><a href="mailto:first-last@domain.com">first-last@domain.com</a></p>');
});
test("Urls ended with )", function() {
equal(typeAnEclipsedURL('http://www.tinymce.com'), '<p>(<a href="http://www.tinymce.com">http://www.tinymce.com</a>)</p>');
equal(typeAnEclipsedURL('https://www.tinymce.com'), '<p>(<a href="https://www.tinymce.com">https://www.tinymce.com</a>)</p>');
equal(typeAnEclipsedURL('ssh://www.tinymce.com'), '<p>(<a href="ssh://www.tinymce.com">ssh://www.tinymce.com</a>)</p>');
equal(typeAnEclipsedURL('ftp://www.tinymce.com'), '<p>(<a href="ftp://www.tinymce.com">ftp://www.tinymce.com</a>)</p>');
equal(typeAnEclipsedURL('www.tinymce.com'), '<p>(<a href="http://www.tinymce.com">www.tinymce.com</a>)</p>');
equal(typeAnEclipsedURL('www.tinymce.com.'), '<p>(<a href="http://www.tinymce.com">www.tinymce.com</a>.)</p>');
});
test("Urls ended with new line", function() {
equal(typeNewlineURL('http://www.tinymce.com'), '<p><a href="http://www.tinymce.com">http://www.tinymce.com</a></p><p>&nbsp;</p>');
equal(typeNewlineURL('https://www.tinymce.com'), '<p><a href="https://www.tinymce.com">https://www.tinymce.com</a></p><p>&nbsp;</p>');
equal(typeNewlineURL('ssh://www.tinymce.com'), '<p><a href="ssh://www.tinymce.com">ssh://www.tinymce.com</a></p><p>&nbsp;</p>');
equal(typeNewlineURL('ftp://www.tinymce.com'), '<p><a href="ftp://www.tinymce.com">ftp://www.tinymce.com</a></p><p>&nbsp;</p>');
equal(typeNewlineURL('www.tinymce.com'), '<p><a href="http://www.tinymce.com">www.tinymce.com</a></p><p>&nbsp;</p>');
equal(typeNewlineURL('www.tinymce.com.'), '<p><a href="http://www.tinymce.com">www.tinymce.com</a>.</p><p>&nbsp;</p>');
});
})();

View File

@ -0,0 +1,128 @@
(function() {
module("tinymce.plugins.Image", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: 'textarea',
add_unload_trigger: false,
skin: false,
plugins: "image",
disable_nodechange: true,
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
},
teardown: function() {
delete editor.settings.image_dimensions;
delete editor.settings.file_browser_callback;
delete editor.settings.image_list;
delete editor.settings.image_class_list;
var win = Utils.getFontmostWindow();
if (win) {
win.close();
}
}
});
function cleanHtml(html) {
return Utils.cleanHtml(html).replace(/<p>(&nbsp;|<br[^>]+>)<\/p>$/, '');
}
function fillAndSubmitWindowForm(data) {
var win = Utils.getFontmostWindow();
win.fromJSON(data);
win.find('form')[0].submit();
win.close();
}
test('Default image dialog on empty editor', function() {
editor.setContent('');
editor.execCommand('mceImage', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"alt": "",
"constrain": true,
"height": "",
"src": "",
"width": ""
});
fillAndSubmitWindowForm({
"alt": "alt",
"height": "100",
"src": "src",
"width": "200"
});
equal(
cleanHtml(editor.getContent()),
'<p><img src="src" alt="alt" width="200" height="100" /></p>'
);
});
test('Image dialog image_dimensions: false', function() {
editor.settings.image_dimensions = false;
editor.setContent('');
editor.execCommand('mceImage', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"alt": "",
"src": ""
});
fillAndSubmitWindowForm({
"alt": "alt",
"src": "src"
});
equal(
cleanHtml(editor.getContent()),
'<p><img src="src" alt="alt" /></p>'
);
});
test('All image dialog ui options on empty editor', function() {
editor.settings.image_list = [
{title: 'link1', value: 'link1'},
{title: 'link2', value: 'link2'}
];
editor.settings.image_class_list = [
{title: 'class1', value: 'class1'},
{title: 'class2', value: 'class2'}
];
editor.setContent('');
editor.execCommand('mceImage', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"alt": "",
"class": "class1",
"constrain": true,
"height": "",
"src": "",
"width": ""
});
fillAndSubmitWindowForm({
"alt": "alt",
"class": "class1",
"constrain": true,
"height": "200",
"src": "src",
"width": "100"
});
equal(
cleanHtml(editor.getContent()),
'<p><img class="class1" src="src" alt="alt" width="100" height="200" /></p>'
);
});
})();

View File

@ -0,0 +1,212 @@
(function() {
module("tinymce.plugins.ImportCSS", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
skin: false,
plugins: 'importcss',
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
},
teardown: function() {
editor.contentCSS = [];
delete editor.settings.importcss_file_filter;
delete editor.settings.importcss_merge_classes;
delete editor.settings.importcss_append;
delete editor.settings.importcss_selector_filter;
delete editor.settings.importcss_groups;
}
});
function fireFormatsMenuEvent(styleSheets, items) {
return editor.fire('renderFormatsMenu', {
control: tinymce.ui.Factory.create('menu', {items: items}).renderTo(document.getElementById('view')),
doc: {
styleSheets: styleSheets
}
});
}
function getMenuItemFormat(item) {
return editor.formatter.get(item.settings.format)[0];
}
test("Import content_css files", function() {
editor.contentCSS.push("test1.css");
editor.contentCSS.push("test2.css");
var evt = fireFormatsMenuEvent([
{
href: 'test1.css',
cssRules: [
{selectorText: '.a'},
{selectorText: 'p.b'},
{selectorText: 'img.c'}
]
},
{href: 'test2.css', cssRules: [{selectorText: '.d'}]},
{href: 'test3.css', cssRules: [{selectorText: '.e'}]}
]);
equal(evt.control.items().length, 4);
equal(evt.control.items()[0].text(), 'a');
equal(getMenuItemFormat(evt.control.items()[0]).classes, 'a');
equal(evt.control.items()[1].text(), 'p.b');
equal(getMenuItemFormat(evt.control.items()[1]).block, 'p');
equal(getMenuItemFormat(evt.control.items()[1]).classes, 'b');
equal(evt.control.items()[2].text(), 'img.c');
equal(getMenuItemFormat(evt.control.items()[2]).selector, 'img');
equal(getMenuItemFormat(evt.control.items()[2]).classes, 'c');
equal(evt.control.items()[3].text(), 'd');
});
test("Import custom importcss_merge_classes: false", function() {
editor.contentCSS.push("test.css");
editor.settings.importcss_merge_classes = false;
var evt = fireFormatsMenuEvent([
{href: 'test.css', cssRules: [{selectorText: '.a'}]}
]);
equal(evt.control.items().length, 1);
deepEqual(getMenuItemFormat(evt.control.items()[0]).attributes, {"class": "a"});
});
test("Import custom importcss_append: true", function() {
editor.contentCSS.push("test.css");
editor.settings.importcss_append = true;
var evt = fireFormatsMenuEvent([
{href: 'test.css', cssRules: [{selectorText: '.b'}]}
], {text: 'a'});
equal(evt.control.items().length, 2);
equal(evt.control.items()[0].text(), 'a');
equal(evt.control.items()[1].text(), 'b');
});
test("Import custom importcss_selector_filter (string)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_selector_filter = ".a";
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [
{selectorText: '.a'},
{selectorText: '.b'}
]},
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'a');
});
test("Import custom importcss_selector_filter (function)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_selector_filter = function(selector) {
return selector === ".a";
};
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [
{selectorText: '.a'},
{selectorText: '.b'}
]},
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'a');
});
test("Import custom importcss_selector_filter (regexp)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_selector_filter = /a/;
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [
{selectorText: '.a'},
{selectorText: '.b'}
]},
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'a');
});
test("Import custom importcss_groups", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_groups = [
{title: 'g1', filter: /a/},
{title: 'g2', filter: /b/},
{title: 'g3'}
];
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [
{selectorText: '.a'},
{selectorText: '.b'},
{selectorText: '.c'}
]},
]);
equal(evt.control.items().length, 3);
equal(evt.control.items()[0].text(), 'g1');
equal(evt.control.items()[0].settings.menu[0].text, 'a');
equal(evt.control.items()[1].text(), 'g2');
equal(evt.control.items()[1].settings.menu[0].text, 'b');
equal(evt.control.items()[2].text(), 'g3');
equal(evt.control.items()[2].settings.menu[0].text, 'c');
});
test("Import custom importcss_file_filter (string)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_file_filter = "test2.css";
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [{selectorText: '.a'}]},
{href: 'test2.css', cssRules: [{selectorText: '.b'}]}
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'b');
});
test("Import custom importcss_file_filter (function)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_file_filter = function(href) {
return href === "test2.css";
};
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [{selectorText: '.a'}]},
{href: 'test2.css', cssRules: [{selectorText: '.b'}]}
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'b');
});
test("Import custom importcss_file_filter (regexp)", function() {
editor.contentCSS.push("test1.css");
editor.settings.importcss_file_filter = /test2\.css/;
var evt = fireFormatsMenuEvent([
{href: 'test1.css', cssRules: [{selectorText: '.a'}]},
{href: 'test2.css', cssRules: [{selectorText: '.b'}]}
]);
equal(evt.control.items().length, 1);
equal(evt.control.items()[0].text(), 'b');
});
})();

View File

@ -0,0 +1,162 @@
(function() {
module("tinymce.plugins.Link", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: 'textarea',
add_unload_trigger: false,
skin: false,
indent: false,
plugins: "link",
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
},
teardown: function() {
delete editor.settings.file_browser_callback;
delete editor.settings.link_list;
delete editor.settings.link_class_list;
delete editor.settings.link_target_list;
delete editor.settings.rel_list;
var win = Utils.getFontmostWindow();
if (win) {
win.close();
}
}
});
function cleanHtml(html) {
return Utils.cleanHtml(html).replace(/<p>(&nbsp;|<br[^>]+>)<\/p>$/, '');
}
function fillAndSubmitWindowForm(data) {
var win = Utils.getFontmostWindow();
win.fromJSON(data);
win.find('form')[0].submit();
win.close();
}
test('Default link dialog on empty editor', function() {
editor.setContent('');
editor.execCommand('mceLink', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"href": "",
"target": "",
"text": "",
"title": ""
});
fillAndSubmitWindowForm({
"href": "href",
"target": "_blank",
"text": "text",
"title": "title"
});
equal(
cleanHtml(editor.getContent()),
'<p><a title="title" href="href" target="_blank">text</a></p>'
);
});
test('Default link dialog on text selection', function() {
editor.setContent('<p>abc</p>');
Utils.setSelection('p', 1, 'p', 2);
editor.execCommand('mceLink', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"href": "",
"target": "",
"text": "b",
"title": ""
});
fillAndSubmitWindowForm({
"href": "href",
"target": "_blank",
"title": "title"
});
equal(
cleanHtml(editor.getContent()),
'<p>a<a title="title" href="href" target="_blank">b</a>c</p>'
);
});
test('Default link dialog on non pure text selection', function() {
editor.setContent('<p>a</p><p>bc</p>');
Utils.setSelection('p:nth-child(1)', 0, 'p:nth-child(2)', 2);
editor.execCommand('mceLink', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"href": "",
"target": "",
"title": ""
});
fillAndSubmitWindowForm({
"href": "href",
"target": "_blank",
"title": "title"
});
equal(
cleanHtml(editor.getContent()),
'<p><a title="title" href="href" target="_blank">a</a></p>' +
'<p><a title="title" href="href" target="_blank">bc</a></p>'
);
});
test('All lists link dialog on empty editor', function() {
editor.settings.link_list = [
{title: 'link1', value: 'link1'},
{title: 'link2', value: 'link2'}
];
editor.settings.link_class_list = [
{title: 'class1', value: 'class1'},
{title: 'class2', value: 'class2'}
];
editor.settings.target_list = [
{title: 'target1', value: 'target1'},
{title: 'target2', value: 'target2'}
];
editor.settings.rel_list = [
{title: 'rel1', value: 'rel1'},
{title: 'rel2', value: 'rel2'}
];
editor.setContent('');
editor.execCommand('mceLink', true);
deepEqual(Utils.getFontmostWindow().toJSON(), {
"class": "class1",
"href": "",
"rel": "rel1",
"target": "target1",
"text": "",
"title": ""
});
fillAndSubmitWindowForm({
"href": "href",
"text": "text",
"title": "title"
});
equal(
cleanHtml(editor.getContent()),
'<p><a class="class1" title="title" href="href" target="target1" rel="rel1">text</a></p>'
);
});
})();

View File

@ -22,7 +22,7 @@ module("tinymce.plugins.Lists", {
indent: false,
schema: 'html5',
entities: 'raw',
valid_elements: 'li,ol,ul,em,strong,span,#p,div,br',
valid_elements: 'li,ol,ul,dl,dt,dd,em,strong,span,#p,div,br',
valid_styles: {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left'
},
@ -1360,6 +1360,31 @@ test('Indent second LI to same level as nested LI 2', function() {
equal(editor.selection.getNode().nodeName, 'LI');
});
test('Indent second and third LI', function() {
editor.getBody().innerHTML = trimBrs(
'<ul>' +
'<li>a</li>' +
'<li>b</li>' +
'<li>c</li>' +
'</ul>'
);
editor.focus();
Utils.setSelection('li:nth-child(2)', 0, 'li:last', 0);
execCommand('Indent');
equal(editor.getContent(),
'<ul>' +
'<li>a' +
'<ul>' +
'<li>b</li>' +
'<li>c</li>' +
'</ul>' +
'</li>' +
'</ul>'
);
});
// Backspace
test('Backspace at beginning of single LI in UL', function() {
@ -1749,3 +1774,102 @@ test('Backspace in LI in UL in inline body element contained within LI', functio
inlineEditor.plugins.lists.backspaceDelete();
equal(inlineEditor.getContent(), '<p>a</p>');
});
test('Apply DL list to multiple Ps', function() {
editor.getBody().innerHTML = trimBrs(
'<p>a</p>' +
'<p>b</p>' +
'<p>c</p>'
);
editor.focus();
Utils.setSelection('p', 0, 'p:last', 0);
execCommand('InsertDefinitionList');
equal(editor.getContent(),
'<dl>' +
'<dt>a</dt>' +
'<dt>b</dt>' +
'<dt>c</dt>' +
'</dl>'
);
equal(editor.selection.getStart().nodeName, 'DT');
});
test('Apply OL list to single P', function() {
editor.getBody().innerHTML = trimBrs(
'<p>a</p>'
);
editor.focus();
Utils.setSelection('p', 0);
execCommand('InsertDefinitionList');
equal(editor.getContent(),'<dl><dt>a</dt></dl>');
equal(editor.selection.getNode().nodeName, 'DT');
});
test('Apply DL to P and merge with adjacent lists', function() {
editor.getBody().innerHTML = trimBrs(
'<dl>' +
'<dt>a</dt>' +
'</dl>' +
'<p>b</p>' +
'<dl>' +
'<dt>c</dt>' +
'</dl>'
);
editor.focus();
Utils.setSelection('p', 1);
execCommand('InsertDefinitionList');
equal(editor.getContent(),
'<dl>' +
'<dt>a</dt>' +
'<dt>b</dt>' +
'<dt>c</dt>' +
'</dl>'
);
equal(editor.selection.getStart().nodeName, 'DT');
});
test('Indent single DT in DL', function() {
editor.getBody().innerHTML = trimBrs(
'<dl>' +
'<dt>a</dt>' +
'</dl>'
);
editor.focus();
Utils.setSelection('dt', 0);
execCommand('Indent');
equal(editor.getContent(),
'<dl>' +
'<dd>a</dd>' +
'</dl>'
);
equal(editor.selection.getNode().nodeName, 'DD');
});
test('Outdent single DD in DL', function() {
editor.getBody().innerHTML = trimBrs(
'<dl>' +
'<dd>a</dd>' +
'</dl>'
);
editor.focus();
Utils.setSelection('dd', 1);
execCommand('Outdent');
equal(editor.getContent(),
'<dl>' +
'<dt>a</dt>' +
'</dl>'
);
equal(editor.selection.getNode().nodeName, 'DT');
});

View File

@ -40,14 +40,12 @@ test("Object retain as is", function() {
test("Embed retain as is", function() {
editor.setContent(
'<video src="320x240.ogg" autoplay loop controls>text<a href="#">link</a></video>'
'<embed src="320x240.ogg" width="100" height="200">text<a href="#">link</a></embed>'
);
equal(editor.getContent(),
// IE produces a different attribute order for some odd reason, I love IE
tinymce.isIE ?
'<p><video width="300" height="150" controls="controls" loop="loop" autoplay="autoplay" src="320x240.ogg">text<a href="#">link</a></video></p>' :
'<p><video width="300" height="150" src="320x240.ogg" autoplay="autoplay" loop="loop" controls="controls">text<a href="#">link</a></video></p>'
equal(
editor.getContent(),
'<p><embed src="320x240.ogg" width="100" height="200"></embed>text<a href="#">link</a></p>'
);
});
@ -56,11 +54,9 @@ test("Video retain as is", function() {
'<video src="320x240.ogg" autoplay loop controls>text<a href="#">link</a></video>'
);
equal(editor.getContent(),
// IE produces a different attribute order for some odd reason, I love IE
tinymce.isIE ?
'<p><video width="300" height="150" controls="controls" loop="loop" autoplay="autoplay" src="320x240.ogg">text<a href="#">link</a></video></p>' :
'<p><video width="300" height="150" src="320x240.ogg" autoplay="autoplay" loop="loop" controls="controls">text<a href="#">link</a></video></p>'
equal(
editor.getContent(),
'<p><video src="320x240.ogg" autoplay="autoplay" loop="loop" controls="controls" width="300" height="150">text<a href="#">link</a></video></p>'
);
});
@ -114,7 +110,7 @@ test("Resize complex object", function() {
equal(editor.getContent(),
'<p>' +
'<video width="100" height="200" controls="controls">' +
'<video controls="controls" width="100" height="200">' +
'<source src="s" />' +
'<object type="application/x-shockwave-flash" data="../../js/tinymce/plugins/media/moxieplayer.swf" width="100" height="200">' +
'<param name="allowfullscreen" value="true" />' +

File diff suppressed because one or more lines are too long

View File

@ -1,314 +1,343 @@
module("tinymce.plugins.Table", {
setupModule: function() {
QUnit.stop();
(function() {
module("tinymce.plugins.Table", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
skin: false,
plugins: 'table',
valid_styles: {
'*' : 'width,height,text-align,float'
},
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
skin: false,
indent: false,
plugins: 'table',
valid_styles: {
'*' : 'width,height,vertical-align,text-align,float'
},
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
}
});
function fillAndSubmitWindowForm(data) {
var win = Utils.getFontmostWindow();
win.fromJSON(data);
win.find('form')[0].submit();
win.close();
}
});
function fillAndSubmitWindowForm(data) {
var win = Utils.getFontmostWindow();
function cleanTableHtml(html) {
return Utils.cleanHtml(html).replace(/<p>(&nbsp;|<br[^>]+>)<\/p>$/, '');
}
win.fromJSON(data);
win.find('form')[0].submit();
win.close();
}
test("Table properties dialog (get data from plain table)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
function cleanTableHtml(html) {
return Utils.cleanHtml(html).replace(/<p>(&nbsp;|<br[^>]+>)<\/p>$/, '');
}
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"border": "",
"caption": false,
"cellpadding": "",
"cellspacing": "",
"height": "",
"width": ""
});
test("Table properties dialog (get data from plain table)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"border": "",
"caption": false,
"cellpadding": "",
"cellspacing": "",
"height": "",
"width": ""
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table properties dialog (get data from full table)", function() {
editor.setContent(
'<table style="width: 100px; height: 101px;" border="4" cellspacing="2" cellpadding="3">' +
'<caption>&nbsp;</caption>' +
'<tbody>' +
'<tr>' +
'<td>&nbsp;</td>' +
'</tr>' +
'</tbody>' +
'</table>'
);
test("Table properties dialog (get data from full table)", function() {
editor.setContent(
'<table style="width: 100px; height: 101px;" border="4" cellspacing="2" cellpadding="3">' +
'<caption>&nbsp;</caption>' +
'<tbody>' +
'<tr>' +
'<td>&nbsp;</td>' +
'</tr>' +
'</tbody>' +
'</table>'
);
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"border": "4",
"caption": true,
"cellpadding": "3",
"cellspacing": "2",
"height": "101",
"width": "100"
});
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"border": "4",
"caption": true,
"cellpadding": "3",
"cellspacing": "2",
"height": "101",
"width": "100"
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table properties dialog (add caption)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
caption: true
});
test("Table properties dialog (add caption)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
caption: true
equal(
cleanTableHtml(editor.getContent()),
'<table><caption>&nbsp;</caption><tbody><tr><td>x</td></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table><caption>&nbsp;</caption><tbody><tr><td>x</td></tr></tbody></table>'
);
});
test("Table properties dialog (remove caption)", function() {
editor.setContent('<table><caption>&nbsp;</caption><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
caption: false
});
test("Table properties dialog (remove caption)", function() {
editor.setContent('<table><caption>&nbsp;</caption><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
caption: false
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><td>x</td></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><td>x</td></tr></tbody></table>'
);
});
test("Table properties dialog (change size in pixels)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
width: 100,
height: 101
});
test("Table properties dialog (change size in pixels)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
width: 100,
height: 101
equal(
cleanTableHtml(editor.getContent()),
'<table style="width: 100px; height: 101px;"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table style="width: 100px; height: 101px;"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
test("Table properties dialog (change size in %)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
width: "100%",
height: "101%"
});
test("Table properties dialog (change size in %)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
width: "100%",
height: "101%"
equal(
cleanTableHtml(editor.getContent()),
'<table style="width: 100%; height: 101%;"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table style="width: 100%; height: 101%;"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
test("Table properties dialog (change: border,cellpadding,cellspacing,align)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
border: "1",
cellpadding: "2",
cellspacing: "3",
align: "right"
});
test("Table properties dialog (change: border,cellpadding,cellspacing,align)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceInsertTable');
fillAndSubmitWindowForm({
border: "1",
cellpadding: "2",
cellspacing: "3",
align: "right"
equal(
cleanTableHtml(editor.getContent()),
'<table style="float: right;" border="1" cellspacing="3" cellpadding="2"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table style="float: right;" border="1" cellspacing="3" cellpadding="2"><tbody><tr><td>x</td></tr></tbody></table>'
);
});
test("Table cell properties dialog (get data from plain cell)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableCellProps');
test("Table cell properties dialog (get data from plain cell)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableCellProps');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"valign": "",
"height": "",
"scope": "",
"type": "td",
"width": ""
});
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"height": "",
"scope": "",
"type": "td",
"width": ""
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table cell properties dialog (get data from complex cell)", function() {
editor.setContent('<table><tr><th style="text-align: right; vertical-align: top; width: 10px; height: 11px" scope="row">X</th></tr></table>');
Utils.setSelection('th', 0);
editor.execCommand('mceTableCellProps');
test("Table cell properties dialog (get data from complex cell)", function() {
editor.setContent('<table><tr><th style="text-align: right; width: 10px; height: 11px" scope="row">X</th></tr></table>');
Utils.setSelection('th', 0);
editor.execCommand('mceTableCellProps');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "right",
"valign": "top",
"height": "11",
"scope": "row",
"type": "th",
"width": "10"
});
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "right",
"height": "11",
"scope": "row",
"type": "th",
"width": "10"
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table cell properties dialog (update all)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableCellProps');
test("Table cell properties dialog (update all)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableCellProps');
fillAndSubmitWindowForm({
"align": "right",
"height": "11",
"scope": "row",
"type": "th",
"width": "10"
});
fillAndSubmitWindowForm({
"align": "right",
"height": "11",
"scope": "row",
"type": "th",
"width": "10"
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><th style="width: 10px; height: 11px; text-align: right;" scope="row">x</th></tr></tbody></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><th style="width: 10px; height: 11px; text-align: right;" scope="row">x</th></tr></tbody></table>'
);
});
test("Table row properties dialog (get data from plain cell)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
test("Table row properties dialog (get data from plain cell)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"height": "",
"type": "tbody"
});
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "",
"height": "",
"type": "tbody"
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table row properties dialog (get data from complex cell)", function() {
editor.setContent('<table><thead><tr style="height: 10px; text-align: right"><td>X</td></tr></thead></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
test("Table row properties dialog (get data from complex cell)", function() {
editor.setContent('<table><thead><tr style="height: 10px; text-align: right"><td>X</td></tr></thead></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "right",
"height": "10",
"type": "thead"
});
deepEqual(Utils.getFontmostWindow().toJSON(), {
"align": "right",
"height": "10",
"type": "thead"
Utils.getFontmostWindow().close();
});
Utils.getFontmostWindow().close();
});
test("Table row properties dialog (update all)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
test("Table row properties dialog (update all)", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableRowProps');
fillAndSubmitWindowForm({
"align": "right",
"height": "10",
"type": "thead"
});
fillAndSubmitWindowForm({
"align": "right",
"height": "10",
"type": "thead"
equal(
cleanTableHtml(editor.getContent()),
'<table><thead><tr style="height: 10px; text-align: right;"><td>x</td></tr></thead></table>'
);
});
equal(
cleanTableHtml(editor.getContent()),
'<table><thead><tr style="height: 10px; text-align: right;"><td>x</td></tr></thead></table>'
);
});
test("mceTableDelete command", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDelete');
equal(cleanTableHtml(editor.getContent()), '');
});
test("mceTableDelete command", function() {
editor.setContent('<table><tr><td>X</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDelete');
equal(cleanTableHtml(editor.getContent()), '');
});
test("mceTableDeleteCol command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDeleteCol');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>2</td></tr></tbody></table>');
});
test("mceTableDeleteCol command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDeleteCol');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>2</td></tr></tbody></table>');
});
test("mceTableDeleteRow command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDeleteRow');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>2</td></tr></tbody></table>');
});
test("mceTableDeleteRow command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableDeleteRow');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>2</td></tr></tbody></table>');
});
test("mceTableInsertColAfter command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertColAfter');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>1</td><td>&nbsp;</td></tr><tr><td>2</td><td>&nbsp;</td></tr></tbody></table>');
});
test("mceTableInsertColAfter command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertColAfter');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>1</td><td>&nbsp;</td></tr><tr><td>2</td><td>&nbsp;</td></tr></tbody></table>');
});
test("mceTableInsertColBefore command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertColBefore');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>&nbsp;</td><td>1</td></tr><tr><td>&nbsp;</td><td>2</td></tr></tbody></table>');
});
test("mceTableInsertColBefore command", function() {
editor.setContent('<table><tr><td>1</td></tr><tr><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertColBefore');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>&nbsp;</td><td>1</td></tr><tr><td>&nbsp;</td><td>2</td></tr></tbody></table>');
});
test("mceTableInsertRowAfter command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertRowAfter');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>1</td><td>2</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table>');
});
test("mceTableInsertRowAfter command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertRowAfter');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>1</td><td>2</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table>');
});
test("mceTableInsertRowBefore command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertRowBefore');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>1</td><td>2</td></tr></tbody></table>');
});
test("mceTableInsertRowBefore command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableInsertRowBefore');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>1</td><td>2</td></tr></tbody></table>');
});
test("mceTableMergeCells command with cell selection", function() {
editor.setContent('<table><tr><td class="mce-item-selected">1</td><td class="mce-item-selected">2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableMergeCells');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td colspan="2">12</td></tr></tbody></table>');
});
test("mceTableMergeCells command with cell selection", function() {
editor.setContent('<table><tr><td class="mce-item-selected">1</td><td class="mce-item-selected">2</td></tr></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableMergeCells');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td colspan="2">12</td></tr></tbody></table>');
});
test("mceTableSplitCells command", function() {
editor.setContent('<table><tbody><tr><td colspan="2">12</td></tr></tbody></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableSplitCells');
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><td>12</td><td>&nbsp;</td></tr></tbody></table>'
);
});
test("mceTableSplitCells command", function() {
editor.setContent('<table><tbody><tr><td colspan="2">12</td></tr></tbody></table>');
Utils.setSelection('td', 0);
editor.execCommand('mceTableSplitCells');
equal(
cleanTableHtml(editor.getContent()),
'<table><tbody><tr><td>12</td><td>&nbsp;</td></tr></tbody></table>'
);
});
test("Tab key navigation", function() {
editor.setContent('<table><tbody><tr><td>A1</td><td>A2</td></tr><tr><td>B1</td><td>B2</td></tr></tbody></table><p>x</p>');
Utils.setSelection('td', 0);
editor.fire('keydown', {keyCode: 9});
equal(editor.selection.getStart().innerHTML, 'A2');
Utils.setSelection('td', 0);
editor.fire('keydown', {keyCode: 9, shiftKey: true});
equal(editor.selection.getStart().innerHTML, 'A1');
Utils.setSelection('td:nth-child(2)', 0);
editor.fire('keydown', {keyCode: 9, shiftKey: true});
equal(editor.selection.getStart().innerHTML, 'A1');
Utils.setSelection('tr:nth-child(2) td:nth-child(2)', 0);
editor.fire('keydown', {keyCode: 9});
equal(editor.selection.getStart().nodeName, 'TD');
equal(
editor.getContent(),
'<table><tbody><tr><td>A1</td><td>A2</td></tr><tr><td>B1</td><td>B2</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table><p>x</p>'
);
});
})();

View File

@ -1,9 +1,10 @@
module("tinymce.Editor", {
setupModule: function() {
document.getElementById('view').innerHTML = '<textarea id="elm1"></textarea><div id="elm2"></div>';
QUnit.stop();
tinymce.init({
selector: "textarea",
selector: "#elm1",
add_unload_trigger: false,
disable_nodechange: true,
skin: false,
@ -16,9 +17,35 @@ module("tinymce.Editor", {
extended_valid_elements: 'custom1,custom2',
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
if (window.inlineEditor) {
QUnit.start();
}
}
});
tinymce.init({
selector: "#elm2",
add_unload_trigger: false,
disable_nodechange: true,
skin: false,
entities: 'raw',
indent: false,
inline: true,
init_instance_callback: function(ed) {
window.inlineEditor = ed;
if (window.editor) {
QUnit.start();
}
}
});
},
teardown: function() {
Utils.unpatch(editor.getDoc());
inlineEditor.show();
editor.show();
}
});
@ -197,3 +224,129 @@ test('Store/restore tabindex', function() {
equal(editor.getContent({format:'raw'}).toLowerCase(), '<p><span data-mce-tabindex="42">abc</span></p>');
equal(editor.getContent(), '<p><span tabindex="42">abc</span></p>');
});
test('show/hide/isHidden and events', function() {
var lastEvent;
editor.on('show hide', function(e) {
lastEvent = e;
});
equal(editor.isHidden(), false, 'Initial isHidden state');
editor.hide();
equal(editor.isHidden(), true, 'After hide isHidden state');
equal(lastEvent.type, "hide");
lastEvent = null;
editor.hide();
strictEqual(lastEvent, null);
editor.show();
equal(editor.isHidden(), false, 'After show isHidden state');
equal(lastEvent.type, "show");
lastEvent = null;
editor.show();
strictEqual(lastEvent, null);
});
test('show/hide/isHidden and events (inline)', function() {
var lastEvent;
inlineEditor.on('show hide', function(e) {
lastEvent = e;
});
equal(inlineEditor.isHidden(), false, 'Initial isHidden state');
inlineEditor.hide();
equal(inlineEditor.isHidden(), true, 'After hide isHidden state');
equal(lastEvent.type, "hide");
strictEqual(inlineEditor.getBody().contentEditable, "false", "ContentEditable after hide");
lastEvent = null;
inlineEditor.hide();
strictEqual(lastEvent, null);
inlineEditor.show();
equal(inlineEditor.isHidden(), false, 'After show isHidden state');
equal(lastEvent.type, "show");
strictEqual(inlineEditor.getBody().contentEditable, "true", "ContentEditable after show");
lastEvent = null;
inlineEditor.show();
strictEqual(lastEvent, null);
});
test('hide save content and hidden state while saving', function() {
var lastEvent, hiddenStateWhileSaving;
editor.on('SaveContent', function(e) {
lastEvent = e;
hiddenStateWhileSaving = editor.isHidden();
});
editor.setContent('xyz');
editor.hide();
strictEqual(hiddenStateWhileSaving, false, 'False isHidden state while saving');
strictEqual(lastEvent.content, '<p>xyz</p>');
strictEqual(document.getElementById('elm1').value, '<p>xyz</p>');
});
asyncTest('remove editor', function() {
document.getElementById('view').appendChild(tinymce.DOM.create('textarea', {id: 'elmx'}));
tinymce.init({
selector: "#elmx",
add_unload_trigger: false,
disable_nodechange: true,
skin: false,
init_instance_callback: function(editor) {
window.setTimeout(function() {
var lastEvent;
editor.on('SaveContent', function(e) {
lastEvent = e;
});
editor.setContent('xyz');
editor.remove();
QUnit.start();
strictEqual(lastEvent.content, '<p>xyz</p>');
strictEqual(document.getElementById('elmx').value, '<p>xyz</p>');
}, 0);
}
});
});
test('insertContent', function() {
editor.setContent('<p>a</p>');
Utils.setSelection('p', 1);
editor.insertContent('b');
equal(editor.getContent(), '<p>ab</p>');
});
test('insertContent merge', function() {
editor.setContent('<p><strong>a</strong></p>');
Utils.setSelection('p', 1);
editor.insertContent('<em><strong>b</strong></em>', {merge: true});
equal(editor.getContent(), '<p><strong>a<em>b</em></strong></p>');
});
test('execCommand return values for native commands', function() {
var lastCmd;
strictEqual(editor.execCommand("NonExistingCommand"), false, "Return value for a completely unhandled command");
Utils.patch(editor.getDoc(), 'execCommand', function(orgFunc, cmd) {
lastCmd = cmd;
return true;
});
strictEqual(editor.execCommand("ExistingCommand"), true, "Return value for an editor handled command");
strictEqual(lastCmd, "ExistingCommand");
});

View File

@ -311,6 +311,27 @@ test('mceInsertContent - block element with space before/after at middle of bloc
equal(editor.getContent(), '<p>a</p><p>b</p><p>c</p>');
});
test('mceInsertContent - strong in strong', function() {
editor.getBody().innerHTML = '<strong>ac</strong>';
Utils.setSelection('strong', 1);
editor.execCommand('mceInsertContent', false, {content: '<strong>b</strong>', merge: true});
equal(editor.getContent(), '<p><strong>abc</strong></p>');
});
test('mceInsertContent - span in span same style color', function() {
editor.getBody().innerHTML = '<span style="color:#ff0000">ac</strong>';
Utils.setSelection('span', 1);
editor.execCommand('mceInsertContent', false, {content: '<span style="color:#ff0000">b</span>', merge: true});
equal(editor.getContent(), '<p><span style="color: #ff0000;">abc</span></p>');
});
test('mceInsertContent - span in span different style color', function() {
editor.getBody().innerHTML = '<span style="color:#ff0000">ac</strong>';
Utils.setSelection('span', 1);
editor.execCommand('mceInsertContent', false, {content: '<span style="color:#00ff00">b</span>', merge: true});
equal(editor.getContent(), '<p><span style="color: #ff0000;">a<span style="color: #00ff00;">b</span>c</span></p>');
});
test('InsertHorizontalRule', function() {
var rng;
@ -720,4 +741,21 @@ test('RemoveFormat', function() {
editor.execCommand('SelectAll');
editor.execCommand('RemoveFormat');
equal(editor.getContent(), '<p>dfn tag code tag samp tag kbd tag var tag cite tag mark tag q tag</p>');
});
});
test('InsertLineBreak', function() {
editor.setContent('<p>123</p>');
Utils.setSelection('p', 2);
editor.execCommand('InsertLineBreak');
equal(editor.getContent(), '<p>12<br />3</p>');
editor.setContent('<p>123</p>');
Utils.setSelection('p', 0);
editor.execCommand('InsertLineBreak');
equal(editor.getContent(), '<p><br />123</p>');
editor.setContent('<p>123</p>');
Utils.setSelection('p', 3);
editor.execCommand('InsertLineBreak');
equal(Utils.cleanHtml(editor.getBody().innerHTML), (tinymce.isIE && tinymce.Env.ie < 11) ? '<p>123<br></p>': '<p>123<br><br></p>');
});

View File

@ -49,6 +49,32 @@ test('Re-init on same id', function() {
strictEqual(tinymce.get().length, 1);
});
asyncTest('Externally destroyed editor', function() {
tinymce.remove();
tinymce.init({
selector: "textarea",
init_instance_callback: function(editor1) {
setTimeout(function() {
// Destroy the editor by setting innerHTML common ajax pattern
document.getElementById('view').innerHTML = '<textarea id="' + editor1.id + '"></textarea>';
// Re-init the editor will have the same id
tinymce.init({
selector: "textarea",
init_instance_callback: function(editor2) {
QUnit.start();
strictEqual(tinymce.get().length, 1);
strictEqual(editor1.id, editor2.id);
ok(editor1.destroyed, "First editor instance should be destroyed");
}
});
}, 0);
}
});
});
asyncTest('Init/remove on same id', function() {
var textArea = document.createElement('textarea');
document.getElementById('view').appendChild(textArea);

View File

@ -10,7 +10,7 @@ module("tinymce.EnterKey", {
skin: false,
entities: 'raw',
schema: 'html5',
extended_valid_elements: 'div[id|style|contenteditable],span[id|style|contenteditable]',
extended_valid_elements: 'div[id|style|contenteditable],span[id|style|contenteditable],#dt,#dd',
valid_styles: {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left'
},
@ -552,7 +552,69 @@ test('Enter inside empty LI in end of OL in OL', function() {
equal(editor.selection.getNode().nodeName, 'LI');
});
test('Enter at beginning of first DT inside DL', function() {
editor.getBody().innerHTML = '<dl><dt>a</dt></dl>';
Utils.setSelection('dt', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dt>\u00a0</dt><dt>a</dt></dl>');
equal(editor.selection.getNode().nodeName, 'DT');
});
test('Enter at beginning of first DD inside DL', function() {
editor.getBody().innerHTML = '<dl><dd>a</dd></dl>';
Utils.setSelection('dd', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dd>\u00a0</dd><dd>a</dd></dl>');
equal(editor.selection.getNode().nodeName, 'DD');
});
test('Enter at beginning of middle DT inside DL', function() {
editor.getBody().innerHTML = '<dl><dt>a</dt><dt>b</dt><dt>c</dt></dl>';
Utils.setSelection('dt:nth-child(2)', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dt>a</dt><dt>\u00a0</dt><dt>b</dt><dt>c</dt></dl>');
equal(editor.selection.getNode().nodeName, 'DT');
});
test('Enter at beginning of middle DD inside DL', function() {
editor.getBody().innerHTML = '<dl><dd>a</dd><dd>b</dd><dd>c</dd></dl>';
Utils.setSelection('dd:nth-child(2)', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dd>a</dd><dd>\u00a0</dd><dd>b</dd><dd>c</dd></dl>');
equal(editor.selection.getNode().nodeName, 'DD');
});
test('Enter at end of last DT inside DL', function() {
editor.getBody().innerHTML = '<dl><dt>a</dt></dl>';
Utils.setSelection('dt', 1);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dt>a</dt><dt>\u00a0</dt></dl>');
equal(editor.selection.getNode().nodeName, 'DT');
});
test('Enter at end of last DD inside DL', function() {
editor.getBody().innerHTML = '<dl><dd>a</dd></dl>';
Utils.setSelection('dd', 1);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dd>a</dd><dd>\u00a0</dd></dl>');
equal(editor.selection.getNode().nodeName, 'DD');
});
test('Enter at end of last empty DT inside DL', function() {
editor.getBody().innerHTML = '<dl><dt>a</dt><dt></dt></dl>';
Utils.setSelection('dt:nth-child(2)', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dt>a</dt></dl><p>\u00a0</p>');
equal(editor.selection.getNode().nodeName, 'P');
});
test('Enter at end of last empty DD inside DL', function() {
editor.getBody().innerHTML = '<dl><dd>a</dd><dd></dd></dl>';
Utils.setSelection('dd:nth-child(2)', 0);
Utils.pressEnter();
equal(editor.getContent(),'<dl><dd>a</dd></dl><p>\u00a0</p>');
equal(editor.selection.getNode().nodeName, 'P');
});
test('Enter at beginning of P inside LI', function() {
editor.getBody().innerHTML = '<ol><li><p>abcd</p></li></ol>';
@ -928,6 +990,19 @@ test('Enter when forced_root_block: false and force_p_newlines: true', function(
equal(editor.getContent(),'<p>te</p><p>xt</p>');
});
test('Enter at end of br line', function() {
editor.settings.forced_root_block = false;
editor.settings.force_p_newlines = true;
editor.getBody().innerHTML = '<p>a<br>b</p>';
Utils.setSelection('p', 1);
Utils.pressEnter();
equal(editor.getContent(), '<p>a</p><p><br />b</p>');
var rng = editor.selection.getRng(true);
equal(rng.startContainer.nodeName, 'P');
equal(rng.startContainer.childNodes[rng.startOffset].nodeName, 'BR');
});
// Ignore on IE 7, 8 this is a known bug not worth fixing
if (!tinymce.Env.ie || tinymce.Env.ie > 8) {
test('Enter before BR between DIVs', function() {

View File

@ -5,9 +5,7 @@ module("tinymce.Formatter - Apply", {
tinymce.init({
selector: "#elm1",
external_plugins: {
noneditable: '../../../../tests/qunit/editor/external-plugins/noneditable/plugin.min.js'
},
external_plugins: { noneditable: '../../../../tests/qunit/editor/external-plugins/noneditable/plugin.min.js' }, // WP
add_unload_trigger: false,
skin: false,
indent: false,
@ -1597,4 +1595,4 @@ test('Bug #6518 - Apply div blocks to inline editor paragraph', function() {
});
inlineEditor.formatter.apply('format');
equal(inlineEditor.getContent(), '<div>a</div><p>b</p>');
});
});

View File

@ -5,9 +5,7 @@ module("tinymce.Formatter - Remove", {
tinymce.init({
selector: "textarea",
external_plugins: {
noneditable: '../../../../tests/qunit/editor/external-plugins/noneditable/plugin.min.js'
},
external_plugins: { noneditable: '../../../../tests/qunit/editor/external-plugins/noneditable/plugin.min.js' }, // WP
indent: false,
add_unload_trigger: false,
skin: false,
@ -313,6 +311,16 @@ test('Caret format at end of text inside other format with text after 2', functi
equal(editor.getContent(), '<p><em><b>abc</b></em><b>d</b>e</p>');
});
test('Toggle styles at the end of the content don\' removes the format where it is not needed.', function() {
editor.setContent('<p><em><b>abce</b></em></p>');
editor.formatter.register('b', {inline: 'b'});
editor.formatter.register('em', {inline: 'em'});
Utils.setSelection('b', 4, 'b', 4);
editor.formatter.remove('b');
editor.formatter.remove('em');
equal(editor.getContent(), '<p><em><b>abce</b></em></p>');
});
test('Caret format on second word in table cell', function() {
editor.setContent('<table><tbody><tr><td>one <b>two</b></td></tr></tbody></table>');
editor.formatter.register('format', {inline: 'b'});

View File

@ -0,0 +1,49 @@
module("tinymce.Shortcuts", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
disable_nodechange: true,
indent: false,
skin: false,
entities: 'raw',
schema: 'html5',
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
}
});
test('Shortcuts formats', function() {
function assertShortcut(shortcut, args) {
var called;
editor.shortcuts.add(shortcut, '', function() {
called = true;
});
args = tinymce.extend({
ctrlKey: false,
altKey: false,
shiftKey: false
}, args);
if (tinymce.Env.mac && args.ctrlKey) {
args.metaKey = true;
args.ctrlKey = false;
}
editor.fire('keydown', args);
ok(called, 'Shortcut wasn\'t called: ' + shortcut);
}
assertShortcut('ctrl+d', {ctrlKey: true, keyCode: 68});
assertShortcut('ctrl+shift+d', {ctrlKey: true, shiftKey: true, keyCode: 68});
assertShortcut('ctrl+shift+alt+d', {ctrlKey: true, shiftKey: true, altKey: true, keyCode: 68});
assertShortcut('ctrl+221', {ctrlKey: true, keyCode: 221});
});

View File

@ -241,15 +241,19 @@ test('Exclude internal elements', function() {
editor.getBody().innerHTML = (
'<span data-mce-bogus="1">\u200B</span>' +
'<span data-mce-bogus="1">\uFEFF</span>' +
'<div data-mce-bogus="1"></div>' +
'<div data-mce-bogus="all"></div>' +
'<div data-mce-bogus="all"><div><b>x</b></div></div>' +
'<img src="about:blank" data-mce-bogus="all">' +
'<br data-mce-bogus="1">' +
'test' +
'<img src="about:blank" />' +
'<table><tr><td>x</td></tr></table>'
);
editor.undoManager.add();
equal(count, 1);
equal(count, 2);
equal(Utils.cleanHtml(lastLevel.content),
'<br data-mce-bogus="1">' +
'test' +
'<img src="about:blank">' +
'<table><tbody><tr><td>x</td></tr></tbody></table>'

View File

@ -169,9 +169,10 @@
equal(e.innerHTML.toLowerCase(), 'content <b>abc</b>');
});
test('createHTML', 4, function() {
equal(DOM.createHTML('span', {'id' : 'id1', 'class' : 'abc 123'}, 'content <b>abc</b>'), '<span id="id1" class="abc 123">content <b>abc</b></span>');
equal(DOM.createHTML('span', {'id' : 'id1', 'class' : 'abc 123'}), '<span id="id1" class="abc 123" />');
test('createHTML', 5, function() {
equal(DOM.createHTML('span', {'id': 'id1', 'class': 'abc 123'}, 'content <b>abc</b>'), '<span id="id1" class="abc 123">content <b>abc</b></span>');
equal(DOM.createHTML('span', {'id': 'id1', 'class': 'abc 123'}), '<span id="id1" class="abc 123" />');
equal(DOM.createHTML('span', {'id': null, 'class': undefined}), '<span />');
equal(DOM.createHTML('span'), '<span />');
equal(DOM.createHTML('span', null, 'content <b>abc</b>'), '<span>content <b>abc</b></span>');
});
@ -313,7 +314,7 @@
DOM.remove('test');
});
test('setGetStyles', 7, function() {
test('setGetStyles', 9, function() {
DOM.add(document.body, 'div', {id : 'test'});
DOM.setStyle('test', 'font-size', '20px');
@ -332,6 +333,12 @@
equal(DOM.getStyle('test3', 'fontSize'), '22px');
equal(DOM.getStyle('test4', 'fontSize'), '22px');
DOM.setStyle('test', 'fontSize', 23);
equal(DOM.getStyle('test', 'fontSize'), '23px', null, tinymce.isWebKit);
DOM.setStyle('test', 'fontSize', '24');
equal(DOM.getStyle('test', 'fontSize'), '24px', null, tinymce.isWebKit);
DOM.setAttrib('test', 'style', '');
DOM.remove('test');

View File

@ -571,6 +571,45 @@ test('normalize to br from document', function() {
// Only run on non IE browsers since it's not an issue on IE
if (!tinymce.isIE) {
test('normalize with contentEditable:false element', function() {
var rng;
editor.setContent('<p>a<b contentEditable="false">b</b>c</p>');
rng = editor.dom.createRng();
rng.setStart(editor.getBody().firstChild.lastChild, 0);
rng.setEnd(editor.getBody().firstChild.lastChild, 0);
editor.selection.setRng(rng);
editor.selection.normalize();
rng = editor.selection.getRng(true);
equal(rng.collapsed, true);
equal(rng.startContainer.nodeType, 3);
equal(rng.startContainer.data, 'c');
});
test('normalize with contentEditable:false parent and contentEditable:true child element', function() {
editor.setContent('<p contentEditable="false">a<em contentEditable="true">b</em></p>');
Utils.setSelection('em', 0);
editor.selection.normalize();
var rng = editor.selection.getRng(true);
equal(rng.collapsed, true);
equal(rng.startContainer.nodeType, 3);
equal(rng.startContainer.data, 'b');
});
test('normalize with contentEditable:true parent and contentEditable:false child element', function() {
editor.setContent('<p contentEditable="true">a<em contentEditable="false">b</em></p>');
Utils.setSelection('em', 0);
editor.selection.normalize();
var rng = editor.selection.getRng(true);
equal(rng.collapsed, true);
equal(rng.startContainer.nodeType, 3);
equal(rng.startContainer.data, 'a');
equal(rng.startOffset, 1);
});
test('normalize to text node from body', function() {
var rng;

View File

@ -293,7 +293,7 @@ test('Script with type attrib and less than', 1, function() {
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<s'+'cript type="text/javascript">1 < 2;</s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<script>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<script type="text/javascript">// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
});
test('Script with whitespace in beginning/end', 1, function() {

View File

@ -162,6 +162,12 @@
deepEqual(countNodes(root), {"body":1, "div":1, "section":1, "p":1, "#text":1}, 'P inside SECTION (count)');
});
test('Remove empty nodes', function() {
parser = new tinymce.html.DomParser({}, new tinymce.html.Schema({valid_elements: '-p,-span[id]'}));
root = parser.parse('<p>a<span></span><span> </span><span id="x">b</span><span id="y"></span></p><p></p><p><span></span></p><p> </p>');
equal(serializer.serialize(root), '<p>a <span id="x">b</span><span id="y"></span></p>');
});
test('addNodeFilter', function() {
var parser, result;
@ -479,5 +485,21 @@
root = parser.parse('<p><span>1</span> <strong>2</strong></p>');
equal(serializer.serialize(root), '<p>1 <strong>2</strong></p>');
});
test('Valid classes', function() {
var parser, root, schema = new tinymce.html.Schema({valid_classes: 'classA classB'});
parser = new tinymce.html.DomParser({}, schema);
root = parser.parse('<p class="classA classB classC">a</p>');
equal(serializer.serialize(root), '<p class="classA classB">a</p>');
});
test('Valid classes multiple elements', function() {
var parser, root, schema = new tinymce.html.Schema({valid_classes: {'*': 'classA classB', 'strong': 'classC'}});
parser = new tinymce.html.DomParser({}, schema);
root = parser.parse('<p class="classA classB classC"><strong class="classA classB classC">a</strong></p>');
equal(serializer.serialize(root), '<p class="classA classB"><strong class="classA classB">a</strong></p>');
});
})();

View File

@ -3,8 +3,8 @@ module("tinymce.html.Entities");
test('encodeRaw', function() {
expect(2);
equal(tinymce.html.Entities.encodeRaw('<>"\'&\u00e5\u00e4\u00f6'), '&lt;&gt;"\'&amp;\u00e5\u00e4\u00f6', 'Raw encoding text');
equal(tinymce.html.Entities.encodeRaw('<>"\'&\u00e5\u00e4\u00f6', true), '&lt;&gt;&quot;\'&amp;\u00e5\u00e4\u00f6', 'Raw encoding attribute');
equal(tinymce.html.Entities.encodeRaw('<>"\'&\u00e5\u00e4\u00f6\u0060'), '&lt;&gt;"\'&amp;\u00e5\u00e4\u00f6\u0060', 'Raw encoding text');
equal(tinymce.html.Entities.encodeRaw('<>"\'&\u00e5\u00e4\u00f6\u0060', true), '&lt;&gt;&quot;\'&amp;\u00e5\u00e4\u00f6&#96;', 'Raw encoding attribute');
});
test('encodeAllRaw', function() {

View File

@ -609,9 +609,33 @@
writer.reset();
parser.parse(
'<a href="javascript:alert(1)">1</a>' +
'<a href=" 2 ">2</a>'
'<a href=" 2 ">2</a>' +
'<a href="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">3</a>'
);
equal(
writer.getContent(),
'<a href="javascript:alert(1)">1</a><a href=" 2 ">2</a>' +
'<a href="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">3</a>'
);
});
test('Parse script urls (allowed html data uris)', function() {
var counter, parser;
counter = createCounter(writer);
counter.validate = false;
counter.allow_html_data_urls = true;
parser = new tinymce.html.SaxParser(counter, schema);
writer.reset();
parser.parse(
'<a href="javascript:alert(1)">1</a>' +
'<a href="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">2</a>'
);
equal(
writer.getContent(),
'<a>1</a>' +
'<a href="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">2</a>'
);
equal(writer.getContent(), '<a href="javascript:alert(1)">1</a><a href=" 2 ">2</a>');
});
test('Parse script urls (denied)', function() {
@ -630,8 +654,61 @@
'<a href="java\nscript:alert(5)">5</a>' +
'<a href="java\tscript:alert(6)">6</a>' +
'<a href="%6aavascript:alert(7)">7</a>' +
'<a href="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">8</a>' +
'<a href=" dAt%61: tExt/html ; bAse64 , PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">9</a>' +
'<object data="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+">10</object>' +
'<button formaction="javascript:alert(11)">11</button>' +
'<table background="javascript:alert(12)"><tr><tr>12</tr></tr></table>' +
'<a href="mhtml:13">13</a>' +
'<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">' +
'<a href="%E3%82%AA%E3%83%BC%E3%83">Invalid url</a>'
);
equal(writer.getContent(), '<a>1</a><a>2</a><a>3</a><a>4</a><a>5</a><a>6</a><a>7</a><a href="%E3%82%AA%E3%83%BC%E3%83">Invalid url</a>');
equal(
writer.getContent(),
'<a>1</a><a>2</a><a>3</a><a>4</a><a>5</a><a>6</a><a>7</a><a>8</a><a>9</a>' +
'<object>10</object><button>11</button><table><tr></tr><tr>12</tr></table><a>13</a>' +
'<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />' +
'<a href="%E3%82%AA%E3%83%BC%E3%83">Invalid url</a>'
);
});
test('Parse away bogus elements', function() {
function testBogusSaxParse(inputHtml, outputHtml, counters) {
var counter, parser;
counter = createCounter(writer);
counter.validate = true;
parser = new tinymce.html.SaxParser(counter, schema);
writer.reset();
parser.parse(inputHtml);
equal(writer.getContent(), outputHtml);
deepEqual(counter.counts, counters);
}
testBogusSaxParse('a<b data-mce-bogus="1">b</b>c', 'abc', {text: 3});
testBogusSaxParse('a<b data-mce-bogus="true">b</b>c', 'abc', {text: 3});
testBogusSaxParse('a<b data-mce-bogus="1"></b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all">b</b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"><!-- x --><?xml?></b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"><b>b</b></b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"><br>b</b><b>c</b>', 'a<b>c</b>', {start: 1, end: 1, text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"><img>b</b><b>c</b>', 'a<b>c</b>', {start: 1, end: 1, text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"><b attr="x">b</b></b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"></b>c', 'ac', {text: 2});
testBogusSaxParse('a<b data-mce-bogus="all"></b><b>c</b>', 'a<b>c</b>', {start: 1, end: 1, text:2});
});
test('findEndTag', function() {
function testFindEndTag(html, startIndex, expectedIndex) {
equal(tinymce.html.SaxParser.findEndTag(schema, html, startIndex), expectedIndex);
}
testFindEndTag('<b>', 3, 3);
testFindEndTag('<img>', 3, 3);
testFindEndTag('<b></b>', 3, 7);
testFindEndTag('<b><img></b>', 3, 12);
testFindEndTag('<b><!-- </b> --></b>', 3, 20);
testFindEndTag('<span><b><i>a<img>b</i><b>c</b></b></span>', 9, 35);
});
})();

View File

@ -256,6 +256,20 @@ test('getTextBlockElements', function() {
});
});
test('getTextInlineElements', function() {
var schema;
expect(1);
schema = new tinymce.html.Schema();
deepEqual(schema.getTextInlineElements(), {
"B": {}, "CITE": {}, "CODE": {}, "DFN": {}, "EM": {}, "FONT": {}, "I": {}, "MARK": {}, "Q": {},
"SAMP": {}, "SPAN": {}, "STRIKE": {}, "STRONG": {}, "SUB": {}, "SUP": {}, "U": {}, "VAR": {},
"b": {}, "cite": {}, "code": {}, "dfn": {}, "em": {}, "font": {}, "i": {}, "mark": {}, "q": {},
"samp": {}, "span": {}, "strike": {}, "strong": {}, "sub": {}, "sup": {}, "u": {}, "var": {}
});
});
test('isValidChild', function() {
var schema;
@ -407,3 +421,116 @@ test('isValid', function() {
ok(schema.isValid('i', 'id'));
});
test('validStyles', function() {
var schema;
schema = new tinymce.html.Schema({valid_styles: 'color,font-size'});
deepEqual(schema.getValidStyles(), {
"*": [
"color",
"font-size"
]
});
schema = new tinymce.html.Schema({valid_styles: 'color font-size'});
deepEqual(schema.getValidStyles(), {
"*": [
"color",
"font-size"
]
});
schema = new tinymce.html.Schema({
valid_styles: {
'*': 'color font-size',
'a': 'background font-family'
}
});
deepEqual(schema.getValidStyles(), {
"*": [
"color",
"font-size"
],
"a": [
"background",
"font-family"
]
});
});
test('invalidStyles', function() {
var schema;
schema = new tinymce.html.Schema({invalid_styles: 'color,font-size'});
deepEqual(schema.getInvalidStyles(), {
'*': {
'color': {},
'font-size': {}
}
});
schema = new tinymce.html.Schema({invalid_styles: 'color font-size'});
deepEqual(schema.getInvalidStyles(), {
'*': {
'color': {},
'font-size': {}
}
});
schema = new tinymce.html.Schema({
invalid_styles: {
'*': 'color font-size',
'a': 'background font-family'
}
});
deepEqual(schema.getInvalidStyles(), {
'*': {
'color': {},
'font-size': {}
},
'a': {
'background': {},
'font-family': {}
}
});
});
test('validClasses', function() {
var schema;
schema = new tinymce.html.Schema({valid_classes: 'classA,classB'});
deepEqual(schema.getValidClasses(), {
'*': {
'classA': {},
'classB': {}
}
});
schema = new tinymce.html.Schema({valid_classes: 'classA classB'});
deepEqual(schema.getValidClasses(), {
'*': {
'classA': {},
'classB': {}
}
});
schema = new tinymce.html.Schema({
valid_classes: {
'*': 'classA classB',
'a': 'classC classD'
}
});
deepEqual(schema.getValidClasses(), {
'*': {
'classA': {},
'classB': {}
},
'a': {
'classC': {},
'classD': {}
}
});
});

View File

@ -122,7 +122,7 @@ test('Font weight', function() {
});
test('Valid styles', function() {
var styles = new tinymce.html.Styles({}, new tinymce.html.Schema({valid_styles : {'*' : 'color,font-size', 'a' : 'margin-left'}}));
var styles = new tinymce.html.Styles({}, new tinymce.html.Schema({valid_styles : {'*': 'color,font-size', 'a': 'margin-left'}}));
expect(2);
@ -130,16 +130,27 @@ test('Valid styles', function() {
equal(styles.serialize(styles.parse('color: #ff0000; font-size: 10px; margin-left: 10px; invalid: 2;'), 'a'), "color: #ff0000; font-size: 10px; margin-left: 10px;");
});
test('Invalid styles', function() {
var styles = new tinymce.html.Styles({}, new tinymce.html.Schema({invalid_styles : {'*': 'color,font-size', 'a': 'margin-left'}}));
equal(styles.serialize(styles.parse('color: #ff0000; font-size: 10px; margin-left: 10px'), 'b'), "margin-left: 10px;");
equal(styles.serialize(styles.parse('color: #ff0000; font-size: 10px; margin-left: 10px; margin-right: 10px;'), 'a'), "margin-right: 10px;");
});
test('Script urls denied', function() {
var styles = new tinymce.html.Styles();
equal(styles.serialize(styles.parse('behavior:url(test.htc)')), "");
equal(styles.serialize(styles.parse('color:expression(alert(1))')), "");
equal(styles.serialize(styles.parse('color:\\65xpression(alert(1))')), "");
equal(styles.serialize(styles.parse('color:exp/**/ression(alert(1))')), "");
equal(styles.serialize(styles.parse('color:/**/')), "");
equal(styles.serialize(styles.parse('color: expression ( alert(1))')), "");
equal(styles.serialize(styles.parse('background:url(jAvaScript:alert(1)')), "");
equal(styles.serialize(styles.parse('background:url(javascript:alert(1)')), "");
equal(styles.serialize(styles.parse('background:url(vbscript:alert(1)')), "");
equal(styles.serialize(styles.parse('background:url(j\navas\u0000cr\tipt:alert(1)')), "");
equal(styles.serialize(styles.parse('background:url(data:image/svg+xml,%3Csvg/%3E)')), "");
});
test('Script urls allowed', function() {

View File

@ -0,0 +1,255 @@
module("tinymce.util.EventDispatcher");
test("fire (no event listeners)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), args;
args = dispatcher.fire('click', {test: 1});
equal(args.test, 1);
equal(args.isDefaultPrevented(), false);
equal(args.isPropagationStopped(), false);
equal(args.isImmediatePropagationStopped(), false);
strictEqual(args.target, dispatcher);
args = dispatcher.fire('click');
equal(args.isDefaultPrevented(), false);
equal(args.isPropagationStopped(), false);
equal(args.isImmediatePropagationStopped(), false);
});
test("fire (event listeners)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
dispatcher.on('click', function() {data += 'a';});
dispatcher.on('click', function() {data += 'b';});
args = dispatcher.fire('click', {test: 1});
equal(data, 'ab');
});
test("fire (event listeners) stopImmediatePropagation", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
dispatcher.on('click', function(e) { data += 'a'; e.stopImmediatePropagation(); });
dispatcher.on('click', function() { data += 'b'; });
dispatcher.fire('click', {test: 1});
equal(data, 'a');
});
test("on", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
strictEqual(dispatcher.on('click', function() {data += 'a';}), dispatcher);
strictEqual(dispatcher.on('click keydown', function() {data += 'b';}), dispatcher);
dispatcher.fire('click');
equal(data, 'ab');
dispatcher.fire('keydown');
equal(data, 'abb');
});
test("on (prepend)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
strictEqual(dispatcher.on('click', function() {data += 'a';}), dispatcher);
strictEqual(dispatcher.on('click', function() {data += 'b';}, true), dispatcher);
dispatcher.fire('click');
equal(data, 'ba');
});
test("once", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
strictEqual(dispatcher.on('click', function() {data += 'a';}), dispatcher);
strictEqual(dispatcher.once('click', function() {data += 'b';}), dispatcher);
strictEqual(dispatcher.on('click', function() {data += 'c';}), dispatcher);
dispatcher.fire('click');
equal(data, 'abc');
dispatcher.fire('click');
equal(data, 'abcac');
});
test("once (prepend)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
strictEqual(dispatcher.on('click', function() {data += 'a';}), dispatcher);
strictEqual(dispatcher.once('click', function() {data += 'b';}, true), dispatcher);
strictEqual(dispatcher.on('click', function() {data += 'c';}), dispatcher);
dispatcher.fire('click');
equal(data, 'bac');
dispatcher.fire('click');
equal(data, 'bacac');
});
test("once (unbind)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
function handler() {
data += 'b';
}
dispatcher.once('click', function() {data += 'a';});
dispatcher.once('click', handler);
dispatcher.off('click', handler);
dispatcher.fire('click');
equal(data, 'a');
});
test("once (multiple events)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
dispatcher.once('click', function() {data += 'a';});
dispatcher.once('keydown', function() {data += 'b';});
dispatcher.fire('click');
equal(data, 'a');
dispatcher.fire('keydown');
equal(data, 'ab');
dispatcher.fire('click');
dispatcher.fire('keydown');
equal(data, 'ab');
});
test("off (all)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
function listenerA() { data += 'a'; }
function listenerB() { data += 'b'; }
function listenerC() { data += 'c'; }
dispatcher.on('click', listenerA);
dispatcher.on('click', listenerB);
dispatcher.on('keydown', listenerC);
dispatcher.off();
data = '';
dispatcher.fire('click');
dispatcher.fire('keydown');
equal(data, '');
});
test("off (all named)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
function listenerA() { data += 'a'; }
function listenerB() { data += 'b'; }
function listenerC() { data += 'c'; }
dispatcher.on('click', listenerA);
dispatcher.on('click', listenerB);
dispatcher.on('keydown', listenerC);
dispatcher.off('click');
data = '';
dispatcher.fire('click');
dispatcher.fire('keydown');
equal(data, 'c');
});
test("off (all specific observer)", function() {
var dispatcher = new tinymce.util.EventDispatcher(), data = '';
function listenerA() { data += 'a'; }
function listenerB() { data += 'b'; }
dispatcher.on('click', listenerA);
dispatcher.on('click', listenerB);
dispatcher.off('click', listenerB);
data = '';
dispatcher.fire('click');
equal(data, 'a');
});
test("scope setting", function() {
var lastScope, lastEvent, dispatcher;
dispatcher = new tinymce.util.EventDispatcher();
dispatcher.on('click', function() {
lastScope = this;
}).fire('click');
strictEqual(dispatcher, lastScope);
var scope = {test: 1};
dispatcher = new tinymce.util.EventDispatcher({scope: scope});
dispatcher.on('click', function(e) {
lastScope = this;
lastEvent = e;
}).fire('click');
strictEqual(scope, lastScope);
strictEqual(lastEvent.target, lastScope);
});
test("beforeFire setting", function() {
var lastArgs, dispatcher, args;
dispatcher = new tinymce.util.EventDispatcher({
beforeFire: function(args) {
lastArgs = args;
}
});
args = dispatcher.fire('click');
strictEqual(lastArgs, args);
});
test("beforeFire setting (stopImmediatePropagation)", function() {
var lastArgs, dispatcher, args, data = '';
dispatcher = new tinymce.util.EventDispatcher({
beforeFire: function(args) {
lastArgs = args;
args.stopImmediatePropagation();
}
});
function listenerA() { data += 'a'; }
dispatcher.on('click', listenerA);
args = dispatcher.fire('click');
strictEqual(lastArgs, args);
strictEqual(data, '');
});
test("toggleEvent setting", function() {
var lastName, lastState;
dispatcher = new tinymce.util.EventDispatcher({
toggleEvent: function(name, state) {
lastName = name;
lastState = state;
}
});
function listenerA() { data += 'a'; }
function listenerB() { data += 'b'; }
dispatcher.on('click', listenerA);
strictEqual(lastName, 'click');
strictEqual(lastState, true);
lastName = lastState = null;
dispatcher.on('click', listenerB);
strictEqual(lastName, null);
strictEqual(lastState, null);
dispatcher.off('click', listenerA);
strictEqual(lastName, null);
strictEqual(lastState, null);
dispatcher.off('click', listenerB);
strictEqual(lastName, 'click');
strictEqual(lastState, false);
});

View File

@ -0,0 +1,46 @@
module("tinymce.util.Observable");
test("Event bubbling/removed state", function() {
var lastName, lastState, data = '';
function Class(parentObj) {
this.toggleNativeEvent = function(name, state) {
lastName = name;
lastState = state;
};
this.parent = function() {
return parentObj;
};
}
tinymce.util.Tools.extend(Class.prototype, tinymce.util.Observable);
var inst1 = new Class();
inst1.on('click', function() { data += 'a'; });
strictEqual(lastName, 'click');
strictEqual(lastState, true);
lastName = lastState = null;
inst1.on('click', function() { data += 'b'; });
strictEqual(lastName, null);
strictEqual(lastState, null);
var inst2 = new Class(inst1);
inst2.on('click', function() { data += 'c'; });
inst2.fire('click');
strictEqual(data, 'cab');
inst2.on('click', function(e) { e.stopPropagation(); });
inst2.fire('click');
strictEqual(data, 'cabc');
inst1.on('remove', function() { data += 'r'; });
inst1.removed = true;
inst1.fire('click');
inst1.fire('remove');
strictEqual(data, 'cabcr');
});

View File

@ -22,7 +22,7 @@ test('parseFullURLs', 3, function() {
equal(new tinymce.util.URI('chrome-extension://abcdefghijklmnopqrstuvwzyz1234567890:8080/path/dir/file.ext?key1=val1&key2=val2#hash').getURI(), 'chrome-extension://abcdefghijklmnopqrstuvwzyz1234567890:8080/path/dir/file.ext?key1=val1&key2=val2#hash');
});
test('relativeURLs', 29, function() {
test('relativeURLs', 31, function() {
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/file.html').toRelative('http://www.site.com/dir1/dir3/file.html'), '../dir3/file.html');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/file.html').toRelative('http://www.site.com/dir3/dir4/file.html'), '../../dir3/dir4/file.html');
equal(new tinymce.util.URI('http://www.site.com/dir1/').toRelative('http://www.site.com/dir1/dir3/file.htm'), 'dir3/file.htm');
@ -52,9 +52,12 @@ test('relativeURLs', 29, function() {
equal(new tinymce.util.URI('chrome-extension://abcdefghijklmnopqrstuvwzyz1234567890/dir1/dir2/').toRelative('/dir1', true), '../');
equal(new tinymce.util.URI('http://www.site.com/').toRelative('http://www.site.com/'), 'http://www.site.com/');
equal(new tinymce.util.URI('http://www.site.com/').toRelative('http://www.site.com'), 'http://www.site.com/');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toRelative('/file.htm?q=http://site.com/'), '../../file.htm?q=http://site.com/');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toRelative('/file.htm#http://site.com/'), '../../file.htm#http://site.com/');
});
test('absoluteURLs', 18, function() {
test('absoluteURLs', 19, function() {
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toAbsolute(''), 'http://www.site.com/dir1/dir2/');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toAbsolute('../dir3'), 'http://www.site.com/dir1/dir3');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toAbsolute('../dir3', 1), '/dir1/dir3');
equal(new tinymce.util.URI('http://www.site.com/dir1/dir2/').toAbsolute('../../../../dir3'), 'http://www.site.com/dir3');
@ -83,3 +86,19 @@ test('strangeURLs', 6, function() {
equal(new tinymce.util.URI('tel:somegroup').getURI(), 'tel:somegroup');
equal(new tinymce.util.URI('//www.site.com/a@b').getURI(), '//www.site.com/a@b');
});
test('isSameOrigin', function() {
ok(new tinymce.util.URI('http://www.site.com').isSameOrigin(new tinymce.util.URI('http://www.site.com')));
ok(new tinymce.util.URI('//www.site.com').isSameOrigin(new tinymce.util.URI('//www.site.com')));
ok(new tinymce.util.URI('http://www.site.com:80').isSameOrigin(new tinymce.util.URI('http://www.site.com')));
ok(new tinymce.util.URI('https://www.site.com:443').isSameOrigin(new tinymce.util.URI('https://www.site.com')));
ok(new tinymce.util.URI('//www.site.com:80').isSameOrigin(new tinymce.util.URI('//www.site.com:80')));
ok(new tinymce.util.URI('mailto:test@site.com').isSameOrigin(new tinymce.util.URI('mailto:test@site.com')));
ok(new tinymce.util.URI('mailto:test@site.com:25').isSameOrigin(new tinymce.util.URI('mailto:test@site.com')));
ok(new tinymce.util.URI('ftp://www.site.com').isSameOrigin(new tinymce.util.URI('ftp://www.site.com')));
ok(new tinymce.util.URI('ftp://www.site.com:21').isSameOrigin(new tinymce.util.URI('ftp://www.site.com')));
ok(new tinymce.util.URI('https://www.site.com').isSameOrigin(new tinymce.util.URI('http://www.site.com')) == false);
ok(new tinymce.util.URI('http://www.site.com:8080').isSameOrigin(new tinymce.util.URI('http://www.site.com')) == false);
ok(new tinymce.util.URI('https://www.site.com:8080').isSameOrigin(new tinymce.util.URI('https://www.site.com')) == false);
ok(new tinymce.util.URI('ftp://www.site.com:1021').isSameOrigin(new tinymce.util.URI('ftp://www.site.com')) == false);
});