tinymce-3.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. (function($) {
  2. /**
  3. * Initialize editor instances.
  4. *
  5. * @see Drupal.wysiwyg.editor.init.ckeditor()
  6. */
  7. Drupal.wysiwyg.editor.init.tinymce = function(settings, pluginInfo) {
  8. // Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
  9. var $drupalToolbars = $('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.document : document);
  10. tinyMCE.onAddEditor.add(function (mgr, ed) {
  11. if (ed.id == 'mce_fullscreen') {
  12. $drupalToolbars.hide();
  13. }
  14. });
  15. tinyMCE.onRemoveEditor.add(function (mgr, ed) {
  16. if (ed.id == 'mce_fullscreen') {
  17. $drupalToolbars.show();
  18. }
  19. else {
  20. // Free our reference to the private instance to not risk memory leaks.
  21. delete ed._drupalWysiwygInstance;
  22. }
  23. });
  24. // Register new plugins.
  25. Drupal.wysiwyg.editor.update.tinymce(settings, pluginInfo);
  26. };
  27. /**
  28. * Update the editor library when new settings are available.
  29. *
  30. * @see Drupal.wysiwyg.editor.update.ckeditor()
  31. */
  32. Drupal.wysiwyg.editor.update.tinymce = function(settings, pluginInfo) {
  33. // Load native external plugins.
  34. // Array syntax required; 'native' is a predefined token in JavaScript.
  35. for (var plugin in pluginInfo['native']) {
  36. if (!(plugin in tinymce.PluginManager.lookup || plugin in tinymce.PluginManager.urls)) {
  37. tinymce.PluginManager.load(plugin, pluginInfo['native'][plugin]);
  38. }
  39. }
  40. // Load Drupal plugins.
  41. for (var plugin in pluginInfo.drupal) {
  42. if (!(plugin in tinymce.PluginManager.lookup)) {
  43. Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, pluginInfo.drupal[plugin]);
  44. }
  45. }
  46. };
  47. /**
  48. * Attach this editor to a target element.
  49. *
  50. * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook.
  51. */
  52. Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
  53. // Configure editor settings for this input format.
  54. var ed = new tinymce.Editor(params.field, settings);
  55. ed._drupalWysiwygInstance = this;
  56. // Reset active instance id on any event.
  57. ed.onEvent.add(function(ed, e) {
  58. Drupal.wysiwyg.activeId = ed.id;
  59. });
  60. // Indicate that the DOM has been loaded (in case of Ajax).
  61. tinymce.dom.Event.domLoaded = true;
  62. // Make toolbar buttons wrappable (required for IE).
  63. ed.onPostRender.add(function (ed) {
  64. var $toolbar = $('<div class="wysiwygToolbar"></div>');
  65. $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function () {
  66. $('<div></div>').addClass(this.className).append($(this).children()).appendTo($toolbar);
  67. });
  68. $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar);
  69. $('#' + ed.editorContainer + ' table.mceToolbar').remove();
  70. });
  71. // Remove TinyMCE's internal mceItem class, which was incorrectly added to
  72. // submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
  73. // for placeholder elements. If preemptively set, the class prevents (native)
  74. // editor plugins from gaining an active state, so we have to manually remove
  75. // it prior to attaching the editor. This is done on the client-side instead
  76. // of the server-side, as Wysiwyg has no way to figure out where content is
  77. // stored, and the class only affects editing.
  78. var $field = $('#' + params.field);
  79. $field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));
  80. // Attach editor.
  81. ed.render();
  82. if (tinymce.minorVersion == '5.7') {
  83. // Work around a TinyMCE bug hiding new instances when switching to them.
  84. // @see http://www.tinymce.com/develop/bugtracker_view.php?id=5510
  85. setTimeout(function () {
  86. tinymce.DOM.show(ed.editorContainer);
  87. }, 1);
  88. }
  89. };
  90. /**
  91. * Detach a single editor instance.
  92. *
  93. * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
  94. */
  95. Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
  96. var instance = tinyMCE.get(params.field);
  97. if (!instance) {
  98. return;
  99. }
  100. instance.save();
  101. if (trigger !== 'serialize') {
  102. // The onRemove event fires before this returns.
  103. instance.remove();
  104. }
  105. };
  106. Drupal.wysiwyg.editor.instance.tinymce = {
  107. addPlugin: function(plugin, pluginSettings) {
  108. if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
  109. return;
  110. }
  111. tinymce.create('tinymce.plugins.drupal_' + plugin, {
  112. /**
  113. * Initialize the plugin, executed after the plugin has been created.
  114. *
  115. * @param ed
  116. * The tinymce.Editor instance the plugin is initialized in.
  117. * @param url
  118. * The absolute URL of the plugin location.
  119. */
  120. init: function(ed, url) {
  121. // Register an editor command for this plugin, invoked by the plugin's button.
  122. ed.addCommand('drupal_' + plugin, function() {
  123. if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
  124. var data = { format: 'html', node: ed.selection.getNode(), content: ed.selection.getContent() };
  125. // TinyMCE creates a completely new instance for fullscreen mode.
  126. var instanceId = ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id;
  127. Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instanceId);
  128. }
  129. });
  130. // Register the plugin button.
  131. ed.addButton('drupal_' + plugin, {
  132. title : pluginSettings.title,
  133. cmd : 'drupal_' + plugin,
  134. image : pluginSettings.icon
  135. });
  136. // Load custom CSS for editor contents on startup.
  137. ed.onInit.add(function() {
  138. if (pluginSettings.css) {
  139. ed.dom.loadCSS(pluginSettings.css);
  140. }
  141. });
  142. // Attach: Replace plain text with HTML representations.
  143. ed.onBeforeSetContent.add(function(ed, data) {
  144. var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
  145. if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
  146. data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, editorId);
  147. data.content = ed._drupalWysiwygInstance.prepareContent(data.content);
  148. }
  149. });
  150. // Detach: Replace HTML representations with plain text.
  151. ed.onGetContent.add(function(ed, data) {
  152. var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
  153. if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
  154. data.content = Drupal.wysiwyg.plugins[plugin].detach(data.content, pluginSettings, editorId);
  155. }
  156. });
  157. // isNode: Return whether the plugin button should be enabled for the
  158. // current selection.
  159. ed.onNodeChange.add(function(ed, command, node) {
  160. if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
  161. command.setActive(plugin, Drupal.wysiwyg.plugins[plugin].isNode(node));
  162. }
  163. });
  164. },
  165. /**
  166. * Return information about the plugin as a name/value array.
  167. */
  168. getInfo: function() {
  169. return {
  170. longname: pluginSettings.title
  171. };
  172. }
  173. });
  174. // Register plugin.
  175. tinymce.PluginManager.add('drupal_' + plugin, tinymce.plugins['drupal_' + plugin]);
  176. },
  177. openDialog: function(dialog, params) {
  178. var instanceId = this.getInstanceId();
  179. var editor = tinyMCE.get(instanceId);
  180. editor.windowManager.open({
  181. file: dialog.url + '/' + instanceId,
  182. width: dialog.width,
  183. height: dialog.height,
  184. inline: 1
  185. }, params);
  186. },
  187. closeDialog: function(dialog) {
  188. var editor = tinyMCE.get(this.getInstanceId());
  189. editor.windowManager.close(dialog);
  190. },
  191. prepareContent: function(content) {
  192. // Certain content elements need to have additional DOM properties applied
  193. // to prevent this editor from highlighting an internal button in addition
  194. // to the button of a Drupal plugin.
  195. var specialProperties = {
  196. img: { 'class': 'mceItem' }
  197. };
  198. var $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
  199. // Find all placeholder/replacement content of Drupal plugins.
  200. $content.find('.drupal-content').each(function() {
  201. // Recursively process DOM elements below this element to apply special
  202. // properties.
  203. var $drupalContent = $(this);
  204. $.each(specialProperties, function(element, properties) {
  205. $drupalContent.find(element).andSelf().each(function() {
  206. for (var property in properties) {
  207. if (property == 'class') {
  208. $(this).addClass(properties[property]);
  209. }
  210. else {
  211. $(this).attr(property, properties[property]);
  212. }
  213. }
  214. });
  215. });
  216. });
  217. return $content.html();
  218. },
  219. insert: function(content) {
  220. content = this.prepareContent(content);
  221. tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceInsertContent', false, content);
  222. },
  223. setContent: function (content) {
  224. content = this.prepareContent(content);
  225. tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceSetContent', false, content);
  226. },
  227. getContent: function () {
  228. return tinyMCE.get(this.getInstanceId()).getContent();
  229. },
  230. isFullscreen: function() {
  231. // TinyMCE creates a completely new instance for fullscreen mode.
  232. return tinyMCE.activeEditor.id == 'mce_fullscreen' && tinyMCE.activeEditor.getParam('fullscreen_editor_id') == this.field;
  233. },
  234. getInstanceId: function () {
  235. return this.isFullscreen() ? 'mce_fullscreen' : this.field;
  236. }
  237. };
  238. })(jQuery);