plugin.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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.2.0 (2020-02-13)
  8. */
  9. (function () {
  10. 'use strict';
  11. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  12. var noop = function () {
  13. };
  14. var constant = function (value) {
  15. return function () {
  16. return value;
  17. };
  18. };
  19. function curry(fn) {
  20. var initialArgs = [];
  21. for (var _i = 1; _i < arguments.length; _i++) {
  22. initialArgs[_i - 1] = arguments[_i];
  23. }
  24. return function () {
  25. var restArgs = [];
  26. for (var _i = 0; _i < arguments.length; _i++) {
  27. restArgs[_i] = arguments[_i];
  28. }
  29. var all = initialArgs.concat(restArgs);
  30. return fn.apply(null, all);
  31. };
  32. }
  33. var never = constant(false);
  34. var always = constant(true);
  35. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  36. var global$2 = tinymce.util.Tools.resolve('tinymce.util.XHR');
  37. var getCreationDateClasses = function (editor) {
  38. return editor.getParam('template_cdate_classes', 'cdate');
  39. };
  40. var getModificationDateClasses = function (editor) {
  41. return editor.getParam('template_mdate_classes', 'mdate');
  42. };
  43. var getSelectedContentClasses = function (editor) {
  44. return editor.getParam('template_selected_content_classes', 'selcontent');
  45. };
  46. var getPreviewReplaceValues = function (editor) {
  47. return editor.getParam('template_preview_replace_values');
  48. };
  49. var getTemplateReplaceValues = function (editor) {
  50. return editor.getParam('template_replace_values');
  51. };
  52. var getTemplates = function (editorSettings) {
  53. return editorSettings.templates;
  54. };
  55. var getCdateFormat = function (editor) {
  56. return editor.getParam('template_cdate_format', editor.translate('%Y-%m-%d'));
  57. };
  58. var getMdateFormat = function (editor) {
  59. return editor.getParam('template_mdate_format', editor.translate('%Y-%m-%d'));
  60. };
  61. var Settings = {
  62. getCreationDateClasses: getCreationDateClasses,
  63. getModificationDateClasses: getModificationDateClasses,
  64. getSelectedContentClasses: getSelectedContentClasses,
  65. getPreviewReplaceValues: getPreviewReplaceValues,
  66. getTemplateReplaceValues: getTemplateReplaceValues,
  67. getTemplates: getTemplates,
  68. getCdateFormat: getCdateFormat,
  69. getMdateFormat: getMdateFormat
  70. };
  71. var addZeros = function (value, len) {
  72. value = '' + value;
  73. if (value.length < len) {
  74. for (var i = 0; i < len - value.length; i++) {
  75. value = '0' + value;
  76. }
  77. }
  78. return value;
  79. };
  80. var getDateTime = function (editor, fmt, date) {
  81. var daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' ');
  82. var daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' ');
  83. var monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
  84. var monthsLong = 'January February March April May June July August September October November December'.split(' ');
  85. date = date || new Date();
  86. fmt = fmt.replace('%D', '%m/%d/%Y');
  87. fmt = fmt.replace('%r', '%I:%M:%S %p');
  88. fmt = fmt.replace('%Y', '' + date.getFullYear());
  89. fmt = fmt.replace('%y', '' + date.getYear());
  90. fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2));
  91. fmt = fmt.replace('%d', addZeros(date.getDate(), 2));
  92. fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2));
  93. fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2));
  94. fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2));
  95. fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1));
  96. fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM'));
  97. fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()]));
  98. fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()]));
  99. fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()]));
  100. fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()]));
  101. fmt = fmt.replace('%%', '%');
  102. return fmt;
  103. };
  104. var DateTimeHelper = { getDateTime: getDateTime };
  105. var createTemplateList = function (editorSettings, callback) {
  106. return function () {
  107. var templateList = Settings.getTemplates(editorSettings);
  108. if (typeof templateList === 'function') {
  109. templateList(callback);
  110. return;
  111. }
  112. if (typeof templateList === 'string') {
  113. global$2.send({
  114. url: templateList,
  115. success: function (text) {
  116. callback(JSON.parse(text));
  117. }
  118. });
  119. } else {
  120. callback(templateList);
  121. }
  122. };
  123. };
  124. var replaceTemplateValues = function (html, templateValues) {
  125. global$1.each(templateValues, function (v, k) {
  126. if (typeof v === 'function') {
  127. v = v(k);
  128. }
  129. html = html.replace(new RegExp('\\{\\$' + k + '\\}', 'g'), v);
  130. });
  131. return html;
  132. };
  133. var replaceVals = function (editor, e) {
  134. var dom = editor.dom, vl = Settings.getTemplateReplaceValues(editor);
  135. global$1.each(dom.select('*', e), function (e) {
  136. global$1.each(vl, function (v, k) {
  137. if (dom.hasClass(e, k)) {
  138. if (typeof vl[k] === 'function') {
  139. vl[k](e);
  140. }
  141. }
  142. });
  143. });
  144. };
  145. var hasClass = function (n, c) {
  146. return new RegExp('\\b' + c + '\\b', 'g').test(n.className);
  147. };
  148. var insertTemplate = function (editor, ui, html) {
  149. var el;
  150. var n;
  151. var dom = editor.dom;
  152. var sel = editor.selection.getContent();
  153. html = replaceTemplateValues(html, Settings.getTemplateReplaceValues(editor));
  154. el = dom.create('div', null, html);
  155. n = dom.select('.mceTmpl', el);
  156. if (n && n.length > 0) {
  157. el = dom.create('div', null);
  158. el.appendChild(n[0].cloneNode(true));
  159. }
  160. global$1.each(dom.select('*', el), function (n) {
  161. if (hasClass(n, Settings.getCreationDateClasses(editor).replace(/\s+/g, '|'))) {
  162. n.innerHTML = DateTimeHelper.getDateTime(editor, Settings.getCdateFormat(editor));
  163. }
  164. if (hasClass(n, Settings.getModificationDateClasses(editor).replace(/\s+/g, '|'))) {
  165. n.innerHTML = DateTimeHelper.getDateTime(editor, Settings.getMdateFormat(editor));
  166. }
  167. if (hasClass(n, Settings.getSelectedContentClasses(editor).replace(/\s+/g, '|'))) {
  168. n.innerHTML = sel;
  169. }
  170. });
  171. replaceVals(editor, el);
  172. editor.execCommand('mceInsertContent', false, el.innerHTML);
  173. editor.addVisual();
  174. };
  175. var Templates = {
  176. createTemplateList: createTemplateList,
  177. replaceTemplateValues: replaceTemplateValues,
  178. replaceVals: replaceVals,
  179. insertTemplate: insertTemplate
  180. };
  181. var register = function (editor) {
  182. editor.addCommand('mceInsertTemplate', curry(Templates.insertTemplate, editor));
  183. };
  184. var Commands = { register: register };
  185. var setup = function (editor) {
  186. editor.on('PreProcess', function (o) {
  187. var dom = editor.dom, dateFormat = Settings.getMdateFormat(editor);
  188. global$1.each(dom.select('div', o.node), function (e) {
  189. if (dom.hasClass(e, 'mceTmpl')) {
  190. global$1.each(dom.select('*', e), function (e) {
  191. if (dom.hasClass(e, editor.getParam('template_mdate_classes', 'mdate').replace(/\s+/g, '|'))) {
  192. e.innerHTML = DateTimeHelper.getDateTime(editor, dateFormat);
  193. }
  194. });
  195. Templates.replaceVals(editor, e);
  196. }
  197. });
  198. });
  199. };
  200. var FilterContent = { setup: setup };
  201. var none = function () {
  202. return NONE;
  203. };
  204. var NONE = function () {
  205. var eq = function (o) {
  206. return o.isNone();
  207. };
  208. var call = function (thunk) {
  209. return thunk();
  210. };
  211. var id = function (n) {
  212. return n;
  213. };
  214. var me = {
  215. fold: function (n, s) {
  216. return n();
  217. },
  218. is: never,
  219. isSome: never,
  220. isNone: always,
  221. getOr: id,
  222. getOrThunk: call,
  223. getOrDie: function (msg) {
  224. throw new Error(msg || 'error: getOrDie called on none.');
  225. },
  226. getOrNull: constant(null),
  227. getOrUndefined: constant(undefined),
  228. or: id,
  229. orThunk: call,
  230. map: none,
  231. each: noop,
  232. bind: none,
  233. exists: never,
  234. forall: always,
  235. filter: none,
  236. equals: eq,
  237. equals_: eq,
  238. toArray: function () {
  239. return [];
  240. },
  241. toString: constant('none()')
  242. };
  243. if (Object.freeze) {
  244. Object.freeze(me);
  245. }
  246. return me;
  247. }();
  248. var some = function (a) {
  249. var constant_a = constant(a);
  250. var self = function () {
  251. return me;
  252. };
  253. var bind = function (f) {
  254. return f(a);
  255. };
  256. var me = {
  257. fold: function (n, s) {
  258. return s(a);
  259. },
  260. is: function (v) {
  261. return a === v;
  262. },
  263. isSome: always,
  264. isNone: never,
  265. getOr: constant_a,
  266. getOrThunk: constant_a,
  267. getOrDie: constant_a,
  268. getOrNull: constant_a,
  269. getOrUndefined: constant_a,
  270. or: self,
  271. orThunk: self,
  272. map: function (f) {
  273. return some(f(a));
  274. },
  275. each: function (f) {
  276. f(a);
  277. },
  278. bind: bind,
  279. exists: bind,
  280. forall: bind,
  281. filter: function (f) {
  282. return f(a) ? me : NONE;
  283. },
  284. toArray: function () {
  285. return [a];
  286. },
  287. toString: function () {
  288. return 'some(' + a + ')';
  289. },
  290. equals: function (o) {
  291. return o.is(a);
  292. },
  293. equals_: function (o, elementEq) {
  294. return o.fold(never, function (b) {
  295. return elementEq(a, b);
  296. });
  297. }
  298. };
  299. return me;
  300. };
  301. var from = function (value) {
  302. return value === null || value === undefined ? NONE : some(value);
  303. };
  304. var Option = {
  305. some: some,
  306. none: none,
  307. from: from
  308. };
  309. var typeOf = function (x) {
  310. if (x === null) {
  311. return 'null';
  312. }
  313. var t = typeof x;
  314. if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  315. return 'array';
  316. }
  317. if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  318. return 'string';
  319. }
  320. return t;
  321. };
  322. var isType = function (type) {
  323. return function (value) {
  324. return typeOf(value) === type;
  325. };
  326. };
  327. var isFunction = isType('function');
  328. var nativeSlice = Array.prototype.slice;
  329. var map = function (xs, f) {
  330. var len = xs.length;
  331. var r = new Array(len);
  332. for (var i = 0; i < len; i++) {
  333. var x = xs[i];
  334. r[i] = f(x, i);
  335. }
  336. return r;
  337. };
  338. var find = function (xs, pred) {
  339. for (var i = 0, len = xs.length; i < len; i++) {
  340. var x = xs[i];
  341. if (pred(x, i)) {
  342. return Option.some(x);
  343. }
  344. }
  345. return Option.none();
  346. };
  347. var from$1 = isFunction(Array.from) ? Array.from : function (x) {
  348. return nativeSlice.call(x);
  349. };
  350. var global$3 = tinymce.util.Tools.resolve('tinymce.util.Promise');
  351. var hasOwnProperty = Object.hasOwnProperty;
  352. var get = function (obj, key) {
  353. return has(obj, key) ? Option.from(obj[key]) : Option.none();
  354. };
  355. var has = function (obj, key) {
  356. return hasOwnProperty.call(obj, key);
  357. };
  358. var entitiesAttr = {
  359. '"': '&quot;',
  360. '<': '&lt;',
  361. '>': '&gt;',
  362. '&': '&amp;',
  363. '\'': '&#039;'
  364. };
  365. var htmlEscape = function (html) {
  366. return html.replace(/["'<>&]/g, function (match) {
  367. return get(entitiesAttr, match).getOr(match);
  368. });
  369. };
  370. var getPreviewContent = function (editor, html) {
  371. if (html.indexOf('<html>') === -1) {
  372. var contentCssLinks_1 = '';
  373. global$1.each(editor.contentCSS, function (url) {
  374. contentCssLinks_1 += '<link type="text/css" rel="stylesheet" href="' + editor.documentBaseURI.toAbsolute(url) + '">';
  375. });
  376. var bodyClass = editor.settings.body_class || '';
  377. if (bodyClass.indexOf('=') !== -1) {
  378. bodyClass = editor.getParam('body_class', '', 'hash');
  379. bodyClass = bodyClass[editor.id] || '';
  380. }
  381. var encode = editor.dom.encode;
  382. var directionality = editor.getBody().dir;
  383. var dirAttr = directionality ? ' dir="' + encode(directionality) + '"' : '';
  384. html = '<!DOCTYPE html>' + '<html>' + '<head>' + contentCssLinks_1 + '</head>' + '<body class="' + encode(bodyClass) + '"' + dirAttr + '>' + html + '</body>' + '</html>';
  385. }
  386. return Templates.replaceTemplateValues(html, Settings.getPreviewReplaceValues(editor));
  387. };
  388. var open = function (editor, templateList) {
  389. var createTemplates = function () {
  390. if (!templateList || templateList.length === 0) {
  391. var message = editor.translate('No templates defined.');
  392. editor.notificationManager.open({
  393. text: message,
  394. type: 'info'
  395. });
  396. return Option.none();
  397. }
  398. return Option.from(global$1.map(templateList, function (template, index) {
  399. var isUrlTemplate = function (t) {
  400. return t.url !== undefined;
  401. };
  402. return {
  403. selected: index === 0,
  404. text: template.title,
  405. value: {
  406. url: isUrlTemplate(template) ? Option.from(template.url) : Option.none(),
  407. content: !isUrlTemplate(template) ? Option.from(template.content) : Option.none(),
  408. description: template.description
  409. }
  410. };
  411. }));
  412. };
  413. var createSelectBoxItems = function (templates) {
  414. return map(templates, function (t) {
  415. return {
  416. text: t.text,
  417. value: t.text
  418. };
  419. });
  420. };
  421. var findTemplate = function (templates, templateTitle) {
  422. return find(templates, function (t) {
  423. return t.text === templateTitle;
  424. });
  425. };
  426. var loadFailedAlert = function (api) {
  427. editor.windowManager.alert('Could not load the specified template.', function () {
  428. return api.focus('template');
  429. });
  430. };
  431. var getTemplateContent = function (t) {
  432. return new global$3(function (resolve, reject) {
  433. t.value.url.fold(function () {
  434. return resolve(t.value.content.getOr(''));
  435. }, function (url) {
  436. return global$2.send({
  437. url: url,
  438. success: function (html) {
  439. resolve(html);
  440. },
  441. error: function (e) {
  442. reject(e);
  443. }
  444. });
  445. });
  446. });
  447. };
  448. var onChange = function (templates, updateDialog) {
  449. return function (api, change) {
  450. if (change.name === 'template') {
  451. var newTemplateTitle = api.getData().template;
  452. findTemplate(templates, newTemplateTitle).each(function (t) {
  453. api.block('Loading...');
  454. getTemplateContent(t).then(function (previewHtml) {
  455. updateDialog(api, t, previewHtml);
  456. }).catch(function () {
  457. updateDialog(api, t, '');
  458. api.disable('save');
  459. loadFailedAlert(api);
  460. });
  461. });
  462. }
  463. };
  464. };
  465. var onSubmit = function (templates) {
  466. return function (api) {
  467. var data = api.getData();
  468. findTemplate(templates, data.template).each(function (t) {
  469. getTemplateContent(t).then(function (previewHtml) {
  470. Templates.insertTemplate(editor, false, previewHtml);
  471. api.close();
  472. }).catch(function () {
  473. api.disable('save');
  474. loadFailedAlert(api);
  475. });
  476. });
  477. };
  478. };
  479. var openDialog = function (templates) {
  480. var selectBoxItems = createSelectBoxItems(templates);
  481. var buildDialogSpec = function (bodyItems, initialData) {
  482. return {
  483. title: 'Insert Template',
  484. size: 'large',
  485. body: {
  486. type: 'panel',
  487. items: bodyItems
  488. },
  489. initialData: initialData,
  490. buttons: [
  491. {
  492. type: 'cancel',
  493. name: 'cancel',
  494. text: 'Cancel'
  495. },
  496. {
  497. type: 'submit',
  498. name: 'save',
  499. text: 'Save',
  500. primary: true
  501. }
  502. ],
  503. onSubmit: onSubmit(templates),
  504. onChange: onChange(templates, updateDialog)
  505. };
  506. };
  507. var updateDialog = function (dialogApi, template, previewHtml) {
  508. var content = getPreviewContent(editor, previewHtml);
  509. var bodyItems = [
  510. {
  511. type: 'selectbox',
  512. name: 'template',
  513. label: 'Templates',
  514. items: selectBoxItems
  515. },
  516. {
  517. type: 'htmlpanel',
  518. html: '<p aria-live="polite">' + htmlEscape(template.value.description) + '</p>'
  519. },
  520. {
  521. label: 'Preview',
  522. type: 'iframe',
  523. name: 'preview',
  524. sandboxed: false
  525. }
  526. ];
  527. var initialData = {
  528. template: template.text,
  529. preview: content
  530. };
  531. dialogApi.unblock();
  532. dialogApi.redial(buildDialogSpec(bodyItems, initialData));
  533. dialogApi.focus('template');
  534. };
  535. var dialogApi = editor.windowManager.open(buildDialogSpec([], {
  536. template: '',
  537. preview: ''
  538. }));
  539. dialogApi.block('Loading...');
  540. getTemplateContent(templates[0]).then(function (previewHtml) {
  541. updateDialog(dialogApi, templates[0], previewHtml);
  542. }).catch(function () {
  543. updateDialog(dialogApi, templates[0], '');
  544. dialogApi.disable('save');
  545. loadFailedAlert(dialogApi);
  546. });
  547. };
  548. var optTemplates = createTemplates();
  549. optTemplates.each(openDialog);
  550. };
  551. var Dialog = { open: open };
  552. var showDialog = function (editor) {
  553. return function (templates) {
  554. Dialog.open(editor, templates);
  555. };
  556. };
  557. var register$1 = function (editor) {
  558. editor.ui.registry.addButton('template', {
  559. icon: 'template',
  560. tooltip: 'Insert template',
  561. onAction: Templates.createTemplateList(editor.settings, showDialog(editor))
  562. });
  563. editor.ui.registry.addMenuItem('template', {
  564. icon: 'template',
  565. text: 'Insert template...',
  566. onAction: Templates.createTemplateList(editor.settings, showDialog(editor))
  567. });
  568. };
  569. var Buttons = { register: register$1 };
  570. function Plugin () {
  571. global.add('template', function (editor) {
  572. Buttons.register(editor);
  573. Commands.register(editor);
  574. FilterContent.setup(editor);
  575. });
  576. }
  577. Plugin();
  578. }());