jquery.tinymce.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /**
  2. * jquery.tinymce.js
  3. *
  4. * Released under LGPL License.
  5. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  6. *
  7. * License: http://www.tinymce.com/license
  8. * Contributing: http://www.tinymce.com/contributing
  9. */
  10. /*global tinymce:true, jQuery */
  11. (function($) {
  12. var undef,
  13. lazyLoading,
  14. patchApplied,
  15. delayedInits = [],
  16. win = window;
  17. $.fn.tinymce = function(settings) {
  18. var self = this, url, base, lang, suffix = "";
  19. // No match then just ignore the call
  20. if (!self.length) {
  21. return self;
  22. }
  23. // Get editor instance
  24. if (!settings) {
  25. return window.tinymce ? tinymce.get(self[0].id) : null;
  26. }
  27. self.css('visibility', 'hidden'); // Hide textarea to avoid flicker
  28. function init() {
  29. var editors = [], initCount = 0;
  30. // Apply patches to the jQuery object, only once
  31. if (!patchApplied) {
  32. applyPatch();
  33. patchApplied = true;
  34. }
  35. // Create an editor instance for each matched node
  36. self.each(function(i, node) {
  37. var ed, id = node.id, oninit = settings.oninit;
  38. // Generate unique id for target element if needed
  39. if (!id) {
  40. node.id = id = tinymce.DOM.uniqueId();
  41. }
  42. // Only init the editor once
  43. if (tinymce.get(id)) {
  44. return;
  45. }
  46. // Create editor instance and render it
  47. ed = new tinymce.Editor(id, settings, tinymce.EditorManager);
  48. editors.push(ed);
  49. ed.on('init', function() {
  50. var scope, func = oninit;
  51. self.css('visibility', '');
  52. // Run this if the oninit setting is defined
  53. // this logic will fire the oninit callback ones each
  54. // matched editor instance is initialized
  55. if (oninit) {
  56. // Fire the oninit event ones each editor instance is initialized
  57. if (++initCount == editors.length) {
  58. if (typeof func === "string") {
  59. scope = (func.indexOf(".") === -1) ? null : tinymce.resolve(func.replace(/\.\w+$/, ""));
  60. func = tinymce.resolve(func);
  61. }
  62. // Call the oninit function with the object
  63. func.apply(scope || tinymce, editors);
  64. }
  65. }
  66. });
  67. });
  68. // Render the editor instances in a separate loop since we
  69. // need to have the full editors array used in the onInit calls
  70. $.each(editors, function(i, ed) {
  71. ed.render();
  72. });
  73. }
  74. // Load TinyMCE on demand, if we need to
  75. if (!win.tinymce && !lazyLoading && (url = settings.script_url)) {
  76. lazyLoading = 1;
  77. base = url.substring(0, url.lastIndexOf("/"));
  78. // Check if it's a dev/src version they want to load then
  79. // make sure that all plugins, themes etc are loaded in source mode as well
  80. if (url.indexOf('.min') != -1) {
  81. suffix = ".min";
  82. }
  83. // Setup tinyMCEPreInit object this will later be used by the TinyMCE
  84. // core script to locate other resources like CSS files, dialogs etc
  85. // You can also predefined a tinyMCEPreInit object and then it will use that instead
  86. win.tinymce = win.tinyMCEPreInit || {
  87. base: base,
  88. suffix: suffix
  89. };
  90. // url contains gzip then we assume it's a compressor
  91. if (url.indexOf('gzip') != -1) {
  92. lang = settings.language || "en";
  93. url = url + (/\?/.test(url) ? '&' : '?') + "js=true&core=true&suffix=" + escape(suffix) +
  94. "&themes=" + escape(settings.theme || 'modern') + "&plugins=" +
  95. escape(settings.plugins || '') + "&languages=" + (lang || '');
  96. // Check if compressor script is already loaded otherwise setup a basic one
  97. if (!win.tinyMCE_GZ) {
  98. win.tinyMCE_GZ = {
  99. start: function() {
  100. function load(url) {
  101. tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(url));
  102. }
  103. // Add core languages
  104. load("langs/" + lang + ".js");
  105. // Add themes with languages
  106. load("themes/" + settings.theme + "/theme" + suffix + ".js");
  107. load("themes/" + settings.theme + "/langs/" + lang + ".js");
  108. // Add plugins with languages
  109. $.each(settings.plugins.split(","), function(i, name) {
  110. if (name) {
  111. load("plugins/" + name + "/plugin" + suffix + ".js");
  112. load("plugins/" + name + "/langs/" + lang + ".js");
  113. }
  114. });
  115. },
  116. end: function() {
  117. }
  118. };
  119. }
  120. }
  121. var script = document.createElement('script');
  122. script.type = 'text/javascript';
  123. script.onload = script.onreadystatechange = function(e) {
  124. e = e || window.event;
  125. if (lazyLoading !== 2 && (e.type == 'load' || /complete|loaded/.test(script.readyState))) {
  126. tinymce.dom.Event.domLoaded = 1;
  127. lazyLoading = 2;
  128. // Execute callback after mainscript has been loaded and before the initialization occurs
  129. if (settings.script_loaded) {
  130. settings.script_loaded();
  131. }
  132. init();
  133. $.each(delayedInits, function(i, init) {
  134. init();
  135. });
  136. }
  137. };
  138. script.src = url;
  139. document.body.appendChild(script);
  140. } else {
  141. // Delay the init call until tinymce is loaded
  142. if (lazyLoading === 1) {
  143. delayedInits.push(init);
  144. } else {
  145. init();
  146. }
  147. }
  148. return self;
  149. };
  150. // Add :tinymce pseudo selector this will select elements that has been converted into editor instances
  151. // it's now possible to use things like $('*:tinymce') to get all TinyMCE bound elements.
  152. $.extend($.expr[":"], {
  153. tinymce: function(e) {
  154. var editor;
  155. if (e.id && "tinymce" in window) {
  156. editor = tinymce.get(e.id);
  157. if (editor && editor.editorManager === tinymce) {
  158. return true;
  159. }
  160. }
  161. return false;
  162. }
  163. });
  164. // This function patches internal jQuery functions so that if
  165. // you for example remove an div element containing an editor it's
  166. // automatically destroyed by the TinyMCE API
  167. function applyPatch() {
  168. // Removes any child editor instances by looking for editor wrapper elements
  169. function removeEditors(name) {
  170. // If the function is remove
  171. if (name === "remove") {
  172. this.each(function(i, node) {
  173. var ed = tinyMCEInstance(node);
  174. if (ed) {
  175. ed.remove();
  176. }
  177. });
  178. }
  179. this.find("span.mceEditor,div.mceEditor").each(function(i, node) {
  180. var ed = tinymce.get(node.id.replace(/_parent$/, ""));
  181. if (ed) {
  182. ed.remove();
  183. }
  184. });
  185. }
  186. // Loads or saves contents from/to textarea if the value
  187. // argument is defined it will set the TinyMCE internal contents
  188. function loadOrSave(value) {
  189. var self = this, ed;
  190. // Handle set value
  191. /*jshint eqnull:true */
  192. if (value != null) {
  193. removeEditors.call(self);
  194. // Saves the contents before get/set value of textarea/div
  195. self.each(function(i, node) {
  196. var ed;
  197. if ((ed = tinymce.get(node.id))) {
  198. ed.setContent(value);
  199. }
  200. });
  201. } else if (self.length > 0) {
  202. // Handle get value
  203. if ((ed = tinymce.get(self[0].id))) {
  204. return ed.getContent();
  205. }
  206. }
  207. }
  208. // Returns tinymce instance for the specified element or null if it wasn't found
  209. function tinyMCEInstance(element) {
  210. var ed = null;
  211. if (element && element.id && win.tinymce) {
  212. ed = tinymce.get(element.id);
  213. }
  214. return ed;
  215. }
  216. // Checks if the specified set contains tinymce instances
  217. function containsTinyMCE(matchedSet) {
  218. return !!((matchedSet) && (matchedSet.length) && (win.tinymce) && (matchedSet.is(":tinymce")));
  219. }
  220. // Patch various jQuery functions
  221. var jQueryFn = {};
  222. // Patch some setter/getter functions these will
  223. // now be able to set/get the contents of editor instances for
  224. // example $('#editorid').html('Content'); will update the TinyMCE iframe instance
  225. $.each(["text", "html", "val"], function(i, name) {
  226. var origFn = jQueryFn[name] = $.fn[name],
  227. textProc = (name === "text");
  228. $.fn[name] = function(value) {
  229. var self = this;
  230. if (!containsTinyMCE(self)) {
  231. return origFn.apply(self, arguments);
  232. }
  233. if (value !== undef) {
  234. loadOrSave.call(self.filter(":tinymce"), value);
  235. origFn.apply(self.not(":tinymce"), arguments);
  236. return self; // return original set for chaining
  237. }
  238. var ret = "";
  239. var args = arguments;
  240. (textProc ? self : self.eq(0)).each(function(i, node) {
  241. var ed = tinyMCEInstance(node);
  242. if (ed) {
  243. ret += textProc ? ed.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g, "") : ed.getContent({save: true});
  244. } else {
  245. ret += origFn.apply($(node), args);
  246. }
  247. });
  248. return ret;
  249. };
  250. });
  251. // Makes it possible to use $('#id').append("content"); to append contents to the TinyMCE editor iframe
  252. $.each(["append", "prepend"], function(i, name) {
  253. var origFn = jQueryFn[name] = $.fn[name],
  254. prepend = (name === "prepend");
  255. $.fn[name] = function(value) {
  256. var self = this;
  257. if (!containsTinyMCE(self)) {
  258. return origFn.apply(self, arguments);
  259. }
  260. if (value !== undef) {
  261. if (typeof value === "string") {
  262. self.filter(":tinymce").each(function(i, node) {
  263. var ed = tinyMCEInstance(node);
  264. if (ed) {
  265. ed.setContent(prepend ? value + ed.getContent() : ed.getContent() + value);
  266. }
  267. });
  268. }
  269. origFn.apply(self.not(":tinymce"), arguments);
  270. return self; // return original set for chaining
  271. }
  272. };
  273. });
  274. // Makes sure that the editor instance gets properly destroyed when the parent element is removed
  275. $.each(["remove", "replaceWith", "replaceAll", "empty"], function(i, name) {
  276. var origFn = jQueryFn[name] = $.fn[name];
  277. $.fn[name] = function() {
  278. removeEditors.call(this, name);
  279. return origFn.apply(this, arguments);
  280. };
  281. });
  282. jQueryFn.attr = $.fn.attr;
  283. // Makes sure that $('#tinymce_id').attr('value') gets the editors current HTML contents
  284. $.fn.attr = function(name, value) {
  285. var self = this, args = arguments;
  286. if ((!name) || (name !== "value") || (!containsTinyMCE(self))) {
  287. if (value !== undef) {
  288. return jQueryFn.attr.apply(self, args);
  289. }
  290. return jQueryFn.attr.apply(self, args);
  291. }
  292. if (value !== undef) {
  293. loadOrSave.call(self.filter(":tinymce"), value);
  294. jQueryFn.attr.apply(self.not(":tinymce"), args);
  295. return self; // return original set for chaining
  296. }
  297. var node = self[0], ed = tinyMCEInstance(node);
  298. return ed ? ed.getContent({save: true}) : jQueryFn.attr.apply($(node), args);
  299. };
  300. }
  301. })(jQuery);