plugin.js 7.4 KB

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