plugin.js 17 KB

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