plugin.js 14 KB

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