plugin.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /**
  2. * Copyright (c) Tiny Technologies, Inc. All rights reserved.
  3. * Licensed under the LGPL or a commercial license.
  4. * For LGPL see License.txt in the project root for license information.
  5. * For commercial licenses see https://www.tiny.cloud/
  6. *
  7. * Version: 5.0.1 (2019-02-21)
  8. */
  9. (function () {
  10. var toc = (function () {
  11. 'use strict';
  12. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  13. var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
  14. var global$2 = tinymce.util.Tools.resolve('tinymce.util.I18n');
  15. var global$3 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  16. var getTocClass = function (editor) {
  17. return editor.getParam('toc_class', 'mce-toc');
  18. };
  19. var getTocHeader = function (editor) {
  20. var tagName = editor.getParam('toc_header', 'h2');
  21. return /^h[1-6]$/.test(tagName) ? tagName : 'h2';
  22. };
  23. var getTocDepth = function (editor) {
  24. var depth = parseInt(editor.getParam('toc_depth', '3'), 10);
  25. return depth >= 1 && depth <= 9 ? depth : 3;
  26. };
  27. var Settings = {
  28. getTocClass: getTocClass,
  29. getTocHeader: getTocHeader,
  30. getTocDepth: getTocDepth
  31. };
  32. var create = function (prefix) {
  33. var counter = 0;
  34. return function () {
  35. var guid = new Date().getTime().toString(32);
  36. return prefix + guid + (counter++).toString(32);
  37. };
  38. };
  39. var Guid = { create: create };
  40. var tocId = Guid.create('mcetoc_');
  41. var generateSelector = function generateSelector(depth) {
  42. var i;
  43. var selector = [];
  44. for (i = 1; i <= depth; i++) {
  45. selector.push('h' + i);
  46. }
  47. return selector.join(',');
  48. };
  49. var hasHeaders = function (editor) {
  50. return readHeaders(editor).length > 0;
  51. };
  52. var readHeaders = function (editor) {
  53. var tocClass = Settings.getTocClass(editor);
  54. var headerTag = Settings.getTocHeader(editor);
  55. var selector = generateSelector(Settings.getTocDepth(editor));
  56. var headers = editor.$(selector);
  57. if (headers.length && /^h[1-9]$/i.test(headerTag)) {
  58. headers = headers.filter(function (i, el) {
  59. return !editor.dom.hasClass(el.parentNode, tocClass);
  60. });
  61. }
  62. return global$3.map(headers, function (h) {
  63. return {
  64. id: h.id ? h.id : tocId(),
  65. level: parseInt(h.nodeName.replace(/^H/i, ''), 10),
  66. title: editor.$.text(h),
  67. element: h
  68. };
  69. });
  70. };
  71. var getMinLevel = function (headers) {
  72. var i, minLevel = 9;
  73. for (i = 0; i < headers.length; i++) {
  74. if (headers[i].level < minLevel) {
  75. minLevel = headers[i].level;
  76. }
  77. if (minLevel === 1) {
  78. return minLevel;
  79. }
  80. }
  81. return minLevel;
  82. };
  83. var generateTitle = function (tag, title) {
  84. var openTag = '<' + tag + ' contenteditable="true">';
  85. var closeTag = '</' + tag + '>';
  86. return openTag + global$1.DOM.encode(title) + closeTag;
  87. };
  88. var generateTocHtml = function (editor) {
  89. var html = generateTocContentHtml(editor);
  90. return '<div class="' + editor.dom.encode(Settings.getTocClass(editor)) + '" contenteditable="false">' + html + '</div>';
  91. };
  92. var generateTocContentHtml = function (editor) {
  93. var html = '';
  94. var headers = readHeaders(editor);
  95. var prevLevel = getMinLevel(headers) - 1;
  96. var i, ii, h, nextLevel;
  97. if (!headers.length) {
  98. return '';
  99. }
  100. html += generateTitle(Settings.getTocHeader(editor), global$2.translate('Table of Contents'));
  101. for (i = 0; i < headers.length; i++) {
  102. h = headers[i];
  103. h.element.id = h.id;
  104. nextLevel = headers[i + 1] && headers[i + 1].level;
  105. if (prevLevel === h.level) {
  106. html += '<li>';
  107. } else {
  108. for (ii = prevLevel; ii < h.level; ii++) {
  109. html += '<ul><li>';
  110. }
  111. }
  112. html += '<a href="#' + h.id + '">' + h.title + '</a>';
  113. if (nextLevel === h.level || !nextLevel) {
  114. html += '</li>';
  115. if (!nextLevel) {
  116. html += '</ul>';
  117. }
  118. } else {
  119. for (ii = h.level; ii > nextLevel; ii--) {
  120. html += '</li></ul><li>';
  121. }
  122. }
  123. prevLevel = h.level;
  124. }
  125. return html;
  126. };
  127. var isEmptyOrOffscren = function (editor, nodes) {
  128. return !nodes.length || editor.dom.getParents(nodes[0], '.mce-offscreen-selection').length > 0;
  129. };
  130. var insertToc = function (editor) {
  131. var tocClass = Settings.getTocClass(editor);
  132. var $tocElm = editor.$('.' + tocClass);
  133. if (isEmptyOrOffscren(editor, $tocElm)) {
  134. editor.insertContent(generateTocHtml(editor));
  135. } else {
  136. updateToc(editor);
  137. }
  138. };
  139. var updateToc = function (editor) {
  140. var tocClass = Settings.getTocClass(editor);
  141. var $tocElm = editor.$('.' + tocClass);
  142. if ($tocElm.length) {
  143. editor.undoManager.transact(function () {
  144. $tocElm.html(generateTocContentHtml(editor));
  145. });
  146. }
  147. };
  148. var Toc = {
  149. hasHeaders: hasHeaders,
  150. insertToc: insertToc,
  151. updateToc: updateToc
  152. };
  153. var register = function (editor) {
  154. editor.addCommand('mceInsertToc', function () {
  155. Toc.insertToc(editor);
  156. });
  157. editor.addCommand('mceUpdateToc', function () {
  158. Toc.updateToc(editor);
  159. });
  160. };
  161. var Commands = { register: register };
  162. var setup = function (editor) {
  163. var $ = editor.$, tocClass = Settings.getTocClass(editor);
  164. editor.on('PreProcess', function (e) {
  165. var $tocElm = $('.' + tocClass, e.node);
  166. if ($tocElm.length) {
  167. $tocElm.removeAttr('contentEditable');
  168. $tocElm.find('[contenteditable]').removeAttr('contentEditable');
  169. }
  170. });
  171. editor.on('SetContent', function () {
  172. var $tocElm = $('.' + tocClass);
  173. if ($tocElm.length) {
  174. $tocElm.attr('contentEditable', false);
  175. $tocElm.children(':first-child').attr('contentEditable', true);
  176. }
  177. });
  178. };
  179. var FilterContent = { setup: setup };
  180. var toggleState = function (editor) {
  181. return function (api) {
  182. var toggleDisabledState = function () {
  183. return api.setDisabled(editor.readonly || !Toc.hasHeaders(editor));
  184. };
  185. toggleDisabledState();
  186. editor.on('LoadContent SetContent change', toggleDisabledState);
  187. return function () {
  188. return editor.on('LoadContent SetContent change', toggleDisabledState);
  189. };
  190. };
  191. };
  192. var isToc = function (editor) {
  193. return function (elm) {
  194. return elm && editor.dom.is(elm, '.' + Settings.getTocClass(editor)) && editor.getBody().contains(elm);
  195. };
  196. };
  197. var register$1 = function (editor) {
  198. editor.ui.registry.addButton('toc', {
  199. icon: 'toc',
  200. tooltip: 'Table of contents',
  201. onAction: function () {
  202. return editor.execCommand('mceInsertToc');
  203. },
  204. onSetup: toggleState(editor)
  205. });
  206. editor.ui.registry.addButton('tocupdate', {
  207. icon: 'reload',
  208. tooltip: 'Update',
  209. onAction: function () {
  210. return editor.execCommand('mceUpdateToc');
  211. }
  212. });
  213. editor.ui.registry.addMenuItem('toc', {
  214. icon: 'toc',
  215. text: 'Table of contents',
  216. onAction: function () {
  217. return editor.execCommand('mceInsertToc');
  218. },
  219. onSetup: toggleState(editor)
  220. });
  221. editor.ui.registry.addContextToolbar('toc', {
  222. items: 'tocupdate',
  223. predicate: isToc(editor),
  224. scope: 'node',
  225. position: 'node'
  226. });
  227. };
  228. var Buttons = { register: register$1 };
  229. global.add('toc', function (editor) {
  230. Commands.register(editor);
  231. Buttons.register(editor);
  232. FilterContent.setup(editor);
  233. });
  234. function Plugin () {
  235. }
  236. return Plugin;
  237. }());
  238. })();