plugin.js 15 KB

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