EditorManager.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /**
  2. * EditorManager.js
  3. *
  4. * Copyright 2009, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://tinymce.moxiecode.com/license
  8. * Contributing: http://tinymce.moxiecode.com/contributing
  9. */
  10. (function(tinymce) {
  11. /**
  12. * @class tinymce
  13. */
  14. // Shorten names
  15. var each = tinymce.each, extend = tinymce.extend,
  16. DOM = tinymce.DOM, Event = tinymce.dom.Event,
  17. ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
  18. explode = tinymce.explode,
  19. Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;
  20. // Setup some URLs where the editor API is located and where the document is
  21. tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
  22. if (!/[\/\\]$/.test(tinymce.documentBaseURL))
  23. tinymce.documentBaseURL += '/';
  24. tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
  25. /**
  26. * Absolute baseURI for the installation path of TinyMCE.
  27. *
  28. * @property baseURI
  29. * @type tinymce.util.URI
  30. */
  31. tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);
  32. // Add before unload listener
  33. // This was required since IE was leaking memory if you added and removed beforeunload listeners
  34. // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
  35. tinymce.onBeforeUnload = new Dispatcher(tinymce);
  36. // Must be on window or IE will leak if the editor is placed in frame or iframe
  37. Event.add(window, 'beforeunload', function(e) {
  38. tinymce.onBeforeUnload.dispatch(tinymce, e);
  39. });
  40. /**
  41. * Fires when a new editor instance is added to the tinymce collection.
  42. *
  43. * @event onAddEditor
  44. * @param {tinymce} sender TinyMCE root class/namespace.
  45. * @param {tinymce.Editor} editor Editor instance.
  46. * @example
  47. * tinyMCE.execCommand("mceAddControl", false, "some_textarea");
  48. * tinyMCE.onAddEditor.add(function(mgr,ed) {
  49. * console.debug('A new editor is available' + ed.id);
  50. * });
  51. */
  52. tinymce.onAddEditor = new Dispatcher(tinymce);
  53. /**
  54. * Fires when an editor instance is removed from the tinymce collection.
  55. *
  56. * @event onRemoveEditor
  57. * @param {tinymce} sender TinyMCE root class/namespace.
  58. * @param {tinymce.Editor} editor Editor instance.
  59. */
  60. tinymce.onRemoveEditor = new Dispatcher(tinymce);
  61. tinymce.EditorManager = extend(tinymce, {
  62. /**
  63. * Collection of editor instances.
  64. *
  65. * @property editors
  66. * @type Object
  67. * @example
  68. * for (edId in tinyMCE.editors)
  69. * tinyMCE.editors[edId].save();
  70. */
  71. editors : [],
  72. /**
  73. * Collection of language pack data.
  74. *
  75. * @property i18n
  76. * @type Object
  77. */
  78. i18n : {},
  79. /**
  80. * Currently active editor instance.
  81. *
  82. * @property activeEditor
  83. * @type tinymce.Editor
  84. * @example
  85. * tinyMCE.activeEditor.selection.getContent();
  86. * tinymce.EditorManager.activeEditor.selection.getContent();
  87. */
  88. activeEditor : null,
  89. /**
  90. * Initializes a set of editors. This method will create a bunch of editors based in the input.
  91. *
  92. * @method init
  93. * @param {Object} s Settings object to be passed to each editor instance.
  94. * @example
  95. * // Initializes a editor using the longer method
  96. * tinymce.EditorManager.init({
  97. * some_settings : 'some value'
  98. * });
  99. *
  100. * // Initializes a editor instance using the shorter version
  101. * tinyMCE.init({
  102. * some_settings : 'some value'
  103. * });
  104. */
  105. init : function(s) {
  106. var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;
  107. function execCallback(se, n, s) {
  108. var f = se[n];
  109. if (!f)
  110. return;
  111. if (tinymce.is(f, 'string')) {
  112. s = f.replace(/\.\w+$/, '');
  113. s = s ? tinymce.resolve(s) : 0;
  114. f = tinymce.resolve(f);
  115. }
  116. return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
  117. };
  118. s = extend({
  119. theme : "simple",
  120. language : "en"
  121. }, s);
  122. t.settings = s;
  123. // Legacy call
  124. Event.add(document, 'init', function() {
  125. var l, co;
  126. execCallback(s, 'onpageload');
  127. switch (s.mode) {
  128. case "exact":
  129. l = s.elements || '';
  130. if(l.length > 0) {
  131. each(explode(l), function(v) {
  132. if (DOM.get(v)) {
  133. ed = new tinymce.Editor(v, s);
  134. el.push(ed);
  135. ed.render(1);
  136. } else {
  137. each(document.forms, function(f) {
  138. each(f.elements, function(e) {
  139. if (e.name === v) {
  140. v = 'mce_editor_' + instanceCounter++;
  141. DOM.setAttrib(e, 'id', v);
  142. ed = new tinymce.Editor(v, s);
  143. el.push(ed);
  144. ed.render(1);
  145. }
  146. });
  147. });
  148. }
  149. });
  150. }
  151. break;
  152. case "textareas":
  153. case "specific_textareas":
  154. function hasClass(n, c) {
  155. return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
  156. };
  157. each(DOM.select('textarea'), function(v) {
  158. if (s.editor_deselector && hasClass(v, s.editor_deselector))
  159. return;
  160. if (!s.editor_selector || hasClass(v, s.editor_selector)) {
  161. // Can we use the name
  162. e = DOM.get(v.name);
  163. if (!v.id && !e)
  164. v.id = v.name;
  165. // Generate unique name if missing or already exists
  166. if (!v.id || t.get(v.id))
  167. v.id = DOM.uniqueId();
  168. ed = new tinymce.Editor(v.id, s);
  169. el.push(ed);
  170. ed.render(1);
  171. }
  172. });
  173. break;
  174. }
  175. // Call onInit when all editors are initialized
  176. if (s.oninit) {
  177. l = co = 0;
  178. each(el, function(ed) {
  179. co++;
  180. if (!ed.initialized) {
  181. // Wait for it
  182. ed.onInit.add(function() {
  183. l++;
  184. // All done
  185. if (l == co)
  186. execCallback(s, 'oninit');
  187. });
  188. } else
  189. l++;
  190. // All done
  191. if (l == co)
  192. execCallback(s, 'oninit');
  193. });
  194. }
  195. });
  196. },
  197. /**
  198. * Returns a editor instance by id.
  199. *
  200. * @method get
  201. * @param {String/Number} id Editor instance id or index to return.
  202. * @return {tinymce.Editor} Editor instance to return.
  203. * @example
  204. * // Adds an onclick event to an editor by id (shorter version)
  205. * tinyMCE.get('mytextbox').onClick.add(function(ed, e) {
  206. * ed.windowManager.alert('Hello world!');
  207. * });
  208. *
  209. * // Adds an onclick event to an editor by id (longer version)
  210. * tinymce.EditorManager.get('mytextbox').onClick.add(function(ed, e) {
  211. * ed.windowManager.alert('Hello world!');
  212. * });
  213. */
  214. get : function(id) {
  215. if (id === undefined)
  216. return this.editors;
  217. return this.editors[id];
  218. },
  219. /**
  220. * Returns a editor instance by id. This method was added for compatibility with the 2.x branch.
  221. *
  222. * @method getInstanceById
  223. * @param {String} id Editor instance id to return.
  224. * @return {tinymce.Editor} Editor instance to return.
  225. * @deprecated Use get method instead.
  226. * @see #get
  227. */
  228. getInstanceById : function(id) {
  229. return this.get(id);
  230. },
  231. /**
  232. * Adds an editor instance to the editor collection. This will also set it as the active editor.
  233. *
  234. * @method add
  235. * @param {tinymce.Editor} editor Editor instance to add to the collection.
  236. * @return {tinymce.Editor} The same instance that got passed in.
  237. */
  238. add : function(editor) {
  239. var self = this, editors = self.editors;
  240. // Add named and index editor instance
  241. editors[editor.id] = editor;
  242. editors.push(editor);
  243. self._setActive(editor);
  244. self.onAddEditor.dispatch(self, editor);
  245. // #ifdef jquery
  246. // Patch the tinymce.Editor instance with jQuery adapter logic
  247. if (tinymce.adapter)
  248. tinymce.adapter.patchEditor(editor);
  249. // #endif
  250. return editor;
  251. },
  252. /**
  253. * Removes a editor instance from the collection.
  254. *
  255. * @method remove
  256. * @param {tinymce.Editor} e Editor instance to remove.
  257. * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
  258. */
  259. remove : function(editor) {
  260. var t = this, i, editors = t.editors;
  261. // Not in the collection
  262. if (!editors[editor.id])
  263. return null;
  264. delete editors[editor.id];
  265. for (i = 0; i < editors.length; i++) {
  266. if (editors[i] == editor) {
  267. editors.splice(i, 1);
  268. break;
  269. }
  270. }
  271. // Select another editor since the active one was removed
  272. if (t.activeEditor == editor)
  273. t._setActive(editors[0]);
  274. editor.destroy();
  275. t.onRemoveEditor.dispatch(t, editor);
  276. return editor;
  277. },
  278. /**
  279. * Executes a specific command on the currently active editor.
  280. *
  281. * @method execCommand
  282. * @param {String} c Command to perform for example Bold.
  283. * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
  284. * @param {String} v Optional value parameter like for example an URL to a link.
  285. * @return {Boolean} true/false if the command was executed or not.
  286. */
  287. execCommand : function(c, u, v) {
  288. var t = this, ed = t.get(v), w;
  289. // Manager commands
  290. switch (c) {
  291. case "mceFocus":
  292. ed.focus();
  293. return true;
  294. case "mceAddEditor":
  295. case "mceAddControl":
  296. if (!t.get(v))
  297. new tinymce.Editor(v, t.settings).render();
  298. return true;
  299. case "mceAddFrameControl":
  300. w = v.window;
  301. // Add tinyMCE global instance and tinymce namespace to specified window
  302. w.tinyMCE = tinyMCE;
  303. w.tinymce = tinymce;
  304. tinymce.DOM.doc = w.document;
  305. tinymce.DOM.win = w;
  306. ed = new tinymce.Editor(v.element_id, v);
  307. ed.render();
  308. // Fix IE memory leaks
  309. if (tinymce.isIE) {
  310. function clr() {
  311. ed.destroy();
  312. w.detachEvent('onunload', clr);
  313. w = w.tinyMCE = w.tinymce = null; // IE leak
  314. };
  315. w.attachEvent('onunload', clr);
  316. }
  317. v.page_window = null;
  318. return true;
  319. case "mceRemoveEditor":
  320. case "mceRemoveControl":
  321. if (ed)
  322. ed.remove();
  323. return true;
  324. case 'mceToggleEditor':
  325. if (!ed) {
  326. t.execCommand('mceAddControl', 0, v);
  327. return true;
  328. }
  329. if (ed.isHidden())
  330. ed.show();
  331. else
  332. ed.hide();
  333. return true;
  334. }
  335. // Run command on active editor
  336. if (t.activeEditor)
  337. return t.activeEditor.execCommand(c, u, v);
  338. return false;
  339. },
  340. /**
  341. * Executes a command on a specific editor by id. This method was added for compatibility with the 2.x branch.
  342. *
  343. * @deprecated Use the execCommand method of a editor instance instead.
  344. * @method execInstanceCommand
  345. * @param {String} id Editor id to perform the command on.
  346. * @param {String} c Command to perform for example Bold.
  347. * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
  348. * @param {String} v Optional value parameter like for example an URL to a link.
  349. * @return {Boolean} true/false if the command was executed or not.
  350. */
  351. execInstanceCommand : function(id, c, u, v) {
  352. var ed = this.get(id);
  353. if (ed)
  354. return ed.execCommand(c, u, v);
  355. return false;
  356. },
  357. /**
  358. * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
  359. *
  360. * @method triggerSave
  361. * @example
  362. * // Saves all contents
  363. * tinyMCE.triggerSave();
  364. */
  365. triggerSave : function() {
  366. each(this.editors, function(e) {
  367. e.save();
  368. });
  369. },
  370. /**
  371. * Adds a language pack, this gets called by the loaded language files like en.js.
  372. *
  373. * @method addI18n
  374. * @param {String} p Prefix for the language items. For example en.myplugin
  375. * @param {Object} o Name/Value collection with items to add to the language group.
  376. */
  377. addI18n : function(p, o) {
  378. var lo, i18n = this.i18n;
  379. if (!tinymce.is(p, 'string')) {
  380. each(p, function(o, lc) {
  381. each(o, function(o, g) {
  382. each(o, function(o, k) {
  383. if (g === 'common')
  384. i18n[lc + '.' + k] = o;
  385. else
  386. i18n[lc + '.' + g + '.' + k] = o;
  387. });
  388. });
  389. });
  390. } else {
  391. each(o, function(o, k) {
  392. i18n[p + '.' + k] = o;
  393. });
  394. }
  395. },
  396. // Private methods
  397. _setActive : function(editor) {
  398. this.selectedInstance = this.activeEditor = editor;
  399. }
  400. });
  401. })(tinymce);
  402. /**
  403. * Alternative name for tinymce added for 2.x compatibility.
  404. *
  405. * @member
  406. * @property tinyMCE
  407. * @type tinymce
  408. * @example
  409. * // To initialize editor instances
  410. * tinyMCE.init({
  411. * ...
  412. * });
  413. */
  414. /**
  415. * Alternative name for tinymce added for compatibility.
  416. *
  417. * @member tinymce
  418. * @property EditorManager
  419. * @type tinymce
  420. * @example
  421. * // To initialize editor instances
  422. * tinymce.EditorManager.get('editor');
  423. */