plugin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /**
  2. * TinyMCE version 6.4.2 (2023-04-26)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  7. const hasProto = (v, constructor, predicate) => {
  8. var _a;
  9. if (predicate(v, constructor.prototype)) {
  10. return true;
  11. } else {
  12. return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
  13. }
  14. };
  15. const typeOf = x => {
  16. const t = typeof x;
  17. if (x === null) {
  18. return 'null';
  19. } else if (t === 'object' && Array.isArray(x)) {
  20. return 'array';
  21. } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
  22. return 'string';
  23. } else {
  24. return t;
  25. }
  26. };
  27. const isType = type => value => typeOf(value) === type;
  28. const isSimpleType = type => value => typeof value === type;
  29. const isString = isType('string');
  30. const isBoolean = isSimpleType('boolean');
  31. const isNullable = a => a === null || a === undefined;
  32. const isNonNullable = a => !isNullable(a);
  33. const isFunction = isSimpleType('function');
  34. const option = name => editor => editor.options.get(name);
  35. const register = editor => {
  36. const registerOption = editor.options.register;
  37. const toolbarProcessor = defaultValue => value => {
  38. const valid = isBoolean(value) || isString(value);
  39. if (valid) {
  40. if (isBoolean(value)) {
  41. return {
  42. value: value ? defaultValue : '',
  43. valid
  44. };
  45. } else {
  46. return {
  47. value: value.trim(),
  48. valid
  49. };
  50. }
  51. } else {
  52. return {
  53. valid: false,
  54. message: 'Must be a boolean or string.'
  55. };
  56. }
  57. };
  58. const defaultSelectionToolbar = 'bold italic | quicklink h2 h3 blockquote';
  59. registerOption('quickbars_selection_toolbar', {
  60. processor: toolbarProcessor(defaultSelectionToolbar),
  61. default: defaultSelectionToolbar
  62. });
  63. const defaultInsertToolbar = 'quickimage quicktable';
  64. registerOption('quickbars_insert_toolbar', {
  65. processor: toolbarProcessor(defaultInsertToolbar),
  66. default: defaultInsertToolbar
  67. });
  68. const defaultImageToolbar = 'alignleft aligncenter alignright';
  69. registerOption('quickbars_image_toolbar', {
  70. processor: toolbarProcessor(defaultImageToolbar),
  71. default: defaultImageToolbar
  72. });
  73. };
  74. const getTextSelectionToolbarItems = option('quickbars_selection_toolbar');
  75. const getInsertToolbarItems = option('quickbars_insert_toolbar');
  76. const getImageToolbarItems = option('quickbars_image_toolbar');
  77. let unique = 0;
  78. const generate = prefix => {
  79. const date = new Date();
  80. const time = date.getTime();
  81. const random = Math.floor(Math.random() * 1000000000);
  82. unique++;
  83. return prefix + '_' + random + unique + String(time);
  84. };
  85. const insertTable = (editor, columns, rows) => {
  86. editor.execCommand('mceInsertTable', false, {
  87. rows,
  88. columns
  89. });
  90. };
  91. const insertBlob = (editor, base64, blob) => {
  92. const blobCache = editor.editorUpload.blobCache;
  93. const blobInfo = blobCache.create(generate('mceu'), blob, base64);
  94. blobCache.add(blobInfo);
  95. editor.insertContent(editor.dom.createHTML('img', { src: blobInfo.blobUri() }));
  96. };
  97. const blobToBase64 = blob => {
  98. return new Promise(resolve => {
  99. const reader = new FileReader();
  100. reader.onloadend = () => {
  101. resolve(reader.result.split(',')[1]);
  102. };
  103. reader.readAsDataURL(blob);
  104. });
  105. };
  106. var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
  107. const pickFile = editor => new Promise(resolve => {
  108. let resolved = false;
  109. const fileInput = document.createElement('input');
  110. fileInput.type = 'file';
  111. fileInput.accept = 'image/*';
  112. fileInput.style.position = 'fixed';
  113. fileInput.style.left = '0';
  114. fileInput.style.top = '0';
  115. fileInput.style.opacity = '0.001';
  116. document.body.appendChild(fileInput);
  117. const resolveFileInput = value => {
  118. var _a;
  119. if (!resolved) {
  120. (_a = fileInput.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(fileInput);
  121. resolved = true;
  122. resolve(value);
  123. }
  124. };
  125. const changeHandler = e => {
  126. resolveFileInput(Array.prototype.slice.call(e.target.files));
  127. };
  128. fileInput.addEventListener('input', changeHandler);
  129. fileInput.addEventListener('change', changeHandler);
  130. const cancelHandler = e => {
  131. const cleanup = () => {
  132. resolveFileInput([]);
  133. };
  134. if (!resolved) {
  135. if (e.type === 'focusin') {
  136. global.setEditorTimeout(editor, cleanup, 1000);
  137. } else {
  138. cleanup();
  139. }
  140. }
  141. editor.off('focusin remove', cancelHandler);
  142. };
  143. editor.on('focusin remove', cancelHandler);
  144. fileInput.click();
  145. });
  146. const setupButtons = editor => {
  147. editor.ui.registry.addButton('quickimage', {
  148. icon: 'image',
  149. tooltip: 'Insert image',
  150. onAction: () => {
  151. pickFile(editor).then(files => {
  152. if (files.length > 0) {
  153. const blob = files[0];
  154. blobToBase64(blob).then(base64 => {
  155. insertBlob(editor, base64, blob);
  156. });
  157. }
  158. });
  159. }
  160. });
  161. editor.ui.registry.addButton('quicktable', {
  162. icon: 'table',
  163. tooltip: 'Insert table',
  164. onAction: () => {
  165. insertTable(editor, 2, 2);
  166. }
  167. });
  168. };
  169. const constant = value => {
  170. return () => {
  171. return value;
  172. };
  173. };
  174. const never = constant(false);
  175. class Optional {
  176. constructor(tag, value) {
  177. this.tag = tag;
  178. this.value = value;
  179. }
  180. static some(value) {
  181. return new Optional(true, value);
  182. }
  183. static none() {
  184. return Optional.singletonNone;
  185. }
  186. fold(onNone, onSome) {
  187. if (this.tag) {
  188. return onSome(this.value);
  189. } else {
  190. return onNone();
  191. }
  192. }
  193. isSome() {
  194. return this.tag;
  195. }
  196. isNone() {
  197. return !this.tag;
  198. }
  199. map(mapper) {
  200. if (this.tag) {
  201. return Optional.some(mapper(this.value));
  202. } else {
  203. return Optional.none();
  204. }
  205. }
  206. bind(binder) {
  207. if (this.tag) {
  208. return binder(this.value);
  209. } else {
  210. return Optional.none();
  211. }
  212. }
  213. exists(predicate) {
  214. return this.tag && predicate(this.value);
  215. }
  216. forall(predicate) {
  217. return !this.tag || predicate(this.value);
  218. }
  219. filter(predicate) {
  220. if (!this.tag || predicate(this.value)) {
  221. return this;
  222. } else {
  223. return Optional.none();
  224. }
  225. }
  226. getOr(replacement) {
  227. return this.tag ? this.value : replacement;
  228. }
  229. or(replacement) {
  230. return this.tag ? this : replacement;
  231. }
  232. getOrThunk(thunk) {
  233. return this.tag ? this.value : thunk();
  234. }
  235. orThunk(thunk) {
  236. return this.tag ? this : thunk();
  237. }
  238. getOrDie(message) {
  239. if (!this.tag) {
  240. throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
  241. } else {
  242. return this.value;
  243. }
  244. }
  245. static from(value) {
  246. return isNonNullable(value) ? Optional.some(value) : Optional.none();
  247. }
  248. getOrNull() {
  249. return this.tag ? this.value : null;
  250. }
  251. getOrUndefined() {
  252. return this.value;
  253. }
  254. each(worker) {
  255. if (this.tag) {
  256. worker(this.value);
  257. }
  258. }
  259. toArray() {
  260. return this.tag ? [this.value] : [];
  261. }
  262. toString() {
  263. return this.tag ? `some(${ this.value })` : 'none()';
  264. }
  265. }
  266. Optional.singletonNone = new Optional(false);
  267. typeof window !== 'undefined' ? window : Function('return this;')();
  268. const ELEMENT = 1;
  269. const name = element => {
  270. const r = element.dom.nodeName;
  271. return r.toLowerCase();
  272. };
  273. const has = (element, key) => {
  274. const dom = element.dom;
  275. return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
  276. };
  277. var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
  278. if (is(scope, a)) {
  279. return Optional.some(scope);
  280. } else if (isFunction(isRoot) && isRoot(scope)) {
  281. return Optional.none();
  282. } else {
  283. return ancestor(scope, a, isRoot);
  284. }
  285. };
  286. const fromHtml = (html, scope) => {
  287. const doc = scope || document;
  288. const div = doc.createElement('div');
  289. div.innerHTML = html;
  290. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  291. const message = 'HTML does not have a single root node';
  292. console.error(message, html);
  293. throw new Error(message);
  294. }
  295. return fromDom(div.childNodes[0]);
  296. };
  297. const fromTag = (tag, scope) => {
  298. const doc = scope || document;
  299. const node = doc.createElement(tag);
  300. return fromDom(node);
  301. };
  302. const fromText = (text, scope) => {
  303. const doc = scope || document;
  304. const node = doc.createTextNode(text);
  305. return fromDom(node);
  306. };
  307. const fromDom = node => {
  308. if (node === null || node === undefined) {
  309. throw new Error('Node cannot be null or undefined');
  310. }
  311. return { dom: node };
  312. };
  313. const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
  314. const SugarElement = {
  315. fromHtml,
  316. fromTag,
  317. fromText,
  318. fromDom,
  319. fromPoint
  320. };
  321. const is = (element, selector) => {
  322. const dom = element.dom;
  323. if (dom.nodeType !== ELEMENT) {
  324. return false;
  325. } else {
  326. const elem = dom;
  327. if (elem.matches !== undefined) {
  328. return elem.matches(selector);
  329. } else if (elem.msMatchesSelector !== undefined) {
  330. return elem.msMatchesSelector(selector);
  331. } else if (elem.webkitMatchesSelector !== undefined) {
  332. return elem.webkitMatchesSelector(selector);
  333. } else if (elem.mozMatchesSelector !== undefined) {
  334. return elem.mozMatchesSelector(selector);
  335. } else {
  336. throw new Error('Browser lacks native selectors');
  337. }
  338. }
  339. };
  340. const ancestor$1 = (scope, predicate, isRoot) => {
  341. let element = scope.dom;
  342. const stop = isFunction(isRoot) ? isRoot : never;
  343. while (element.parentNode) {
  344. element = element.parentNode;
  345. const el = SugarElement.fromDom(element);
  346. if (predicate(el)) {
  347. return Optional.some(el);
  348. } else if (stop(el)) {
  349. break;
  350. }
  351. }
  352. return Optional.none();
  353. };
  354. const closest$2 = (scope, predicate, isRoot) => {
  355. const is = (s, test) => test(s);
  356. return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
  357. };
  358. const closest$1 = (scope, predicate, isRoot) => closest$2(scope, predicate, isRoot).isSome();
  359. const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
  360. const closest = (scope, selector, isRoot) => {
  361. const is$1 = (element, selector) => is(element, selector);
  362. return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);
  363. };
  364. const addToEditor$1 = editor => {
  365. const insertToolbarItems = getInsertToolbarItems(editor);
  366. if (insertToolbarItems.length > 0) {
  367. editor.ui.registry.addContextToolbar('quickblock', {
  368. predicate: node => {
  369. const sugarNode = SugarElement.fromDom(node);
  370. const textBlockElementsMap = editor.schema.getTextBlockElements();
  371. const isRoot = elem => elem.dom === editor.getBody();
  372. return !has(sugarNode, 'data-mce-bogus') && closest(sugarNode, 'table,[data-mce-bogus="all"]', isRoot).fold(() => closest$1(sugarNode, elem => name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom), isRoot), never);
  373. },
  374. items: insertToolbarItems,
  375. position: 'line',
  376. scope: 'editor'
  377. });
  378. }
  379. };
  380. const addToEditor = editor => {
  381. const isEditable = node => editor.dom.getContentEditableParent(node) !== 'false';
  382. const isImage = node => node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);
  383. const imageToolbarItems = getImageToolbarItems(editor);
  384. if (imageToolbarItems.length > 0) {
  385. editor.ui.registry.addContextToolbar('imageselection', {
  386. predicate: isImage,
  387. items: imageToolbarItems,
  388. position: 'node'
  389. });
  390. }
  391. const textToolbarItems = getTextSelectionToolbarItems(editor);
  392. if (textToolbarItems.length > 0) {
  393. editor.ui.registry.addContextToolbar('textselection', {
  394. predicate: node => !isImage(node) && !editor.selection.isCollapsed() && isEditable(node),
  395. items: textToolbarItems,
  396. position: 'selection',
  397. scope: 'editor'
  398. });
  399. }
  400. };
  401. var Plugin = () => {
  402. global$1.add('quickbars', editor => {
  403. register(editor);
  404. setupButtons(editor);
  405. addToEditor$1(editor);
  406. addToEditor(editor);
  407. });
  408. };
  409. Plugin();
  410. })();