plugin.js 19 KB

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