[TASK] Re-add autolinking RTE feature in CKeditor 79/54879/2
authorBenni Mack <benni@typo3.org>
Fri, 10 Nov 2017 09:45:55 +0000 (10:45 +0100)
committerBenni Mack <benni@typo3.org>
Thu, 30 Nov 2017 20:38:52 +0000 (21:38 +0100)
A missing functionality is added, which happened when introducing CKeditor.

Automatically linking a URL when typing www.typo3.org should happen directly.

A specific plugin is used for that. Base concept was taken from
https://github.com/Gnodiah/ckeditor-autolink

Resolves: #80977
Releases: master, 8.7
Change-Id: I575b63eeb696166b5e3b06047106e26cb68eb7b4
Reviewed-on: https://review.typo3.org/54879
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
typo3/sysext/rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml
typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/Plugins/autolinking.js [new file with mode: 0644]

index 86ffa8c..e6c84f1 100644 (file)
@@ -5,3 +5,4 @@ editor:
     typo3link: { resource: "EXT:rte_ckeditor/Resources/Public/JavaScript/Plugins/typo3link.js", route: "rteckeditor_wizard_browse_links" }
     # This is a plugin, found here: https://github.com/ufdada/quicktable
     quicktable: { resource: "EXT:rte_ckeditor/Resources/Public/JavaScript/Plugins/quicktable/plugin.js" }
+    autolinking: { resource: "EXT:rte_ckeditor/Resources/Public/JavaScript/Plugins/autolinking.js" }
diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/Plugins/autolinking.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/Plugins/autolinking.js
new file mode 100644 (file)
index 0000000..409bc9c
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * AutoLinking plugin for CKEditor 4.7
+ *
+ * Automatically creates an anchor tag when typing an URL or email.
+ * Inspired by https://github.com/Gnodiah/ckeditor-autolink
+ */
+CKEDITOR.plugins.add('autolinking', {
+       init: function(editor) {
+               var spaceChar = 32, enterChar = 13, tabChar = 9, fillChar = ' ';
+               var isFillChar = function (node, isInStart) {
+                       return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + ' '), '').length;
+               };
+               var isBodyTag = function (node) {
+                       return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
+               };
+               var isAnchorTag = function (node) {
+                       return node && node.nodeType == 1 && node.tagName.toLowerCase() === 'a';
+               };
+               var html = function (str) {
+                       return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
+                               return {
+                                       '&lt;':'<',
+                                       '&amp;':'&',
+                                       '&quot;':'"',
+                                       '&gt;':'>',
+                                       '&#39;':"'"
+                               }[m]
+                       }) : '';
+               };
+
+               var hasParentAnchorTag = function (node) {
+                       if (node && !isBodyTag(node)) {
+                               while (node) {
+                                       if (isBodyTag(node)) {
+                                               return false;
+                                       } else if (isAnchorTag(node)) {
+                                               return true;
+                                       }
+                                       node = node.parentNode;
+                               }
+                       }
+                       return false;
+               };
+
+               editor.on('instanceReady', function() {
+                       editor.autolinking = function(evt) {
+                               var sel = editor.getSelection().getNative(),
+                                       range = sel.getRangeAt(0).cloneRange(),
+                                       offset,
+                                       charCode;
+
+                               var start = range.startContainer;
+                               while (start.nodeType == 1 && range.startOffset > 0) {
+                                       start = range.startContainer.childNodes[range.startOffset - 1];
+                                       if (!start) break;
+
+                                       range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length);
+                                       range.collapse(true);
+                                       start = range.startContainer;
+                               }
+
+                               do {
+                                       if (range.startOffset == 0) {
+                                               start = range.startContainer.previousSibling;
+
+                                               while (start && start.nodeType == 1) {
+                                                       if (CKEDITOR.env.gecko && start.firstChild)
+                                                               start = start.firstChild;
+                                                       else
+                                                               start = start.lastChild;
+                                               }
+                                               if (!start || isFillChar(start)) break;
+                                               offset = start.nodeValue.length;
+                                       } else {
+                                               start = range.startContainer;
+                                               offset = range.startOffset;
+                                       }
+                                       range.setStart(start, offset - 1);
+                                       charCode = range.toString().charCodeAt(0);
+                               } while (charCode != 160 && charCode != spaceChar);
+
+                               if (range.toString().replace(new RegExp(fillChar, 'g'), '').match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)) {
+                                       while (range.toString().length) {
+                                               if (/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())) break;
+
+                                               try {
+                                                       range.setStart(range.startContainer, range.startOffset+1);
+                                               } catch (e) {
+                                                       var startChar = range.startContainer;
+                                                       while (!(next = startChar.nextSibling)) {
+                                                               if (isBodyTag(startChar)) {
+                                                                       return;
+                                                               }
+                                                               startChar = startChar.parentNode;
+                                                       }
+                                                       range.setStart(next, 0);
+                                               }
+                                       }
+
+                                       if (hasParentAnchorTag(range.startContainer)) {
+                                               return;
+                                       }
+
+                                       var a = document.createElement('a'),
+                                               href;
+
+                                       editor.undoManger && editor.undoManger.save();
+                                       a.appendChild(range.extractContents());
+                                       a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, '');
+                                       href = a.getAttribute('href').replace(new RegExp(fillChar,'g'), '');
+                                       href = /^(?:https?:\/\/)/ig.test(href) ? href : 'http://' + href;
+                                       a.href = html(href);
+
+                                       range.insertNode(a);
+                                       range.setStart(a.nextSibling, 0);
+                                       range.collapse(true);
+                                       sel.removeAllRanges();
+                                       sel.addRange(range);
+                                       editor.undoManger && editor.undoManger.save();
+                               }
+                       };
+
+                       editor.on('key', function(evt) {
+                               if (evt.data.keyCode === spaceChar || evt.data.keyCode === tabChar || evt.data.keyCode === enterChar) {
+                                       editor.autolinking(evt);
+                               }
+                       });
+               });
+       }
+});