plugin.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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 (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. var clone = function () {
  20. return Cell(get());
  21. };
  22. return {
  23. get: get,
  24. set: set,
  25. clone: clone
  26. };
  27. };
  28. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  29. var __assign = function () {
  30. __assign = Object.assign || function __assign(t) {
  31. for (var s, i = 1, n = arguments.length; i < n; i++) {
  32. s = arguments[i];
  33. for (var p in s)
  34. if (Object.prototype.hasOwnProperty.call(s, p))
  35. t[p] = s[p];
  36. }
  37. return t;
  38. };
  39. return __assign.apply(this, arguments);
  40. };
  41. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  42. var global$2 = tinymce.util.Tools.resolve('tinymce.html.DomParser');
  43. var global$3 = tinymce.util.Tools.resolve('tinymce.html.Node');
  44. var global$4 = tinymce.util.Tools.resolve('tinymce.html.Serializer');
  45. var shouldHideInSourceView = function (editor) {
  46. return editor.getParam('fullpage_hide_in_source_view');
  47. };
  48. var getDefaultXmlPi = function (editor) {
  49. return editor.getParam('fullpage_default_xml_pi');
  50. };
  51. var getDefaultEncoding = function (editor) {
  52. return editor.getParam('fullpage_default_encoding');
  53. };
  54. var getDefaultFontFamily = function (editor) {
  55. return editor.getParam('fullpage_default_font_family');
  56. };
  57. var getDefaultFontSize = function (editor) {
  58. return editor.getParam('fullpage_default_font_size');
  59. };
  60. var getDefaultTextColor = function (editor) {
  61. return editor.getParam('fullpage_default_text_color');
  62. };
  63. var getDefaultTitle = function (editor) {
  64. return editor.getParam('fullpage_default_title');
  65. };
  66. var getDefaultDocType = function (editor) {
  67. return editor.getParam('fullpage_default_doctype', '<!DOCTYPE html>');
  68. };
  69. var Settings = {
  70. shouldHideInSourceView: shouldHideInSourceView,
  71. getDefaultXmlPi: getDefaultXmlPi,
  72. getDefaultEncoding: getDefaultEncoding,
  73. getDefaultFontFamily: getDefaultFontFamily,
  74. getDefaultFontSize: getDefaultFontSize,
  75. getDefaultTextColor: getDefaultTextColor,
  76. getDefaultTitle: getDefaultTitle,
  77. getDefaultDocType: getDefaultDocType
  78. };
  79. var parseHeader = function (head) {
  80. return global$2({
  81. validate: false,
  82. root_name: '#document'
  83. }).parse(head, { format: 'xhtml' });
  84. };
  85. var htmlToData = function (editor, head) {
  86. var headerFragment = parseHeader(head);
  87. var data = {};
  88. var elm, matches;
  89. function getAttr(elm, name) {
  90. var value = elm.attr(name);
  91. return value || '';
  92. }
  93. data.fontface = Settings.getDefaultFontFamily(editor);
  94. data.fontsize = Settings.getDefaultFontSize(editor);
  95. elm = headerFragment.firstChild;
  96. if (elm.type === 7) {
  97. data.xml_pi = true;
  98. matches = /encoding="([^"]+)"/.exec(elm.value);
  99. if (matches) {
  100. data.docencoding = matches[1];
  101. }
  102. }
  103. elm = headerFragment.getAll('#doctype')[0];
  104. if (elm) {
  105. data.doctype = '<!DOCTYPE' + elm.value + '>';
  106. }
  107. elm = headerFragment.getAll('title')[0];
  108. if (elm && elm.firstChild) {
  109. data.title = elm.firstChild.value;
  110. }
  111. global$1.each(headerFragment.getAll('meta'), function (meta) {
  112. var name = meta.attr('name');
  113. var httpEquiv = meta.attr('http-equiv');
  114. var matches;
  115. if (name) {
  116. data[name.toLowerCase()] = meta.attr('content');
  117. } else if (httpEquiv === 'Content-Type') {
  118. matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
  119. if (matches) {
  120. data.docencoding = matches[1];
  121. }
  122. }
  123. });
  124. elm = headerFragment.getAll('html')[0];
  125. if (elm) {
  126. data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
  127. }
  128. data.stylesheets = [];
  129. global$1.each(headerFragment.getAll('link'), function (link) {
  130. if (link.attr('rel') === 'stylesheet') {
  131. data.stylesheets.push(link.attr('href'));
  132. }
  133. });
  134. elm = headerFragment.getAll('body')[0];
  135. if (elm) {
  136. data.langdir = getAttr(elm, 'dir');
  137. data.style = getAttr(elm, 'style');
  138. data.visited_color = getAttr(elm, 'vlink');
  139. data.link_color = getAttr(elm, 'link');
  140. data.active_color = getAttr(elm, 'alink');
  141. }
  142. return data;
  143. };
  144. var dataToHtml = function (editor, data, head) {
  145. var headerFragment, headElement, html, elm, value;
  146. var dom = editor.dom;
  147. function setAttr(elm, name, value) {
  148. elm.attr(name, value ? value : undefined);
  149. }
  150. function addHeadNode(node) {
  151. if (headElement.firstChild) {
  152. headElement.insert(node, headElement.firstChild);
  153. } else {
  154. headElement.append(node);
  155. }
  156. }
  157. headerFragment = parseHeader(head);
  158. headElement = headerFragment.getAll('head')[0];
  159. if (!headElement) {
  160. elm = headerFragment.getAll('html')[0];
  161. headElement = new global$3('head', 1);
  162. if (elm.firstChild) {
  163. elm.insert(headElement, elm.firstChild, true);
  164. } else {
  165. elm.append(headElement);
  166. }
  167. }
  168. elm = headerFragment.firstChild;
  169. if (data.xml_pi) {
  170. value = 'version="1.0"';
  171. if (data.docencoding) {
  172. value += ' encoding="' + data.docencoding + '"';
  173. }
  174. if (elm.type !== 7) {
  175. elm = new global$3('xml', 7);
  176. headerFragment.insert(elm, headerFragment.firstChild, true);
  177. }
  178. elm.value = value;
  179. } else if (elm && elm.type === 7) {
  180. elm.remove();
  181. }
  182. elm = headerFragment.getAll('#doctype')[0];
  183. if (data.doctype) {
  184. if (!elm) {
  185. elm = new global$3('#doctype', 10);
  186. if (data.xml_pi) {
  187. headerFragment.insert(elm, headerFragment.firstChild);
  188. } else {
  189. addHeadNode(elm);
  190. }
  191. }
  192. elm.value = data.doctype.substring(9, data.doctype.length - 1);
  193. } else if (elm) {
  194. elm.remove();
  195. }
  196. elm = null;
  197. global$1.each(headerFragment.getAll('meta'), function (meta) {
  198. if (meta.attr('http-equiv') === 'Content-Type') {
  199. elm = meta;
  200. }
  201. });
  202. if (data.docencoding) {
  203. if (!elm) {
  204. elm = new global$3('meta', 1);
  205. elm.attr('http-equiv', 'Content-Type');
  206. elm.shortEnded = true;
  207. addHeadNode(elm);
  208. }
  209. elm.attr('content', 'text/html; charset=' + data.docencoding);
  210. } else if (elm) {
  211. elm.remove();
  212. }
  213. elm = headerFragment.getAll('title')[0];
  214. if (data.title) {
  215. if (!elm) {
  216. elm = new global$3('title', 1);
  217. addHeadNode(elm);
  218. } else {
  219. elm.empty();
  220. }
  221. elm.append(new global$3('#text', 3)).value = data.title;
  222. } else if (elm) {
  223. elm.remove();
  224. }
  225. global$1.each('keywords,description,author,copyright,robots'.split(','), function (name) {
  226. var nodes = headerFragment.getAll('meta');
  227. var i, meta;
  228. var value = data[name];
  229. for (i = 0; i < nodes.length; i++) {
  230. meta = nodes[i];
  231. if (meta.attr('name') === name) {
  232. if (value) {
  233. meta.attr('content', value);
  234. } else {
  235. meta.remove();
  236. }
  237. return;
  238. }
  239. }
  240. if (value) {
  241. elm = new global$3('meta', 1);
  242. elm.attr('name', name);
  243. elm.attr('content', value);
  244. elm.shortEnded = true;
  245. addHeadNode(elm);
  246. }
  247. });
  248. var currentStyleSheetsMap = {};
  249. global$1.each(headerFragment.getAll('link'), function (stylesheet) {
  250. if (stylesheet.attr('rel') === 'stylesheet') {
  251. currentStyleSheetsMap[stylesheet.attr('href')] = stylesheet;
  252. }
  253. });
  254. global$1.each(data.stylesheets, function (stylesheet) {
  255. if (!currentStyleSheetsMap[stylesheet]) {
  256. elm = new global$3('link', 1);
  257. elm.attr({
  258. rel: 'stylesheet',
  259. text: 'text/css',
  260. href: stylesheet
  261. });
  262. elm.shortEnded = true;
  263. addHeadNode(elm);
  264. }
  265. delete currentStyleSheetsMap[stylesheet];
  266. });
  267. global$1.each(currentStyleSheetsMap, function (stylesheet) {
  268. stylesheet.remove();
  269. });
  270. elm = headerFragment.getAll('body')[0];
  271. if (elm) {
  272. setAttr(elm, 'dir', data.langdir);
  273. setAttr(elm, 'style', data.style);
  274. setAttr(elm, 'vlink', data.visited_color);
  275. setAttr(elm, 'link', data.link_color);
  276. setAttr(elm, 'alink', data.active_color);
  277. dom.setAttribs(editor.getBody(), {
  278. style: data.style,
  279. dir: data.dir,
  280. vLink: data.visited_color,
  281. link: data.link_color,
  282. aLink: data.active_color
  283. });
  284. }
  285. elm = headerFragment.getAll('html')[0];
  286. if (elm) {
  287. setAttr(elm, 'lang', data.langcode);
  288. setAttr(elm, 'xml:lang', data.langcode);
  289. }
  290. if (!headElement.firstChild) {
  291. headElement.remove();
  292. }
  293. html = global$4({
  294. validate: false,
  295. indent: true,
  296. indent_before: 'head,html,body,meta,title,script,link,style',
  297. indent_after: 'head,html,body,meta,title,script,link,style'
  298. }).serialize(headerFragment);
  299. return html.substring(0, html.indexOf('</body>'));
  300. };
  301. var Parser = {
  302. parseHeader: parseHeader,
  303. htmlToData: htmlToData,
  304. dataToHtml: dataToHtml
  305. };
  306. var open = function (editor, headState) {
  307. var data = Parser.htmlToData(editor, headState.get());
  308. var defaultData = {
  309. title: '',
  310. keywords: '',
  311. description: '',
  312. robots: '',
  313. author: '',
  314. docencoding: ''
  315. };
  316. var initialData = __assign(__assign({}, defaultData), data);
  317. editor.windowManager.open({
  318. title: 'Metadata and Document Properties',
  319. size: 'normal',
  320. body: {
  321. type: 'panel',
  322. items: [
  323. {
  324. name: 'title',
  325. type: 'input',
  326. label: 'Title'
  327. },
  328. {
  329. name: 'keywords',
  330. type: 'input',
  331. label: 'Keywords'
  332. },
  333. {
  334. name: 'description',
  335. type: 'input',
  336. label: 'Description'
  337. },
  338. {
  339. name: 'robots',
  340. type: 'input',
  341. label: 'Robots'
  342. },
  343. {
  344. name: 'author',
  345. type: 'input',
  346. label: 'Author'
  347. },
  348. {
  349. name: 'docencoding',
  350. type: 'input',
  351. label: 'Encoding'
  352. }
  353. ]
  354. },
  355. buttons: [
  356. {
  357. type: 'cancel',
  358. name: 'cancel',
  359. text: 'Cancel'
  360. },
  361. {
  362. type: 'submit',
  363. name: 'save',
  364. text: 'Save',
  365. primary: true
  366. }
  367. ],
  368. initialData: initialData,
  369. onSubmit: function (api) {
  370. var nuData = api.getData();
  371. var headHtml = Parser.dataToHtml(editor, global$1.extend(data, nuData), headState.get());
  372. headState.set(headHtml);
  373. api.close();
  374. }
  375. });
  376. };
  377. var Dialog = { open: open };
  378. var register = function (editor, headState) {
  379. editor.addCommand('mceFullPageProperties', function () {
  380. Dialog.open(editor, headState);
  381. });
  382. };
  383. var Commands = { register: register };
  384. var protectHtml = function (protect, html) {
  385. global$1.each(protect, function (pattern) {
  386. html = html.replace(pattern, function (str) {
  387. return '<!--mce:protected ' + escape(str) + '-->';
  388. });
  389. });
  390. return html;
  391. };
  392. var unprotectHtml = function (html) {
  393. return html.replace(/<!--mce:protected ([\s\S]*?)-->/g, function (a, m) {
  394. return unescape(m);
  395. });
  396. };
  397. var Protect = {
  398. protectHtml: protectHtml,
  399. unprotectHtml: unprotectHtml
  400. };
  401. var each = global$1.each;
  402. var low = function (s) {
  403. return s.replace(/<\/?[A-Z]+/g, function (a) {
  404. return a.toLowerCase();
  405. });
  406. };
  407. var handleSetContent = function (editor, headState, footState, evt) {
  408. var startPos, endPos, content, headerFragment, styles = '';
  409. var dom = editor.dom;
  410. if (evt.selection) {
  411. return;
  412. }
  413. content = Protect.protectHtml(editor.settings.protect, evt.content);
  414. if (evt.format === 'raw' && headState.get()) {
  415. return;
  416. }
  417. if (evt.source_view && Settings.shouldHideInSourceView(editor)) {
  418. return;
  419. }
  420. if (content.length === 0 && !evt.source_view) {
  421. content = global$1.trim(headState.get()) + '\n' + global$1.trim(content) + '\n' + global$1.trim(footState.get());
  422. }
  423. content = content.replace(/<(\/?)BODY/gi, '<$1body');
  424. startPos = content.indexOf('<body');
  425. if (startPos !== -1) {
  426. startPos = content.indexOf('>', startPos);
  427. headState.set(low(content.substring(0, startPos + 1)));
  428. endPos = content.indexOf('</body', startPos);
  429. if (endPos === -1) {
  430. endPos = content.length;
  431. }
  432. evt.content = global$1.trim(content.substring(startPos + 1, endPos));
  433. footState.set(low(content.substring(endPos)));
  434. } else {
  435. headState.set(getDefaultHeader(editor));
  436. footState.set('\n</body>\n</html>');
  437. }
  438. headerFragment = Parser.parseHeader(headState.get());
  439. each(headerFragment.getAll('style'), function (node) {
  440. if (node.firstChild) {
  441. styles += node.firstChild.value;
  442. }
  443. });
  444. var bodyElm = headerFragment.getAll('body')[0];
  445. if (bodyElm) {
  446. dom.setAttribs(editor.getBody(), {
  447. style: bodyElm.attr('style') || '',
  448. dir: bodyElm.attr('dir') || '',
  449. vLink: bodyElm.attr('vlink') || '',
  450. link: bodyElm.attr('link') || '',
  451. aLink: bodyElm.attr('alink') || ''
  452. });
  453. }
  454. dom.remove('fullpage_styles');
  455. var headElm = editor.getDoc().getElementsByTagName('head')[0];
  456. if (styles) {
  457. var styleElm = dom.add(headElm, 'style', { id: 'fullpage_styles' });
  458. styleElm.appendChild(domGlobals.document.createTextNode(styles));
  459. }
  460. var currentStyleSheetsMap = {};
  461. global$1.each(headElm.getElementsByTagName('link'), function (stylesheet) {
  462. if (stylesheet.rel === 'stylesheet' && stylesheet.getAttribute('data-mce-fullpage')) {
  463. currentStyleSheetsMap[stylesheet.href] = stylesheet;
  464. }
  465. });
  466. global$1.each(headerFragment.getAll('link'), function (stylesheet) {
  467. var href = stylesheet.attr('href');
  468. if (!href) {
  469. return true;
  470. }
  471. if (!currentStyleSheetsMap[href] && stylesheet.attr('rel') === 'stylesheet') {
  472. dom.add(headElm, 'link', {
  473. 'rel': 'stylesheet',
  474. 'text': 'text/css',
  475. 'href': href,
  476. 'data-mce-fullpage': '1'
  477. });
  478. }
  479. delete currentStyleSheetsMap[href];
  480. });
  481. global$1.each(currentStyleSheetsMap, function (stylesheet) {
  482. stylesheet.parentNode.removeChild(stylesheet);
  483. });
  484. };
  485. var getDefaultHeader = function (editor) {
  486. var header = '', value, styles = '';
  487. if (Settings.getDefaultXmlPi(editor)) {
  488. var piEncoding = Settings.getDefaultEncoding(editor);
  489. header += '<?xml version="1.0" encoding="' + (piEncoding ? piEncoding : 'ISO-8859-1') + '" ?>\n';
  490. }
  491. header += Settings.getDefaultDocType(editor);
  492. header += '\n<html>\n<head>\n';
  493. if (value = Settings.getDefaultTitle(editor)) {
  494. header += '<title>' + value + '</title>\n';
  495. }
  496. if (value = Settings.getDefaultEncoding(editor)) {
  497. header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
  498. }
  499. if (value = Settings.getDefaultFontFamily(editor)) {
  500. styles += 'font-family: ' + value + ';';
  501. }
  502. if (value = Settings.getDefaultFontSize(editor)) {
  503. styles += 'font-size: ' + value + ';';
  504. }
  505. if (value = Settings.getDefaultTextColor(editor)) {
  506. styles += 'color: ' + value + ';';
  507. }
  508. header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
  509. return header;
  510. };
  511. var handleGetContent = function (editor, head, foot, evt) {
  512. if (!evt.selection && (!evt.source_view || !Settings.shouldHideInSourceView(editor))) {
  513. evt.content = Protect.unprotectHtml(global$1.trim(head) + '\n' + global$1.trim(evt.content) + '\n' + global$1.trim(foot));
  514. }
  515. };
  516. var setup = function (editor, headState, footState) {
  517. editor.on('BeforeSetContent', function (evt) {
  518. handleSetContent(editor, headState, footState, evt);
  519. });
  520. editor.on('GetContent', function (evt) {
  521. handleGetContent(editor, headState.get(), footState.get(), evt);
  522. });
  523. };
  524. var FilterContent = { setup: setup };
  525. var register$1 = function (editor) {
  526. editor.ui.registry.addButton('fullpage', {
  527. tooltip: 'Metadata and document properties',
  528. icon: 'document-properties',
  529. onAction: function () {
  530. editor.execCommand('mceFullPageProperties');
  531. }
  532. });
  533. editor.ui.registry.addMenuItem('fullpage', {
  534. text: 'Metadata and document properties',
  535. icon: 'document-properties',
  536. onAction: function () {
  537. editor.execCommand('mceFullPageProperties');
  538. }
  539. });
  540. };
  541. var Buttons = { register: register$1 };
  542. function Plugin () {
  543. global.add('fullpage', function (editor) {
  544. var headState = Cell(''), footState = Cell('');
  545. Commands.register(editor, headState);
  546. Buttons.register(editor);
  547. FilterContent.setup(editor, headState, footState);
  548. });
  549. }
  550. Plugin();
  551. }(window));