plugin.js 13 KB

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