You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
9.9 KiB
378 lines
9.9 KiB
4 years ago
|
/**
|
||
|
* jquery.tinymce.js
|
||
|
*
|
||
|
* Released under LGPL License.
|
||
|
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
|
||
|
*
|
||
|
* License: http://www.tinymce.com/license
|
||
|
* Contributing: http://www.tinymce.com/contributing
|
||
|
*/
|
||
|
|
||
|
/*global tinymce:true, jQuery */
|
||
|
|
||
|
(function($) {
|
||
|
var undef,
|
||
|
lazyLoading,
|
||
|
patchApplied,
|
||
|
delayedInits = [],
|
||
|
win = window;
|
||
|
|
||
|
$.fn.tinymce = function(settings) {
|
||
|
var self = this, url, base, lang, suffix = "";
|
||
|
|
||
|
// No match then just ignore the call
|
||
|
if (!self.length) {
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
// Get editor instance
|
||
|
if (!settings) {
|
||
|
return window.tinymce ? tinymce.get(self[0].id) : null;
|
||
|
}
|
||
|
|
||
|
self.css('visibility', 'hidden'); // Hide textarea to avoid flicker
|
||
|
|
||
|
function init() {
|
||
|
var editors = [], initCount = 0;
|
||
|
|
||
|
// Apply patches to the jQuery object, only once
|
||
|
if (!patchApplied) {
|
||
|
applyPatch();
|
||
|
patchApplied = true;
|
||
|
}
|
||
|
|
||
|
// Create an editor instance for each matched node
|
||
|
self.each(function(i, node) {
|
||
|
var ed, id = node.id, oninit = settings.oninit;
|
||
|
|
||
|
// Generate unique id for target element if needed
|
||
|
if (!id) {
|
||
|
node.id = id = tinymce.DOM.uniqueId();
|
||
|
}
|
||
|
|
||
|
// Only init the editor once
|
||
|
if (tinymce.get(id)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Create editor instance and render it
|
||
|
ed = new tinymce.Editor(id, settings, tinymce.EditorManager);
|
||
|
editors.push(ed);
|
||
|
|
||
|
ed.on('init', function() {
|
||
|
var scope, func = oninit;
|
||
|
|
||
|
self.css('visibility', '');
|
||
|
|
||
|
// Run this if the oninit setting is defined
|
||
|
// this logic will fire the oninit callback ones each
|
||
|
// matched editor instance is initialized
|
||
|
if (oninit) {
|
||
|
// Fire the oninit event ones each editor instance is initialized
|
||
|
if (++initCount == editors.length) {
|
||
|
if (typeof func === "string") {
|
||
|
scope = (func.indexOf(".") === -1) ? null : tinymce.resolve(func.replace(/\.\w+$/, ""));
|
||
|
func = tinymce.resolve(func);
|
||
|
}
|
||
|
|
||
|
// Call the oninit function with the object
|
||
|
func.apply(scope || tinymce, editors);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Render the editor instances in a separate loop since we
|
||
|
// need to have the full editors array used in the onInit calls
|
||
|
$.each(editors, function(i, ed) {
|
||
|
ed.render();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Load TinyMCE on demand, if we need to
|
||
|
if (!win.tinymce && !lazyLoading && (url = settings.script_url)) {
|
||
|
lazyLoading = 1;
|
||
|
base = url.substring(0, url.lastIndexOf("/"));
|
||
|
|
||
|
// Check if it's a dev/src version they want to load then
|
||
|
// make sure that all plugins, themes etc are loaded in source mode as well
|
||
|
if (url.indexOf('.min') != -1) {
|
||
|
suffix = ".min";
|
||
|
}
|
||
|
|
||
|
// Setup tinyMCEPreInit object this will later be used by the TinyMCE
|
||
|
// core script to locate other resources like CSS files, dialogs etc
|
||
|
// You can also predefined a tinyMCEPreInit object and then it will use that instead
|
||
|
win.tinymce = win.tinyMCEPreInit || {
|
||
|
base: base,
|
||
|
suffix: suffix
|
||
|
};
|
||
|
|
||
|
// url contains gzip then we assume it's a compressor
|
||
|
if (url.indexOf('gzip') != -1) {
|
||
|
lang = settings.language || "en";
|
||
|
url = url + (/\?/.test(url) ? '&' : '?') + "js=true&core=true&suffix=" + escape(suffix) +
|
||
|
"&themes=" + escape(settings.theme || 'modern') + "&plugins=" +
|
||
|
escape(settings.plugins || '') + "&languages=" + (lang || '');
|
||
|
|
||
|
// Check if compressor script is already loaded otherwise setup a basic one
|
||
|
if (!win.tinyMCE_GZ) {
|
||
|
win.tinyMCE_GZ = {
|
||
|
start: function() {
|
||
|
function load(url) {
|
||
|
tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(url));
|
||
|
}
|
||
|
|
||
|
// Add core languages
|
||
|
load("langs/" + lang + ".js");
|
||
|
|
||
|
// Add themes with languages
|
||
|
load("themes/" + settings.theme + "/theme" + suffix + ".js");
|
||
|
load("themes/" + settings.theme + "/langs/" + lang + ".js");
|
||
|
|
||
|
// Add plugins with languages
|
||
|
$.each(settings.plugins.split(","), function(i, name) {
|
||
|
if (name) {
|
||
|
load("plugins/" + name + "/plugin" + suffix + ".js");
|
||
|
load("plugins/" + name + "/langs/" + lang + ".js");
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
end: function() {
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var script = document.createElement('script');
|
||
|
script.type = 'text/javascript';
|
||
|
script.onload = script.onreadystatechange = function(e) {
|
||
|
e = e || window.event;
|
||
|
|
||
|
if (lazyLoading !== 2 && (e.type == 'load' || /complete|loaded/.test(script.readyState))) {
|
||
|
tinymce.dom.Event.domLoaded = 1;
|
||
|
lazyLoading = 2;
|
||
|
|
||
|
// Execute callback after mainscript has been loaded and before the initialization occurs
|
||
|
if (settings.script_loaded) {
|
||
|
settings.script_loaded();
|
||
|
}
|
||
|
|
||
|
init();
|
||
|
|
||
|
$.each(delayedInits, function(i, init) {
|
||
|
init();
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
script.src = url;
|
||
|
document.body.appendChild(script);
|
||
|
} else {
|
||
|
// Delay the init call until tinymce is loaded
|
||
|
if (lazyLoading === 1) {
|
||
|
delayedInits.push(init);
|
||
|
} else {
|
||
|
init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
};
|
||
|
|
||
|
// Add :tinymce pseudo selector this will select elements that has been converted into editor instances
|
||
|
// it's now possible to use things like $('*:tinymce') to get all TinyMCE bound elements.
|
||
|
$.extend($.expr[":"], {
|
||
|
tinymce: function(e) {
|
||
|
var editor;
|
||
|
|
||
|
if (e.id && "tinymce" in window) {
|
||
|
editor = tinymce.get(e.id);
|
||
|
|
||
|
if (editor && editor.editorManager === tinymce) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// This function patches internal jQuery functions so that if
|
||
|
// you for example remove an div element containing an editor it's
|
||
|
// automatically destroyed by the TinyMCE API
|
||
|
function applyPatch() {
|
||
|
// Removes any child editor instances by looking for editor wrapper elements
|
||
|
function removeEditors(name) {
|
||
|
// If the function is remove
|
||
|
if (name === "remove") {
|
||
|
this.each(function(i, node) {
|
||
|
var ed = tinyMCEInstance(node);
|
||
|
|
||
|
if (ed) {
|
||
|
ed.remove();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.find("span.mceEditor,div.mceEditor").each(function(i, node) {
|
||
|
var ed = tinymce.get(node.id.replace(/_parent$/, ""));
|
||
|
|
||
|
if (ed) {
|
||
|
ed.remove();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Loads or saves contents from/to textarea if the value
|
||
|
// argument is defined it will set the TinyMCE internal contents
|
||
|
function loadOrSave(value) {
|
||
|
var self = this, ed;
|
||
|
|
||
|
// Handle set value
|
||
|
/*jshint eqnull:true */
|
||
|
if (value != null) {
|
||
|
removeEditors.call(self);
|
||
|
|
||
|
// Saves the contents before get/set value of textarea/div
|
||
|
self.each(function(i, node) {
|
||
|
var ed;
|
||
|
|
||
|
if ((ed = tinymce.get(node.id))) {
|
||
|
ed.setContent(value);
|
||
|
}
|
||
|
});
|
||
|
} else if (self.length > 0) {
|
||
|
// Handle get value
|
||
|
if ((ed = tinymce.get(self[0].id))) {
|
||
|
return ed.getContent();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns tinymce instance for the specified element or null if it wasn't found
|
||
|
function tinyMCEInstance(element) {
|
||
|
var ed = null;
|
||
|
|
||
|
if (element && element.id && win.tinymce) {
|
||
|
ed = tinymce.get(element.id);
|
||
|
}
|
||
|
|
||
|
return ed;
|
||
|
}
|
||
|
|
||
|
// Checks if the specified set contains tinymce instances
|
||
|
function containsTinyMCE(matchedSet) {
|
||
|
return !!((matchedSet) && (matchedSet.length) && (win.tinymce) && (matchedSet.is(":tinymce")));
|
||
|
}
|
||
|
|
||
|
// Patch various jQuery functions
|
||
|
var jQueryFn = {};
|
||
|
|
||
|
// Patch some setter/getter functions these will
|
||
|
// now be able to set/get the contents of editor instances for
|
||
|
// example $('#editorid').html('Content'); will update the TinyMCE iframe instance
|
||
|
$.each(["text", "html", "val"], function(i, name) {
|
||
|
var origFn = jQueryFn[name] = $.fn[name],
|
||
|
textProc = (name === "text");
|
||
|
|
||
|
$.fn[name] = function(value) {
|
||
|
var self = this;
|
||
|
|
||
|
if (!containsTinyMCE(self)) {
|
||
|
return origFn.apply(self, arguments);
|
||
|
}
|
||
|
|
||
|
if (value !== undef) {
|
||
|
loadOrSave.call(self.filter(":tinymce"), value);
|
||
|
origFn.apply(self.not(":tinymce"), arguments);
|
||
|
|
||
|
return self; // return original set for chaining
|
||
|
}
|
||
|
|
||
|
var ret = "";
|
||
|
var args = arguments;
|
||
|
|
||
|
(textProc ? self : self.eq(0)).each(function(i, node) {
|
||
|
var ed = tinyMCEInstance(node);
|
||
|
|
||
|
if (ed) {
|
||
|
ret += textProc ? ed.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g, "") : ed.getContent({save: true});
|
||
|
} else {
|
||
|
ret += origFn.apply($(node), args);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return ret;
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// Makes it possible to use $('#id').append("content"); to append contents to the TinyMCE editor iframe
|
||
|
$.each(["append", "prepend"], function(i, name) {
|
||
|
var origFn = jQueryFn[name] = $.fn[name],
|
||
|
prepend = (name === "prepend");
|
||
|
|
||
|
$.fn[name] = function(value) {
|
||
|
var self = this;
|
||
|
|
||
|
if (!containsTinyMCE(self)) {
|
||
|
return origFn.apply(self, arguments);
|
||
|
}
|
||
|
|
||
|
if (value !== undef) {
|
||
|
if (typeof value === "string") {
|
||
|
self.filter(":tinymce").each(function(i, node) {
|
||
|
var ed = tinyMCEInstance(node);
|
||
|
|
||
|
if (ed) {
|
||
|
ed.setContent(prepend ? value + ed.getContent() : ed.getContent() + value);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
origFn.apply(self.not(":tinymce"), arguments);
|
||
|
|
||
|
return self; // return original set for chaining
|
||
|
}
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// Makes sure that the editor instance gets properly destroyed when the parent element is removed
|
||
|
$.each(["remove", "replaceWith", "replaceAll", "empty"], function(i, name) {
|
||
|
var origFn = jQueryFn[name] = $.fn[name];
|
||
|
|
||
|
$.fn[name] = function() {
|
||
|
removeEditors.call(this, name);
|
||
|
|
||
|
return origFn.apply(this, arguments);
|
||
|
};
|
||
|
});
|
||
|
|
||
|
jQueryFn.attr = $.fn.attr;
|
||
|
|
||
|
// Makes sure that $('#tinymce_id').attr('value') gets the editors current HTML contents
|
||
|
$.fn.attr = function(name, value) {
|
||
|
var self = this, args = arguments;
|
||
|
|
||
|
if ((!name) || (name !== "value") || (!containsTinyMCE(self))) {
|
||
|
if (value !== undef) {
|
||
|
return jQueryFn.attr.apply(self, args);
|
||
|
}
|
||
|
|
||
|
return jQueryFn.attr.apply(self, args);
|
||
|
}
|
||
|
|
||
|
if (value !== undef) {
|
||
|
loadOrSave.call(self.filter(":tinymce"), value);
|
||
|
jQueryFn.attr.apply(self.not(":tinymce"), args);
|
||
|
|
||
|
return self; // return original set for chaining
|
||
|
}
|
||
|
|
||
|
var node = self[0], ed = tinyMCEInstance(node);
|
||
|
|
||
|
return ed ? ed.getContent({save: true}) : jQueryFn.attr.apply($(node), args);
|
||
|
};
|
||
|
}
|
||
|
})(jQuery);
|