plugin.js 17 KB

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