run:R W Run
23.45 KB
2026-03-11 16:18:51
R W Run
8.73 KB
2026-03-11 16:18:51
R W Run
error_log
📄plugin.js
1(function () {
2var link = (function (domGlobals) {
3 'use strict';
4
5 var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
6
7 var global$1 = tinymce.util.Tools.resolve('tinymce.util.VK');
8
9 var assumeExternalTargets = function (editorSettings) {
10 return typeof editorSettings.link_assume_external_targets === 'boolean' ? editorSettings.link_assume_external_targets : false;
11 };
12 var hasContextToolbar = function (editorSettings) {
13 return typeof editorSettings.link_context_toolbar === 'boolean' ? editorSettings.link_context_toolbar : false;
14 };
15 var getLinkList = function (editorSettings) {
16 return editorSettings.link_list;
17 };
18 var hasDefaultLinkTarget = function (editorSettings) {
19 return typeof editorSettings.default_link_target === 'string';
20 };
21 var getDefaultLinkTarget = function (editorSettings) {
22 return editorSettings.default_link_target;
23 };
24 var getTargetList = function (editorSettings) {
25 return editorSettings.target_list;
26 };
27 var setTargetList = function (editor, list) {
28 editor.settings.target_list = list;
29 };
30 var shouldShowTargetList = function (editorSettings) {
31 return getTargetList(editorSettings) !== false;
32 };
33 var getRelList = function (editorSettings) {
34 return editorSettings.rel_list;
35 };
36 var hasRelList = function (editorSettings) {
37 return getRelList(editorSettings) !== undefined;
38 };
39 var getLinkClassList = function (editorSettings) {
40 return editorSettings.link_class_list;
41 };
42 var hasLinkClassList = function (editorSettings) {
43 return getLinkClassList(editorSettings) !== undefined;
44 };
45 var shouldShowLinkTitle = function (editorSettings) {
46 return editorSettings.link_title !== false;
47 };
48 var allowUnsafeLinkTarget = function (editorSettings) {
49 return typeof editorSettings.allow_unsafe_link_target === 'boolean' ? editorSettings.allow_unsafe_link_target : false;
50 };
51 var Settings = {
52 assumeExternalTargets: assumeExternalTargets,
53 hasContextToolbar: hasContextToolbar,
54 getLinkList: getLinkList,
55 hasDefaultLinkTarget: hasDefaultLinkTarget,
56 getDefaultLinkTarget: getDefaultLinkTarget,
57 getTargetList: getTargetList,
58 setTargetList: setTargetList,
59 shouldShowTargetList: shouldShowTargetList,
60 getRelList: getRelList,
61 hasRelList: hasRelList,
62 getLinkClassList: getLinkClassList,
63 hasLinkClassList: hasLinkClassList,
64 shouldShowLinkTitle: shouldShowLinkTitle,
65 allowUnsafeLinkTarget: allowUnsafeLinkTarget
66 };
67
68 var global$2 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
69
70 var global$3 = tinymce.util.Tools.resolve('tinymce.Env');
71
72 var appendClickRemove = function (link, evt) {
73 domGlobals.document.body.appendChild(link);
74 link.dispatchEvent(evt);
75 domGlobals.document.body.removeChild(link);
76 };
77 var open = function (url) {
78 if (!global$3.ie || global$3.ie > 10) {
79 var link = domGlobals.document.createElement('a');
80 link.target = '_blank';
81 link.href = url;
82 link.rel = 'noreferrer noopener';
83 var evt = domGlobals.document.createEvent('MouseEvents');
84 evt.initMouseEvent('click', true, true, domGlobals.window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
85 appendClickRemove(link, evt);
86 } else {
87 var win = domGlobals.window.open('', '_blank');
88 if (win) {
89 win.opener = null;
90 var doc = win.document;
91 doc.open();
92 doc.write('<meta http-equiv="refresh" content="0; url=' + global$2.DOM.encode(url) + '">');
93 doc.close();
94 }
95 }
96 };
97 var OpenUrl = { open: open };
98
99 var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
100
101 var toggleTargetRules = function (rel, isUnsafe) {
102 var rules = ['noopener'];
103 var newRel = rel ? rel.split(/\s+/) : [];
104 var toString = function (rel) {
105 return global$4.trim(rel.sort().join(' '));
106 };
107 var addTargetRules = function (rel) {
108 rel = removeTargetRules(rel);
109 return rel.length ? rel.concat(rules) : rules;
110 };
111 var removeTargetRules = function (rel) {
112 return rel.filter(function (val) {
113 return global$4.inArray(rules, val) === -1;
114 });
115 };
116 newRel = isUnsafe ? addTargetRules(newRel) : removeTargetRules(newRel);
117 return newRel.length ? toString(newRel) : null;
118 };
119 var trimCaretContainers = function (text) {
120 return text.replace(/\uFEFF/g, '');
121 };
122 var getAnchorElement = function (editor, selectedElm) {
123 selectedElm = selectedElm || editor.selection.getNode();
124 if (isImageFigure(selectedElm)) {
125 return editor.dom.select('a[href]', selectedElm)[0];
126 } else {
127 return editor.dom.getParent(selectedElm, 'a[href]');
128 }
129 };
130 var getAnchorText = function (selection, anchorElm) {
131 var text = anchorElm ? anchorElm.innerText || anchorElm.textContent : selection.getContent({ format: 'text' });
132 return trimCaretContainers(text);
133 };
134 var isLink = function (elm) {
135 return elm && elm.nodeName === 'A' && elm.href;
136 };
137 var hasLinks = function (elements) {
138 return global$4.grep(elements, isLink).length > 0;
139 };
140 var isOnlyTextSelected = function (html) {
141 if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') === -1)) {
142 return false;
143 }
144 return true;
145 };
146 var isImageFigure = function (node) {
147 return node && node.nodeName === 'FIGURE' && /\bimage\b/i.test(node.className);
148 };
149 var link = function (editor, attachState) {
150 return function (data) {
151 editor.undoManager.transact(function () {
152 var selectedElm = editor.selection.getNode();
153 var anchorElm = getAnchorElement(editor, selectedElm);
154 var linkAttrs = {
155 href: data.href,
156 target: data.target ? data.target : null,
157 rel: data.rel ? data.rel : null,
158 class: data.class ? data.class : null,
159 title: data.title ? data.title : null
160 };
161 if (!Settings.hasRelList(editor.settings) && Settings.allowUnsafeLinkTarget(editor.settings) === false) {
162 linkAttrs.rel = toggleTargetRules(linkAttrs.rel, linkAttrs.target === '_blank');
163 }
164 if (data.href === attachState.href) {
165 attachState.attach();
166 attachState = {};
167 }
168 if (anchorElm) {
169 editor.focus();
170 if (data.hasOwnProperty('text')) {
171 if ('innerText' in anchorElm) {
172 anchorElm.innerText = data.text;
173 } else {
174 anchorElm.textContent = data.text;
175 }
176 }
177 editor.dom.setAttribs(anchorElm, linkAttrs);
178 editor.selection.select(anchorElm);
179 editor.undoManager.add();
180 } else {
181 if (isImageFigure(selectedElm)) {
182 linkImageFigure(editor, selectedElm, linkAttrs);
183 } else if (data.hasOwnProperty('text')) {
184 editor.insertContent(editor.dom.createHTML('a', linkAttrs, editor.dom.encode(data.text)));
185 } else {
186 editor.execCommand('mceInsertLink', false, linkAttrs);
187 }
188 }
189 });
190 };
191 };
192 var unlink = function (editor) {
193 return function () {
194 editor.undoManager.transact(function () {
195 var node = editor.selection.getNode();
196 if (isImageFigure(node)) {
197 unlinkImageFigure(editor, node);
198 } else {
199 editor.execCommand('unlink');
200 }
201 });
202 };
203 };
204 var unlinkImageFigure = function (editor, fig) {
205 var a, img;
206 img = editor.dom.select('img', fig)[0];
207 if (img) {
208 a = editor.dom.getParents(img, 'a[href]', fig)[0];
209 if (a) {
210 a.parentNode.insertBefore(img, a);
211 editor.dom.remove(a);
212 }
213 }
214 };
215 var linkImageFigure = function (editor, fig, attrs) {
216 var a, img;
217 img = editor.dom.select('img', fig)[0];
218 if (img) {
219 a = editor.dom.create('a', attrs);
220 img.parentNode.insertBefore(a, img);
221 a.appendChild(img);
222 }
223 };
224 var Utils = {
225 link: link,
226 unlink: unlink,
227 isLink: isLink,
228 hasLinks: hasLinks,
229 isOnlyTextSelected: isOnlyTextSelected,
230 getAnchorElement: getAnchorElement,
231 getAnchorText: getAnchorText,
232 toggleTargetRules: toggleTargetRules
233 };
234
235 var global$5 = tinymce.util.Tools.resolve('tinymce.util.Delay');
236
237 var global$6 = tinymce.util.Tools.resolve('tinymce.util.XHR');
238
239 var attachState = {};
240 var createLinkList = function (editor, callback) {
241 var linkList = Settings.getLinkList(editor.settings);
242 if (typeof linkList === 'string') {
243 global$6.send({
244 url: linkList,
245 success: function (text) {
246 callback(editor, JSON.parse(text));
247 }
248 });
249 } else if (typeof linkList === 'function') {
250 linkList(function (list) {
251 callback(editor, list);
252 });
253 } else {
254 callback(editor, linkList);
255 }
256 };
257 var buildListItems = function (inputList, itemCallback, startItems) {
258 var appendItems = function (values, output) {
259 output = output || [];
260 global$4.each(values, function (item) {
261 var menuItem = { text: item.text || item.title };
262 if (item.menu) {
263 menuItem.menu = appendItems(item.menu);
264 } else {
265 menuItem.value = item.value;
266 if (itemCallback) {
267 itemCallback(menuItem);
268 }
269 }
270 output.push(menuItem);
271 });
272 return output;
273 };
274 return appendItems(inputList, startItems || []);
275 };
276 var delayedConfirm = function (editor, message, callback) {
277 var rng = editor.selection.getRng();
278 global$5.setEditorTimeout(editor, function () {
279 editor.windowManager.confirm(message, function (state) {
280 editor.selection.setRng(rng);
281 callback(state);
282 });
283 });
284 };
285 var showDialog = function (editor, linkList) {
286 var data = {};
287 var selection = editor.selection;
288 var dom = editor.dom;
289 var anchorElm, initialText;
290 var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value;
291 var linkListChangeHandler = function (e) {
292 var textCtrl = win.find('#text');
293 if (!textCtrl.value() || e.lastControl && textCtrl.value() === e.lastControl.text()) {
294 textCtrl.value(e.control.text());
295 }
296 win.find('#href').value(e.control.value());
297 };
298 var buildAnchorListControl = function (url) {
299 var anchorList = [];
300 global$4.each(editor.dom.select('a:not([href])'), function (anchor) {
301 var id = anchor.name || anchor.id;
302 if (id) {
303 anchorList.push({
304 text: id,
305 value: '#' + id,
306 selected: url.indexOf('#' + id) !== -1
307 });
308 }
309 });
310 if (anchorList.length) {
311 anchorList.unshift({
312 text: 'None',
313 value: ''
314 });
315 return {
316 name: 'anchor',
317 type: 'listbox',
318 label: 'Anchors',
319 values: anchorList,
320 onselect: linkListChangeHandler
321 };
322 }
323 };
324 var updateText = function () {
325 if (!initialText && onlyText && !data.text) {
326 this.parent().parent().find('#text')[0].value(this.value());
327 }
328 };
329 var urlChange = function (e) {
330 var meta = e.meta || {};
331 if (linkListCtrl) {
332 linkListCtrl.value(editor.convertURL(this.value(), 'href'));
333 }
334 global$4.each(e.meta, function (value, key) {
335 var inp = win.find('#' + key);
336 if (key === 'text') {
337 if (initialText.length === 0) {
338 inp.value(value);
339 data.text = value;
340 }
341 } else {
342 inp.value(value);
343 }
344 });
345 if (meta.attach) {
346 attachState = {
347 href: this.value(),
348 attach: meta.attach
349 };
350 }
351 if (!meta.text) {
352 updateText.call(this);
353 }
354 };
355 var onBeforeCall = function (e) {
356 e.meta = win.toJSON();
357 };
358 onlyText = Utils.isOnlyTextSelected(selection.getContent());
359 anchorElm = Utils.getAnchorElement(editor);
360 data.text = initialText = Utils.getAnchorText(editor.selection, anchorElm);
361 data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';
362 if (anchorElm) {
363 data.target = dom.getAttrib(anchorElm, 'target');
364 } else if (Settings.hasDefaultLinkTarget(editor.settings)) {
365 data.target = Settings.getDefaultLinkTarget(editor.settings);
366 }
367 if (value = dom.getAttrib(anchorElm, 'rel')) {
368 data.rel = value;
369 }
370 if (value = dom.getAttrib(anchorElm, 'class')) {
371 data.class = value;
372 }
373 if (value = dom.getAttrib(anchorElm, 'title')) {
374 data.title = value;
375 }
376 if (onlyText) {
377 textListCtrl = {
378 name: 'text',
379 type: 'textbox',
380 size: 40,
381 label: 'Text to display',
382 onchange: function () {
383 data.text = this.value();
384 }
385 };
386 }
387 if (linkList) {
388 linkListCtrl = {
389 type: 'listbox',
390 label: 'Link list',
391 values: buildListItems(linkList, function (item) {
392 item.value = editor.convertURL(item.value || item.url, 'href');
393 }, [{
394 text: 'None',
395 value: ''
396 }]),
397 onselect: linkListChangeHandler,
398 value: editor.convertURL(data.href, 'href'),
399 onPostRender: function () {
400 linkListCtrl = this;
401 }
402 };
403 }
404 if (Settings.shouldShowTargetList(editor.settings)) {
405 if (Settings.getTargetList(editor.settings) === undefined) {
406 Settings.setTargetList(editor, [
407 {
408 text: 'None',
409 value: ''
410 },
411 {
412 text: 'New window',
413 value: '_blank'
414 }
415 ]);
416 }
417 targetListCtrl = {
418 name: 'target',
419 type: 'listbox',
420 label: 'Target',
421 values: buildListItems(Settings.getTargetList(editor.settings))
422 };
423 }
424 if (Settings.hasRelList(editor.settings)) {
425 relListCtrl = {
426 name: 'rel',
427 type: 'listbox',
428 label: 'Rel',
429 values: buildListItems(Settings.getRelList(editor.settings), function (item) {
430 if (Settings.allowUnsafeLinkTarget(editor.settings) === false) {
431 item.value = Utils.toggleTargetRules(item.value, data.target === '_blank');
432 }
433 })
434 };
435 }
436 if (Settings.hasLinkClassList(editor.settings)) {
437 classListCtrl = {
438 name: 'class',
439 type: 'listbox',
440 label: 'Class',
441 values: buildListItems(Settings.getLinkClassList(editor.settings), function (item) {
442 if (item.value) {
443 item.textStyle = function () {
444 return editor.formatter.getCssText({
445 inline: 'a',
446 classes: [item.value]
447 });
448 };
449 }
450 })
451 };
452 }
453 if (Settings.shouldShowLinkTitle(editor.settings)) {
454 linkTitleCtrl = {
455 name: 'title',
456 type: 'textbox',
457 label: 'Title',
458 value: data.title
459 };
460 }
461 win = editor.windowManager.open({
462 title: 'Insert link',
463 data: data,
464 body: [
465 {
466 name: 'href',
467 type: 'filepicker',
468 filetype: 'file',
469 size: 40,
470 autofocus: true,
471 label: 'Url',
472 onchange: urlChange,
473 onkeyup: updateText,
474 onpaste: updateText,
475 onbeforecall: onBeforeCall
476 },
477 textListCtrl,
478 linkTitleCtrl,
479 buildAnchorListControl(data.href),
480 linkListCtrl,
481 relListCtrl,
482 targetListCtrl,
483 classListCtrl
484 ],
485 onSubmit: function (e) {
486 var assumeExternalTargets = Settings.assumeExternalTargets(editor.settings);
487 var insertLink = Utils.link(editor, attachState);
488 var removeLink = Utils.unlink(editor);
489 var resultData = global$4.extend({}, data, e.data);
490 var href = resultData.href;
491 if (!href) {
492 removeLink();
493 return;
494 }
495 if (!onlyText || resultData.text === initialText) {
496 delete resultData.text;
497 }
498 if (href.indexOf('@') > 0 && href.indexOf('//') === -1 && href.indexOf('mailto:') === -1) {
499 delayedConfirm(editor, 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?', function (state) {
500 if (state) {
501 resultData.href = 'mailto:' + href;
502 }
503 insertLink(resultData);
504 });
505 return;
506 }
507 if (assumeExternalTargets === true && !/^\w+:/i.test(href) || assumeExternalTargets === false && /^\s*www[\.|\d\.]/i.test(href)) {
508 delayedConfirm(editor, 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?', function (state) {
509 if (state) {
510 resultData.href = 'http://' + href;
511 }
512 insertLink(resultData);
513 });
514 return;
515 }
516 insertLink(resultData);
517 }
518 });
519 };
520 var open$1 = function (editor) {
521 createLinkList(editor, showDialog);
522 };
523 var Dialog = { open: open$1 };
524
525 var getLink = function (editor, elm) {
526 return editor.dom.getParent(elm, 'a[href]');
527 };
528 var getSelectedLink = function (editor) {
529 return getLink(editor, editor.selection.getStart());
530 };
531 var getHref = function (elm) {
532 var href = elm.getAttribute('data-mce-href');
533 return href ? href : elm.getAttribute('href');
534 };
535 var isContextMenuVisible = function (editor) {
536 var contextmenu = editor.plugins.contextmenu;
537 return contextmenu ? contextmenu.isContextMenuVisible() : false;
538 };
539 var hasOnlyAltModifier = function (e) {
540 return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
541 };
542 var gotoLink = function (editor, a) {
543 if (a) {
544 var href = getHref(a);
545 if (/^#/.test(href)) {
546 var targetEl = editor.$(href);
547 if (targetEl.length) {
548 editor.selection.scrollIntoView(targetEl[0], true);
549 }
550 } else {
551 OpenUrl.open(a.href);
552 }
553 }
554 };
555 var openDialog = function (editor) {
556 return function () {
557 Dialog.open(editor);
558 };
559 };
560 var gotoSelectedLink = function (editor) {
561 return function () {
562 gotoLink(editor, getSelectedLink(editor));
563 };
564 };
565 var leftClickedOnAHref = function (editor) {
566 return function (elm) {
567 var sel, rng, node;
568 if (Settings.hasContextToolbar(editor.settings) && !isContextMenuVisible(editor) && Utils.isLink(elm)) {
569 sel = editor.selection;
570 rng = sel.getRng();
571 node = rng.startContainer;
572 if (node.nodeType === 3 && sel.isCollapsed() && rng.startOffset > 0 && rng.startOffset < node.data.length) {
573 return true;
574 }
575 }
576 return false;
577 };
578 };
579 var setupGotoLinks = function (editor) {
580 editor.on('click', function (e) {
581 var link = getLink(editor, e.target);
582 if (link && global$1.metaKeyPressed(e)) {
583 e.preventDefault();
584 gotoLink(editor, link);
585 }
586 });
587 editor.on('keydown', function (e) {
588 var link = getSelectedLink(editor);
589 if (link && e.keyCode === 13 && hasOnlyAltModifier(e)) {
590 e.preventDefault();
591 gotoLink(editor, link);
592 }
593 });
594 };
595 var toggleActiveState = function (editor) {
596 return function () {
597 var self = this;
598 editor.on('nodechange', function (e) {
599 self.active(!editor.readonly && !!Utils.getAnchorElement(editor, e.element));
600 });
601 };
602 };
603 var toggleViewLinkState = function (editor) {
604 return function () {
605 var self = this;
606 var toggleVisibility = function (e) {
607 if (Utils.hasLinks(e.parents)) {
608 self.show();
609 } else {
610 self.hide();
611 }
612 };
613 if (!Utils.hasLinks(editor.dom.getParents(editor.selection.getStart()))) {
614 self.hide();
615 }
616 editor.on('nodechange', toggleVisibility);
617 self.on('remove', function () {
618 editor.off('nodechange', toggleVisibility);
619 });
620 };
621 };
622 var Actions = {
623 openDialog: openDialog,
624 gotoSelectedLink: gotoSelectedLink,
625 leftClickedOnAHref: leftClickedOnAHref,
626 setupGotoLinks: setupGotoLinks,
627 toggleActiveState: toggleActiveState,
628 toggleViewLinkState: toggleViewLinkState
629 };
630
631 var register = function (editor) {
632 editor.addCommand('mceLink', Actions.openDialog(editor));
633 };
634 var Commands = { register: register };
635
636 var setup = function (editor) {
637 editor.addShortcut('Meta+K', '', Actions.openDialog(editor));
638 };
639 var Keyboard = { setup: setup };
640
641 var setupButtons = function (editor) {
642 editor.addButton('link', {
643 active: false,
644 icon: 'link',
645 tooltip: 'Insert/edit link',
646 onclick: Actions.openDialog(editor),
647 onpostrender: Actions.toggleActiveState(editor)
648 });
649 editor.addButton('unlink', {
650 active: false,
651 icon: 'unlink',
652 tooltip: 'Remove link',
653 onclick: Utils.unlink(editor),
654 onpostrender: Actions.toggleActiveState(editor)
655 });
656 if (editor.addContextToolbar) {
657 editor.addButton('openlink', {
658 icon: 'newtab',
659 tooltip: 'Open link',
660 onclick: Actions.gotoSelectedLink(editor)
661 });
662 }
663 };
664 var setupMenuItems = function (editor) {
665 editor.addMenuItem('openlink', {
666 text: 'Open link',
667 icon: 'newtab',
668 onclick: Actions.gotoSelectedLink(editor),
669 onPostRender: Actions.toggleViewLinkState(editor),
670 prependToContext: true
671 });
672 editor.addMenuItem('link', {
673 icon: 'link',
674 text: 'Link',
675 shortcut: 'Meta+K',
676 onclick: Actions.openDialog(editor),
677 stateSelector: 'a[href]',
678 context: 'insert',
679 prependToContext: true
680 });
681 editor.addMenuItem('unlink', {
682 icon: 'unlink',
683 text: 'Remove link',
684 onclick: Utils.unlink(editor),
685 stateSelector: 'a[href]'
686 });
687 };
688 var setupContextToolbars = function (editor) {
689 if (editor.addContextToolbar) {
690 editor.addContextToolbar(Actions.leftClickedOnAHref(editor), 'openlink | link unlink');
691 }
692 };
693 var Controls = {
694 setupButtons: setupButtons,
695 setupMenuItems: setupMenuItems,
696 setupContextToolbars: setupContextToolbars
697 };
698
699 global.add('link', function (editor) {
700 Controls.setupButtons(editor);
701 Controls.setupMenuItems(editor);
702 Controls.setupContextToolbars(editor);
703 Actions.setupGotoLinks(editor);
704 Commands.register(editor);
705 Keyboard.setup(editor);
706 });
707 function Plugin () {
708 }
709
710 return Plugin;
711
712}(window));
713})();
714