plugin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /**
  2. * TinyMCE version 6.0.3 (2022-05-25)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global$2 = 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$1 = tinymce.util.Tools.resolve('tinymce.Env');
  107. var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
  108. const pickFile = editor => new Promise(resolve => {
  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 changeHandler = e => {
  118. resolve(Array.prototype.slice.call(e.target.files));
  119. };
  120. fileInput.addEventListener('change', changeHandler);
  121. const cancelHandler = e => {
  122. const cleanup = () => {
  123. resolve([]);
  124. fileInput.parentNode.removeChild(fileInput);
  125. };
  126. if (global$1.os.isAndroid() && e.type !== 'remove') {
  127. global.setEditorTimeout(editor, cleanup, 0);
  128. } else {
  129. cleanup();
  130. }
  131. editor.off('focusin remove', cancelHandler);
  132. };
  133. editor.on('focusin remove', cancelHandler);
  134. fileInput.click();
  135. });
  136. const setupButtons = editor => {
  137. editor.ui.registry.addButton('quickimage', {
  138. icon: 'image',
  139. tooltip: 'Insert image',
  140. onAction: () => {
  141. pickFile(editor).then(files => {
  142. if (files.length > 0) {
  143. const blob = files[0];
  144. blobToBase64(blob).then(base64 => {
  145. insertBlob(editor, base64, blob);
  146. });
  147. }
  148. });
  149. }
  150. });
  151. editor.ui.registry.addButton('quicktable', {
  152. icon: 'table',
  153. tooltip: 'Insert table',
  154. onAction: () => {
  155. insertTable(editor, 2, 2);
  156. }
  157. });
  158. };
  159. const constant = value => {
  160. return () => {
  161. return value;
  162. };
  163. };
  164. const never = constant(false);
  165. class Optional {
  166. constructor(tag, value) {
  167. this.tag = tag;
  168. this.value = value;
  169. }
  170. static some(value) {
  171. return new Optional(true, value);
  172. }
  173. static none() {
  174. return Optional.singletonNone;
  175. }
  176. fold(onNone, onSome) {
  177. if (this.tag) {
  178. return onSome(this.value);
  179. } else {
  180. return onNone();
  181. }
  182. }
  183. isSome() {
  184. return this.tag;
  185. }
  186. isNone() {
  187. return !this.tag;
  188. }
  189. map(mapper) {
  190. if (this.tag) {
  191. return Optional.some(mapper(this.value));
  192. } else {
  193. return Optional.none();
  194. }
  195. }
  196. bind(binder) {
  197. if (this.tag) {
  198. return binder(this.value);
  199. } else {
  200. return Optional.none();
  201. }
  202. }
  203. exists(predicate) {
  204. return this.tag && predicate(this.value);
  205. }
  206. forall(predicate) {
  207. return !this.tag || predicate(this.value);
  208. }
  209. filter(predicate) {
  210. if (!this.tag || predicate(this.value)) {
  211. return this;
  212. } else {
  213. return Optional.none();
  214. }
  215. }
  216. getOr(replacement) {
  217. return this.tag ? this.value : replacement;
  218. }
  219. or(replacement) {
  220. return this.tag ? this : replacement;
  221. }
  222. getOrThunk(thunk) {
  223. return this.tag ? this.value : thunk();
  224. }
  225. orThunk(thunk) {
  226. return this.tag ? this : thunk();
  227. }
  228. getOrDie(message) {
  229. if (!this.tag) {
  230. throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
  231. } else {
  232. return this.value;
  233. }
  234. }
  235. static from(value) {
  236. return isNonNullable(value) ? Optional.some(value) : Optional.none();
  237. }
  238. getOrNull() {
  239. return this.tag ? this.value : null;
  240. }
  241. getOrUndefined() {
  242. return this.value;
  243. }
  244. each(worker) {
  245. if (this.tag) {
  246. worker(this.value);
  247. }
  248. }
  249. toArray() {
  250. return this.tag ? [this.value] : [];
  251. }
  252. toString() {
  253. return this.tag ? `some(${ this.value })` : 'none()';
  254. }
  255. }
  256. Optional.singletonNone = new Optional(false);
  257. var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
  258. if (is(scope, a)) {
  259. return Optional.some(scope);
  260. } else if (isFunction(isRoot) && isRoot(scope)) {
  261. return Optional.none();
  262. } else {
  263. return ancestor(scope, a, isRoot);
  264. }
  265. };
  266. const ELEMENT = 1;
  267. const fromHtml = (html, scope) => {
  268. const doc = scope || document;
  269. const div = doc.createElement('div');
  270. div.innerHTML = html;
  271. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  272. const message = 'HTML does not have a single root node';
  273. console.error(message, html);
  274. throw new Error(message);
  275. }
  276. return fromDom(div.childNodes[0]);
  277. };
  278. const fromTag = (tag, scope) => {
  279. const doc = scope || document;
  280. const node = doc.createElement(tag);
  281. return fromDom(node);
  282. };
  283. const fromText = (text, scope) => {
  284. const doc = scope || document;
  285. const node = doc.createTextNode(text);
  286. return fromDom(node);
  287. };
  288. const fromDom = node => {
  289. if (node === null || node === undefined) {
  290. throw new Error('Node cannot be null or undefined');
  291. }
  292. return { dom: node };
  293. };
  294. const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
  295. const SugarElement = {
  296. fromHtml,
  297. fromTag,
  298. fromText,
  299. fromDom,
  300. fromPoint
  301. };
  302. const is = (element, selector) => {
  303. const dom = element.dom;
  304. if (dom.nodeType !== ELEMENT) {
  305. return false;
  306. } else {
  307. const elem = dom;
  308. if (elem.matches !== undefined) {
  309. return elem.matches(selector);
  310. } else if (elem.msMatchesSelector !== undefined) {
  311. return elem.msMatchesSelector(selector);
  312. } else if (elem.webkitMatchesSelector !== undefined) {
  313. return elem.webkitMatchesSelector(selector);
  314. } else if (elem.mozMatchesSelector !== undefined) {
  315. return elem.mozMatchesSelector(selector);
  316. } else {
  317. throw new Error('Browser lacks native selectors');
  318. }
  319. }
  320. };
  321. typeof window !== 'undefined' ? window : Function('return this;')();
  322. const name = element => {
  323. const r = element.dom.nodeName;
  324. return r.toLowerCase();
  325. };
  326. const ancestor$1 = (scope, predicate, isRoot) => {
  327. let element = scope.dom;
  328. const stop = isFunction(isRoot) ? isRoot : never;
  329. while (element.parentNode) {
  330. element = element.parentNode;
  331. const el = SugarElement.fromDom(element);
  332. if (predicate(el)) {
  333. return Optional.some(el);
  334. } else if (stop(el)) {
  335. break;
  336. }
  337. }
  338. return Optional.none();
  339. };
  340. const closest$1 = (scope, predicate, isRoot) => {
  341. const is = (s, test) => test(s);
  342. return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
  343. };
  344. const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
  345. const closest = (scope, selector, isRoot) => {
  346. const is$1 = (element, selector) => is(element, selector);
  347. return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);
  348. };
  349. const addToEditor$1 = editor => {
  350. const insertToolbarItems = getInsertToolbarItems(editor);
  351. if (insertToolbarItems.length > 0) {
  352. editor.ui.registry.addContextToolbar('quickblock', {
  353. predicate: node => {
  354. const sugarNode = SugarElement.fromDom(node);
  355. const textBlockElementsMap = editor.schema.getTextBlockElements();
  356. const isRoot = elem => elem.dom === editor.getBody();
  357. return closest(sugarNode, 'table', isRoot).fold(() => closest$1(sugarNode, elem => name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom), isRoot).isSome(), never);
  358. },
  359. items: insertToolbarItems,
  360. position: 'line',
  361. scope: 'editor'
  362. });
  363. }
  364. };
  365. const addToEditor = editor => {
  366. const isEditable = node => editor.dom.getContentEditableParent(node) !== 'false';
  367. const isImage = node => node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);
  368. const imageToolbarItems = getImageToolbarItems(editor);
  369. if (imageToolbarItems.length > 0) {
  370. editor.ui.registry.addContextToolbar('imageselection', {
  371. predicate: isImage,
  372. items: imageToolbarItems,
  373. position: 'node'
  374. });
  375. }
  376. const textToolbarItems = getTextSelectionToolbarItems(editor);
  377. if (textToolbarItems.length > 0) {
  378. editor.ui.registry.addContextToolbar('textselection', {
  379. predicate: node => !isImage(node) && !editor.selection.isCollapsed() && isEditable(node),
  380. items: textToolbarItems,
  381. position: 'selection',
  382. scope: 'editor'
  383. });
  384. }
  385. };
  386. var Plugin = () => {
  387. global$2.add('quickbars', editor => {
  388. register(editor);
  389. setupButtons(editor);
  390. addToEditor$1(editor);
  391. addToEditor(editor);
  392. });
  393. };
  394. Plugin();
  395. })();