tinymce-4.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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.on('AddEditor', function (e) {
  11. e.editor.on('FullscreenStateChanged', function (e) {
  12. if (e.state) {
  13. $drupalToolbars.hide();
  14. }
  15. else {
  16. $drupalToolbars.show();
  17. }
  18. });
  19. });
  20. // Register new plugins.
  21. Drupal.wysiwyg.editor.update.tinymce(settings, pluginInfo);
  22. };
  23. /**
  24. * Update the editor library when new settings are available.
  25. *
  26. * @see Drupal.wysiwyg.editor.update.ckeditor()
  27. */
  28. Drupal.wysiwyg.editor.update.tinymce = function (settings, pluginInfo) {
  29. // Load native external plugins.
  30. // Array syntax required; 'native' is a predefined token in JavaScript.
  31. var plugin;
  32. for (plugin in pluginInfo['native']) {
  33. if (!(plugin in tinymce.PluginManager.lookup || plugin in tinymce.PluginManager.urls)) {
  34. tinymce.PluginManager.load(plugin, pluginInfo['native'][plugin]);
  35. }
  36. }
  37. // Load Drupal plugins.
  38. for (plugin in pluginInfo.drupal) {
  39. if (!(plugin in tinymce.PluginManager.lookup)) {
  40. Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, pluginInfo.drupal[plugin]);
  41. }
  42. }
  43. };
  44. /**
  45. * Attach this editor to a target element.
  46. *
  47. * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook.
  48. */
  49. Drupal.wysiwyg.editor.attach.tinymce = function (context, params, settings) {
  50. // Remove TinyMCE's internal mceItem class, which was incorrectly added to
  51. // submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
  52. // for placeholder elements. If preemptively set, the class prevents (native)
  53. // editor plugins from gaining an active state, so we have to manually remove
  54. // it prior to attaching the editor. This is done on the client-side instead
  55. // of the server-side, as Wysiwyg has no way to figure out where content is
  56. // stored, and the class only affects editing.
  57. var $field = $('#' + params.field);
  58. $field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));
  59. // Attach editor.
  60. settings.selector = '#' + params.field;
  61. var oldSetup = settings.setup;
  62. settings.setup = function (editor) {
  63. editor.on('focus', function (e) {
  64. Drupal.wysiwyg.activeId = this.id;
  65. });
  66. if (oldSetup) {
  67. oldSetup(editor);
  68. }
  69. };
  70. tinymce.init(settings);
  71. };
  72. /**
  73. * Detach a single or all editors.
  74. *
  75. * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
  76. */
  77. Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
  78. var instance;
  79. if (typeof params !== 'undefined') {
  80. instance = tinymce.get(params.field);
  81. if (instance) {
  82. instance.save();
  83. if (trigger !== 'serialize') {
  84. instance.remove();
  85. }
  86. }
  87. }
  88. else {
  89. // Save contents of all editors back into textareas.
  90. tinymce.triggerSave();
  91. if (trigger !== 'serialize') {
  92. // Remove all editor instances.
  93. for (instance in tinymce.editors) {
  94. if (!tinymce.editors.hasOwnProperty(instance)) {
  95. continue;
  96. }
  97. tinymce.editors[instance].remove();
  98. }
  99. }
  100. }
  101. };
  102. Drupal.wysiwyg.editor.instance.tinymce = {
  103. addPlugin: function (plugin, pluginSettings) {
  104. if (typeof Drupal.wysiwyg.plugins[plugin] !== 'object') {
  105. return;
  106. }
  107. // Register plugin.
  108. tinymce.PluginManager.add('drupal_' + plugin, function (editor) {
  109. var button = {
  110. title: pluginSettings.title,
  111. image: pluginSettings.icon,
  112. onPostRender: function () {
  113. var self = this;
  114. editor.on('nodeChange', function (e) {
  115. // isNode: Return whether the plugin button should be enabled for
  116. // the current selection.
  117. if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
  118. self.active(Drupal.wysiwyg.plugins[plugin].isNode(e.element));
  119. }
  120. });
  121. }
  122. };
  123. if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
  124. button.onclick = function () {
  125. var data = {format: 'html', node: editor.selection.getNode(), content: editor.selection.getContent()};
  126. // TinyMCE creates a completely new instance for fullscreen mode.
  127. Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, editor.id);
  128. };
  129. }
  130. // Register the plugin button.
  131. editor.addButton('drupal_' + plugin, button);
  132. /**
  133. * Initialize the plugin, executed after the plugin has been created.
  134. *
  135. * @param ed
  136. * The tinymce.Editor instance the plugin is initialized in.
  137. * @param url
  138. * The absolute URL of the plugin location.
  139. */
  140. editor.on('init', function (e) {
  141. // Load custom CSS for editor contents on startup.
  142. if (pluginSettings.css) {
  143. editor.dom.loadCSS(pluginSettings.css);
  144. }
  145. });
  146. // Attach: Replace plain text with HTML representations.
  147. editor.on('beforeSetContent', function (e) {
  148. if (typeof Drupal.wysiwyg.plugins[plugin].attach === 'function') {
  149. e.content = Drupal.wysiwyg.plugins[plugin].attach(e.content, pluginSettings, e.target.id);
  150. e.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(e.content);
  151. }
  152. });
  153. // Detach: Replace HTML representations with plain text.
  154. editor.on('getContent', function (e) {
  155. var editorId = (e.target.id === 'mce_fullscreen' ? e.target.getParam('fullscreen_editor_id') : e.target.id);
  156. if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
  157. e.content = Drupal.wysiwyg.plugins[plugin].detach(e.content, pluginSettings, editorId);
  158. }
  159. });
  160. });
  161. },
  162. openDialog: function (dialog, params) {
  163. var instanceId = this.getInstanceId();
  164. var editor = tinymce.get(instanceId);
  165. editor.windowManager.open({
  166. file: dialog.url + '/' + instanceId,
  167. width: dialog.width,
  168. height: dialog.height,
  169. inline: 1
  170. }, params);
  171. },
  172. closeDialog: function (dialog) {
  173. var editor = tinymce.get(this.getInstanceId());
  174. editor.windowManager.close(dialog);
  175. },
  176. prepareContent: function (content) {
  177. // Certain content elements need to have additional DOM properties applied
  178. // to prevent this editor from highlighting an internal button in addition
  179. // to the button of a Drupal plugin.
  180. var specialProperties = {
  181. img: {class: 'mceItem'}
  182. };
  183. // No .outerHTML() in jQuery :(
  184. var $content = $('<div>' + content + '</div>');
  185. // Find all placeholder/replacement content of Drupal plugins.
  186. $content.find('.drupal-content').each(function () {
  187. // Recursively process DOM elements below this element to apply special
  188. // properties.
  189. var $drupalContent = $(this);
  190. $.each(specialProperties, function (element, properties) {
  191. $drupalContent.find(element).andSelf().each(function () {
  192. for (var property in properties) {
  193. if (property === 'class') {
  194. $(this).addClass(properties[property]);
  195. }
  196. else {
  197. $(this).attr(property, properties[property]);
  198. }
  199. }
  200. });
  201. });
  202. });
  203. return $content.html();
  204. },
  205. insert: function (content) {
  206. content = this.prepareContent(content);
  207. tinymce.get(this.field).insertContent(content);
  208. },
  209. setContent: function (content) {
  210. content = this.prepareContent(content);
  211. tinymce.get(this.field).setContent(content);
  212. },
  213. getContent: function () {
  214. return tinymce.get(this.getInstanceId()).getContent();
  215. },
  216. isFullscreen: function () {
  217. var editor = tinymce.get(this.field);
  218. return editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
  219. },
  220. getInstanceId: function () {
  221. return this.field;
  222. }
  223. };
  224. })(jQuery);