plugin.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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 Cell = function (initial) {
  12. var value = initial;
  13. var get = function () {
  14. return value;
  15. };
  16. var set = function (v) {
  17. value = v;
  18. };
  19. return {
  20. get: get,
  21. set: set
  22. };
  23. };
  24. var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  25. var get$2 = function (toggleState) {
  26. var isEnabled = function () {
  27. return toggleState.get();
  28. };
  29. return { isEnabled: isEnabled };
  30. };
  31. var fireVisualChars = function (editor, state) {
  32. return editor.fire('VisualChars', { state: state });
  33. };
  34. var typeOf = function (x) {
  35. var t = typeof x;
  36. if (x === null) {
  37. return 'null';
  38. } else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  39. return 'array';
  40. } else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  41. return 'string';
  42. } else {
  43. return t;
  44. }
  45. };
  46. var isType$1 = function (type) {
  47. return function (value) {
  48. return typeOf(value) === type;
  49. };
  50. };
  51. var isSimpleType = function (type) {
  52. return function (value) {
  53. return typeof value === type;
  54. };
  55. };
  56. var isString = isType$1('string');
  57. var isBoolean = isSimpleType('boolean');
  58. var isNumber = isSimpleType('number');
  59. var noop = function () {
  60. };
  61. var constant = function (value) {
  62. return function () {
  63. return value;
  64. };
  65. };
  66. var identity = function (x) {
  67. return x;
  68. };
  69. var never = constant(false);
  70. var always = constant(true);
  71. var none = function () {
  72. return NONE;
  73. };
  74. var NONE = function () {
  75. var call = function (thunk) {
  76. return thunk();
  77. };
  78. var id = identity;
  79. var me = {
  80. fold: function (n, _s) {
  81. return n();
  82. },
  83. isSome: never,
  84. isNone: always,
  85. getOr: id,
  86. getOrThunk: call,
  87. getOrDie: function (msg) {
  88. throw new Error(msg || 'error: getOrDie called on none.');
  89. },
  90. getOrNull: constant(null),
  91. getOrUndefined: constant(undefined),
  92. or: id,
  93. orThunk: call,
  94. map: none,
  95. each: noop,
  96. bind: none,
  97. exists: never,
  98. forall: always,
  99. filter: function () {
  100. return none();
  101. },
  102. toArray: function () {
  103. return [];
  104. },
  105. toString: constant('none()')
  106. };
  107. return me;
  108. }();
  109. var some = function (a) {
  110. var constant_a = constant(a);
  111. var self = function () {
  112. return me;
  113. };
  114. var bind = function (f) {
  115. return f(a);
  116. };
  117. var me = {
  118. fold: function (n, s) {
  119. return s(a);
  120. },
  121. isSome: always,
  122. isNone: never,
  123. getOr: constant_a,
  124. getOrThunk: constant_a,
  125. getOrDie: constant_a,
  126. getOrNull: constant_a,
  127. getOrUndefined: constant_a,
  128. or: self,
  129. orThunk: self,
  130. map: function (f) {
  131. return some(f(a));
  132. },
  133. each: function (f) {
  134. f(a);
  135. },
  136. bind: bind,
  137. exists: bind,
  138. forall: bind,
  139. filter: function (f) {
  140. return f(a) ? me : NONE;
  141. },
  142. toArray: function () {
  143. return [a];
  144. },
  145. toString: function () {
  146. return 'some(' + a + ')';
  147. }
  148. };
  149. return me;
  150. };
  151. var from = function (value) {
  152. return value === null || value === undefined ? NONE : some(value);
  153. };
  154. var Optional = {
  155. some: some,
  156. none: none,
  157. from: from
  158. };
  159. var map = function (xs, f) {
  160. var len = xs.length;
  161. var r = new Array(len);
  162. for (var i = 0; i < len; i++) {
  163. var x = xs[i];
  164. r[i] = f(x, i);
  165. }
  166. return r;
  167. };
  168. var each$1 = function (xs, f) {
  169. for (var i = 0, len = xs.length; i < len; i++) {
  170. var x = xs[i];
  171. f(x, i);
  172. }
  173. };
  174. var filter = function (xs, pred) {
  175. var r = [];
  176. for (var i = 0, len = xs.length; i < len; i++) {
  177. var x = xs[i];
  178. if (pred(x, i)) {
  179. r.push(x);
  180. }
  181. }
  182. return r;
  183. };
  184. var keys = Object.keys;
  185. var each = function (obj, f) {
  186. var props = keys(obj);
  187. for (var k = 0, len = props.length; k < len; k++) {
  188. var i = props[k];
  189. var x = obj[i];
  190. f(x, i);
  191. }
  192. };
  193. typeof window !== 'undefined' ? window : Function('return this;')();
  194. var TEXT = 3;
  195. var type = function (element) {
  196. return element.dom.nodeType;
  197. };
  198. var value = function (element) {
  199. return element.dom.nodeValue;
  200. };
  201. var isType = function (t) {
  202. return function (element) {
  203. return type(element) === t;
  204. };
  205. };
  206. var isText = isType(TEXT);
  207. var rawSet = function (dom, key, value) {
  208. if (isString(value) || isBoolean(value) || isNumber(value)) {
  209. dom.setAttribute(key, value + '');
  210. } else {
  211. console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
  212. throw new Error('Attribute value was not simple');
  213. }
  214. };
  215. var set = function (element, key, value) {
  216. rawSet(element.dom, key, value);
  217. };
  218. var get$1 = function (element, key) {
  219. var v = element.dom.getAttribute(key);
  220. return v === null ? undefined : v;
  221. };
  222. var remove$3 = function (element, key) {
  223. element.dom.removeAttribute(key);
  224. };
  225. var read = function (element, attr) {
  226. var value = get$1(element, attr);
  227. return value === undefined || value === '' ? [] : value.split(' ');
  228. };
  229. var add$2 = function (element, attr, id) {
  230. var old = read(element, attr);
  231. var nu = old.concat([id]);
  232. set(element, attr, nu.join(' '));
  233. return true;
  234. };
  235. var remove$2 = function (element, attr, id) {
  236. var nu = filter(read(element, attr), function (v) {
  237. return v !== id;
  238. });
  239. if (nu.length > 0) {
  240. set(element, attr, nu.join(' '));
  241. } else {
  242. remove$3(element, attr);
  243. }
  244. return false;
  245. };
  246. var supports = function (element) {
  247. return element.dom.classList !== undefined;
  248. };
  249. var get = function (element) {
  250. return read(element, 'class');
  251. };
  252. var add$1 = function (element, clazz) {
  253. return add$2(element, 'class', clazz);
  254. };
  255. var remove$1 = function (element, clazz) {
  256. return remove$2(element, 'class', clazz);
  257. };
  258. var add = function (element, clazz) {
  259. if (supports(element)) {
  260. element.dom.classList.add(clazz);
  261. } else {
  262. add$1(element, clazz);
  263. }
  264. };
  265. var cleanClass = function (element) {
  266. var classList = supports(element) ? element.dom.classList : get(element);
  267. if (classList.length === 0) {
  268. remove$3(element, 'class');
  269. }
  270. };
  271. var remove = function (element, clazz) {
  272. if (supports(element)) {
  273. var classList = element.dom.classList;
  274. classList.remove(clazz);
  275. } else {
  276. remove$1(element, clazz);
  277. }
  278. cleanClass(element);
  279. };
  280. var fromHtml = function (html, scope) {
  281. var doc = scope || document;
  282. var div = doc.createElement('div');
  283. div.innerHTML = html;
  284. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  285. console.error('HTML does not have a single root node', html);
  286. throw new Error('HTML must have a single root node');
  287. }
  288. return fromDom(div.childNodes[0]);
  289. };
  290. var fromTag = function (tag, scope) {
  291. var doc = scope || document;
  292. var node = doc.createElement(tag);
  293. return fromDom(node);
  294. };
  295. var fromText = function (text, scope) {
  296. var doc = scope || document;
  297. var node = doc.createTextNode(text);
  298. return fromDom(node);
  299. };
  300. var fromDom = function (node) {
  301. if (node === null || node === undefined) {
  302. throw new Error('Node cannot be null or undefined');
  303. }
  304. return { dom: node };
  305. };
  306. var fromPoint = function (docElm, x, y) {
  307. return Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
  308. };
  309. var SugarElement = {
  310. fromHtml: fromHtml,
  311. fromTag: fromTag,
  312. fromText: fromText,
  313. fromDom: fromDom,
  314. fromPoint: fromPoint
  315. };
  316. var charMap = {
  317. '\xA0': 'nbsp',
  318. '\xAD': 'shy'
  319. };
  320. var charMapToRegExp = function (charMap, global) {
  321. var regExp = '';
  322. each(charMap, function (_value, key) {
  323. regExp += key;
  324. });
  325. return new RegExp('[' + regExp + ']', global ? 'g' : '');
  326. };
  327. var charMapToSelector = function (charMap) {
  328. var selector = '';
  329. each(charMap, function (value) {
  330. if (selector) {
  331. selector += ',';
  332. }
  333. selector += 'span.mce-' + value;
  334. });
  335. return selector;
  336. };
  337. var regExp = charMapToRegExp(charMap);
  338. var regExpGlobal = charMapToRegExp(charMap, true);
  339. var selector = charMapToSelector(charMap);
  340. var nbspClass = 'mce-nbsp';
  341. var wrapCharWithSpan = function (value) {
  342. return '<span data-mce-bogus="1" class="mce-' + charMap[value] + '">' + value + '</span>';
  343. };
  344. var isMatch = function (n) {
  345. var value$1 = value(n);
  346. return isText(n) && value$1 !== undefined && regExp.test(value$1);
  347. };
  348. var filterDescendants = function (scope, predicate) {
  349. var result = [];
  350. var dom = scope.dom;
  351. var children = map(dom.childNodes, SugarElement.fromDom);
  352. each$1(children, function (x) {
  353. if (predicate(x)) {
  354. result = result.concat([x]);
  355. }
  356. result = result.concat(filterDescendants(x, predicate));
  357. });
  358. return result;
  359. };
  360. var findParentElm = function (elm, rootElm) {
  361. while (elm.parentNode) {
  362. if (elm.parentNode === rootElm) {
  363. return elm;
  364. }
  365. elm = elm.parentNode;
  366. }
  367. };
  368. var replaceWithSpans = function (text) {
  369. return text.replace(regExpGlobal, wrapCharWithSpan);
  370. };
  371. var isWrappedNbsp = function (node) {
  372. return node.nodeName.toLowerCase() === 'span' && node.classList.contains('mce-nbsp-wrap');
  373. };
  374. var show = function (editor, rootElm) {
  375. var nodeList = filterDescendants(SugarElement.fromDom(rootElm), isMatch);
  376. each$1(nodeList, function (n) {
  377. var parent = n.dom.parentNode;
  378. if (isWrappedNbsp(parent)) {
  379. add(SugarElement.fromDom(parent), nbspClass);
  380. } else {
  381. var withSpans = replaceWithSpans(editor.dom.encode(value(n)));
  382. var div = editor.dom.create('div', null, withSpans);
  383. var node = void 0;
  384. while (node = div.lastChild) {
  385. editor.dom.insertAfter(node, n.dom);
  386. }
  387. editor.dom.remove(n.dom);
  388. }
  389. });
  390. };
  391. var hide = function (editor, rootElm) {
  392. var nodeList = editor.dom.select(selector, rootElm);
  393. each$1(nodeList, function (node) {
  394. if (isWrappedNbsp(node)) {
  395. remove(SugarElement.fromDom(node), nbspClass);
  396. } else {
  397. editor.dom.remove(node, true);
  398. }
  399. });
  400. };
  401. var toggle = function (editor) {
  402. var body = editor.getBody();
  403. var bookmark = editor.selection.getBookmark();
  404. var parentNode = findParentElm(editor.selection.getNode(), body);
  405. parentNode = parentNode !== undefined ? parentNode : body;
  406. hide(editor, parentNode);
  407. show(editor, parentNode);
  408. editor.selection.moveToBookmark(bookmark);
  409. };
  410. var applyVisualChars = function (editor, toggleState) {
  411. fireVisualChars(editor, toggleState.get());
  412. var body = editor.getBody();
  413. if (toggleState.get() === true) {
  414. show(editor, body);
  415. } else {
  416. hide(editor, body);
  417. }
  418. };
  419. var toggleVisualChars = function (editor, toggleState) {
  420. toggleState.set(!toggleState.get());
  421. var bookmark = editor.selection.getBookmark();
  422. applyVisualChars(editor, toggleState);
  423. editor.selection.moveToBookmark(bookmark);
  424. };
  425. var register$1 = function (editor, toggleState) {
  426. editor.addCommand('mceVisualChars', function () {
  427. toggleVisualChars(editor, toggleState);
  428. });
  429. };
  430. var isEnabledByDefault = function (editor) {
  431. return editor.getParam('visualchars_default_state', false);
  432. };
  433. var hasForcedRootBlock = function (editor) {
  434. return editor.getParam('forced_root_block') !== false;
  435. };
  436. var setup$1 = function (editor, toggleState) {
  437. editor.on('init', function () {
  438. applyVisualChars(editor, toggleState);
  439. });
  440. };
  441. var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
  442. var setup = function (editor, toggleState) {
  443. var debouncedToggle = global.debounce(function () {
  444. toggle(editor);
  445. }, 300);
  446. if (hasForcedRootBlock(editor)) {
  447. editor.on('keydown', function (e) {
  448. if (toggleState.get() === true) {
  449. e.keyCode === 13 ? toggle(editor) : debouncedToggle();
  450. }
  451. });
  452. }
  453. editor.on('remove', debouncedToggle.stop);
  454. };
  455. var toggleActiveState = function (editor, enabledStated) {
  456. return function (api) {
  457. api.setActive(enabledStated.get());
  458. var editorEventCallback = function (e) {
  459. return api.setActive(e.state);
  460. };
  461. editor.on('VisualChars', editorEventCallback);
  462. return function () {
  463. return editor.off('VisualChars', editorEventCallback);
  464. };
  465. };
  466. };
  467. var register = function (editor, toggleState) {
  468. var onAction = function () {
  469. return editor.execCommand('mceVisualChars');
  470. };
  471. editor.ui.registry.addToggleButton('visualchars', {
  472. tooltip: 'Show invisible characters',
  473. icon: 'visualchars',
  474. onAction: onAction,
  475. onSetup: toggleActiveState(editor, toggleState)
  476. });
  477. editor.ui.registry.addToggleMenuItem('visualchars', {
  478. text: 'Show invisible characters',
  479. icon: 'visualchars',
  480. onAction: onAction,
  481. onSetup: toggleActiveState(editor, toggleState)
  482. });
  483. };
  484. function Plugin () {
  485. global$1.add('visualchars', function (editor) {
  486. var toggleState = Cell(isEnabledByDefault(editor));
  487. register$1(editor, toggleState);
  488. register(editor, toggleState);
  489. setup(editor, toggleState);
  490. setup$1(editor, toggleState);
  491. return get$2(toggleState);
  492. });
  493. }
  494. Plugin();
  495. }());