plugin.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  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 global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  12. var unique = 0;
  13. var generate = function (prefix) {
  14. var date = new Date();
  15. var time = date.getTime();
  16. var random = Math.floor(Math.random() * 1000000000);
  17. unique++;
  18. return prefix + '_' + random + unique + String(time);
  19. };
  20. var createTableHtml = function (cols, rows) {
  21. var x, y, html;
  22. html = '<table data-mce-id="mce" style="width: 100%">';
  23. html += '<tbody>';
  24. for (y = 0; y < rows; y++) {
  25. html += '<tr>';
  26. for (x = 0; x < cols; x++) {
  27. html += '<td><br></td>';
  28. }
  29. html += '</tr>';
  30. }
  31. html += '</tbody>';
  32. html += '</table>';
  33. return html;
  34. };
  35. var getInsertedElement = function (editor) {
  36. var elms = editor.dom.select('*[data-mce-id]');
  37. return elms[0];
  38. };
  39. var insertTableHtml = function (editor, cols, rows) {
  40. editor.undoManager.transact(function () {
  41. var tableElm, cellElm;
  42. editor.insertContent(createTableHtml(cols, rows));
  43. tableElm = getInsertedElement(editor);
  44. tableElm.removeAttribute('data-mce-id');
  45. cellElm = editor.dom.select('td,th', tableElm);
  46. editor.selection.setCursorLocation(cellElm[0], 0);
  47. });
  48. };
  49. var insertTable = function (editor, cols, rows) {
  50. editor.plugins.table ? editor.plugins.table.insertTable(cols, rows) : insertTableHtml(editor, cols, rows);
  51. };
  52. var insertBlob = function (editor, base64, blob) {
  53. var blobCache, blobInfo;
  54. blobCache = editor.editorUpload.blobCache;
  55. blobInfo = blobCache.create(generate('mceu'), blob, base64);
  56. blobCache.add(blobInfo);
  57. editor.insertContent(editor.dom.createHTML('img', { src: blobInfo.blobUri() }));
  58. };
  59. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Promise');
  60. var blobToBase64 = function (blob) {
  61. return new global$1(function (resolve) {
  62. var reader = new domGlobals.FileReader();
  63. reader.onloadend = function () {
  64. resolve(reader.result.split(',')[1]);
  65. };
  66. reader.readAsDataURL(blob);
  67. });
  68. };
  69. var global$2 = tinymce.util.Tools.resolve('tinymce.Env');
  70. var global$3 = tinymce.util.Tools.resolve('tinymce.util.Delay');
  71. var pickFile = function (editor) {
  72. return new global$1(function (resolve) {
  73. var fileInput = domGlobals.document.createElement('input');
  74. fileInput.type = 'file';
  75. fileInput.accept = 'image/*';
  76. fileInput.style.position = 'fixed';
  77. fileInput.style.left = '0';
  78. fileInput.style.top = '0';
  79. fileInput.style.opacity = '0.001';
  80. domGlobals.document.body.appendChild(fileInput);
  81. var changeHandler = function (e) {
  82. resolve(Array.prototype.slice.call(e.target.files));
  83. };
  84. fileInput.addEventListener('change', changeHandler);
  85. var cancelHandler = function (e) {
  86. var cleanup = function () {
  87. resolve([]);
  88. fileInput.parentNode.removeChild(fileInput);
  89. };
  90. if (global$2.os.isAndroid() && e.type !== 'remove') {
  91. global$3.setEditorTimeout(editor, cleanup, 0);
  92. } else {
  93. cleanup();
  94. }
  95. editor.off('focusin remove', cancelHandler);
  96. };
  97. editor.on('focusin remove', cancelHandler);
  98. fileInput.click();
  99. });
  100. };
  101. var setupButtons = function (editor) {
  102. editor.ui.registry.addButton('quickimage', {
  103. icon: 'image',
  104. tooltip: 'Insert image',
  105. onAction: function () {
  106. pickFile(editor).then(function (files) {
  107. if (files.length > 0) {
  108. var blob_1 = files[0];
  109. blobToBase64(blob_1).then(function (base64) {
  110. insertBlob(editor, base64, blob_1);
  111. });
  112. }
  113. });
  114. }
  115. });
  116. editor.ui.registry.addButton('quicktable', {
  117. icon: 'table',
  118. tooltip: 'Insert table',
  119. onAction: function () {
  120. insertTable(editor, 2, 2);
  121. }
  122. });
  123. };
  124. var noop = function () {
  125. };
  126. var constant = function (value) {
  127. return function () {
  128. return value;
  129. };
  130. };
  131. var never = constant(false);
  132. var always = constant(true);
  133. var none = function () {
  134. return NONE;
  135. };
  136. var NONE = function () {
  137. var eq = function (o) {
  138. return o.isNone();
  139. };
  140. var call = function (thunk) {
  141. return thunk();
  142. };
  143. var id = function (n) {
  144. return n;
  145. };
  146. var me = {
  147. fold: function (n, _s) {
  148. return n();
  149. },
  150. is: never,
  151. isSome: never,
  152. isNone: always,
  153. getOr: id,
  154. getOrThunk: call,
  155. getOrDie: function (msg) {
  156. throw new Error(msg || 'error: getOrDie called on none.');
  157. },
  158. getOrNull: constant(null),
  159. getOrUndefined: constant(undefined),
  160. or: id,
  161. orThunk: call,
  162. map: none,
  163. each: noop,
  164. bind: none,
  165. exists: never,
  166. forall: always,
  167. filter: none,
  168. equals: eq,
  169. equals_: eq,
  170. toArray: function () {
  171. return [];
  172. },
  173. toString: constant('none()')
  174. };
  175. return me;
  176. }();
  177. var some = function (a) {
  178. var constant_a = constant(a);
  179. var self = function () {
  180. return me;
  181. };
  182. var bind = function (f) {
  183. return f(a);
  184. };
  185. var me = {
  186. fold: function (n, s) {
  187. return s(a);
  188. },
  189. is: function (v) {
  190. return a === v;
  191. },
  192. isSome: always,
  193. isNone: never,
  194. getOr: constant_a,
  195. getOrThunk: constant_a,
  196. getOrDie: constant_a,
  197. getOrNull: constant_a,
  198. getOrUndefined: constant_a,
  199. or: self,
  200. orThunk: self,
  201. map: function (f) {
  202. return some(f(a));
  203. },
  204. each: function (f) {
  205. f(a);
  206. },
  207. bind: bind,
  208. exists: bind,
  209. forall: bind,
  210. filter: function (f) {
  211. return f(a) ? me : NONE;
  212. },
  213. toArray: function () {
  214. return [a];
  215. },
  216. toString: function () {
  217. return 'some(' + a + ')';
  218. },
  219. equals: function (o) {
  220. return o.is(a);
  221. },
  222. equals_: function (o, elementEq) {
  223. return o.fold(never, function (b) {
  224. return elementEq(a, b);
  225. });
  226. }
  227. };
  228. return me;
  229. };
  230. var from = function (value) {
  231. return value === null || value === undefined ? NONE : some(value);
  232. };
  233. var Option = {
  234. some: some,
  235. none: none,
  236. from: from
  237. };
  238. var fromHtml = function (html, scope) {
  239. var doc = scope || domGlobals.document;
  240. var div = doc.createElement('div');
  241. div.innerHTML = html;
  242. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  243. domGlobals.console.error('HTML does not have a single root node', html);
  244. throw new Error('HTML must have a single root node');
  245. }
  246. return fromDom(div.childNodes[0]);
  247. };
  248. var fromTag = function (tag, scope) {
  249. var doc = scope || domGlobals.document;
  250. var node = doc.createElement(tag);
  251. return fromDom(node);
  252. };
  253. var fromText = function (text, scope) {
  254. var doc = scope || domGlobals.document;
  255. var node = doc.createTextNode(text);
  256. return fromDom(node);
  257. };
  258. var fromDom = function (node) {
  259. if (node === null || node === undefined) {
  260. throw new Error('Node cannot be null or undefined');
  261. }
  262. return { dom: constant(node) };
  263. };
  264. var fromPoint = function (docElm, x, y) {
  265. var doc = docElm.dom();
  266. return Option.from(doc.elementFromPoint(x, y)).map(fromDom);
  267. };
  268. var Element = {
  269. fromHtml: fromHtml,
  270. fromTag: fromTag,
  271. fromText: fromText,
  272. fromDom: fromDom,
  273. fromPoint: fromPoint
  274. };
  275. var Global = typeof domGlobals.window !== 'undefined' ? domGlobals.window : Function('return this;')();
  276. var ELEMENT = 1;
  277. var name = function (element) {
  278. var r = element.dom().nodeName;
  279. return r.toLowerCase();
  280. };
  281. var typeOf = function (x) {
  282. var t = typeof x;
  283. if (x === null) {
  284. return 'null';
  285. } else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  286. return 'array';
  287. } else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  288. return 'string';
  289. } else {
  290. return t;
  291. }
  292. };
  293. var isType = function (type) {
  294. return function (value) {
  295. return typeOf(value) === type;
  296. };
  297. };
  298. var isSimpleType = function (type) {
  299. return function (value) {
  300. return typeof value === type;
  301. };
  302. };
  303. var eq = function (t) {
  304. return function (a) {
  305. return t === a;
  306. };
  307. };
  308. var isString = isType('string');
  309. var isObject = isType('object');
  310. var isArray = isType('array');
  311. var isBoolean = isSimpleType('boolean');
  312. var isUndefined = eq(undefined);
  313. var isFunction = isSimpleType('function');
  314. function ClosestOrAncestor (is, ancestor, scope, a, isRoot) {
  315. return is(scope, a) ? Option.some(scope) : isFunction(isRoot) && isRoot(scope) ? Option.none() : ancestor(scope, a, isRoot);
  316. }
  317. var ELEMENT$1 = ELEMENT;
  318. var is = function (element, selector) {
  319. var dom = element.dom();
  320. if (dom.nodeType !== ELEMENT$1) {
  321. return false;
  322. } else {
  323. var elem = dom;
  324. if (elem.matches !== undefined) {
  325. return elem.matches(selector);
  326. } else if (elem.msMatchesSelector !== undefined) {
  327. return elem.msMatchesSelector(selector);
  328. } else if (elem.webkitMatchesSelector !== undefined) {
  329. return elem.webkitMatchesSelector(selector);
  330. } else if (elem.mozMatchesSelector !== undefined) {
  331. return elem.mozMatchesSelector(selector);
  332. } else {
  333. throw new Error('Browser lacks native selectors');
  334. }
  335. }
  336. };
  337. var ancestor = function (scope, predicate, isRoot) {
  338. var element = scope.dom();
  339. var stop = isFunction(isRoot) ? isRoot : constant(false);
  340. while (element.parentNode) {
  341. element = element.parentNode;
  342. var el = Element.fromDom(element);
  343. if (predicate(el)) {
  344. return Option.some(el);
  345. } else if (stop(el)) {
  346. break;
  347. }
  348. }
  349. return Option.none();
  350. };
  351. var closest = function (scope, predicate, isRoot) {
  352. var is = function (s, test) {
  353. return test(s);
  354. };
  355. return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot);
  356. };
  357. var ancestor$1 = function (scope, selector, isRoot) {
  358. return ancestor(scope, function (e) {
  359. return is(e, selector);
  360. }, isRoot);
  361. };
  362. var closest$1 = function (scope, selector, isRoot) {
  363. var is$1 = function (element, selector) {
  364. return is(element, selector);
  365. };
  366. return ClosestOrAncestor(is$1, ancestor$1, scope, selector, isRoot);
  367. };
  368. var validDefaultOrDie = function (value, predicate) {
  369. if (predicate(value)) {
  370. return true;
  371. }
  372. throw new Error('Default value doesn\'t match requested type.');
  373. };
  374. var items = function (value, defaultValue) {
  375. if (isArray(value) || isObject(value)) {
  376. throw new Error('expected a string but found: ' + value);
  377. }
  378. if (isUndefined(value)) {
  379. return defaultValue;
  380. }
  381. if (isBoolean(value)) {
  382. return value === false ? '' : defaultValue;
  383. }
  384. return value;
  385. };
  386. var getToolbarItemsOr_ = function (predicate) {
  387. return function (editor, name, defaultValue) {
  388. validDefaultOrDie(defaultValue, predicate);
  389. var value = editor.getParam(name, defaultValue);
  390. return items(value, defaultValue);
  391. };
  392. };
  393. var getToolbarItemsOr = getToolbarItemsOr_(isString);
  394. var getTextSelectionToolbarItems = function (editor) {
  395. return getToolbarItemsOr(editor, 'quickbars_selection_toolbar', 'bold italic | quicklink h2 h3 blockquote');
  396. };
  397. var getInsertToolbarItems = function (editor) {
  398. return getToolbarItemsOr(editor, 'quickbars_insert_toolbar', 'quickimage quicktable');
  399. };
  400. var getImageToolbarItems = function (editor) {
  401. return getToolbarItemsOr(editor, 'quickbars_image_toolbar', 'alignleft aligncenter alignright');
  402. };
  403. var addToEditor = function (editor) {
  404. var insertToolbarItems = getInsertToolbarItems(editor);
  405. if (insertToolbarItems.trim().length > 0) {
  406. editor.ui.registry.addContextToolbar('quickblock', {
  407. predicate: function (node) {
  408. var sugarNode = Element.fromDom(node);
  409. var textBlockElementsMap = editor.schema.getTextBlockElements();
  410. var isRoot = function (elem) {
  411. return elem.dom() === editor.getBody();
  412. };
  413. return closest$1(sugarNode, 'table', isRoot).fold(function () {
  414. return closest(sugarNode, function (elem) {
  415. return name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom());
  416. }, isRoot).isSome();
  417. }, function () {
  418. return false;
  419. });
  420. },
  421. items: insertToolbarItems,
  422. position: 'line',
  423. scope: 'editor'
  424. });
  425. }
  426. };
  427. var addToEditor$1 = function (editor) {
  428. var isEditable = function (node) {
  429. return editor.dom.getContentEditableParent(node) !== 'false';
  430. };
  431. var isImage = function (node) {
  432. return node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);
  433. };
  434. var imageToolbarItems = getImageToolbarItems(editor);
  435. if (imageToolbarItems.trim().length > 0) {
  436. editor.ui.registry.addContextToolbar('imageselection', {
  437. predicate: isImage,
  438. items: imageToolbarItems,
  439. position: 'node'
  440. });
  441. }
  442. var textToolbarItems = getTextSelectionToolbarItems(editor);
  443. if (textToolbarItems.trim().length > 0) {
  444. editor.ui.registry.addContextToolbar('textselection', {
  445. predicate: function (node) {
  446. return !isImage(node) && !editor.selection.isCollapsed() && isEditable(node);
  447. },
  448. items: textToolbarItems,
  449. position: 'selection',
  450. scope: 'editor'
  451. });
  452. }
  453. };
  454. function Plugin () {
  455. global.add('quickbars', function (editor) {
  456. setupButtons(editor);
  457. addToEditor(editor);
  458. addToEditor$1(editor);
  459. });
  460. }
  461. Plugin();
  462. }(window));