Autolink in Atto

Autolink in Atto

by J D -
Number of replies: 6

Hi Moodler, 

I'm not really sure how it is supposed to work in Moodle, if it's a bug or an improvement request. Using latest Moodle 2.7 (2014-06-05), in both Chrome and Firefox and using Atto as default editor, if I type in the editor www.moodle.org, nothing happens and the url remains plain text. However with IE (tested with 8 and 10), the url actually turn into an hyperlink. I expected this last behavior from both browsers above too and I found it was actually a feature in TinyMCE (see http://www.tinymce.com/wiki.php/plugin:autolink and http://tinymcesupport.com/tutorials/autolink-automatic-link-plugin). So... I'm not sure if it's IE here giving me results that are not supposed to be or if it's something broken with Firefox and Chrome or is it something disabled by Moodle. Any thought before I open a bug/improvement in the tracker?

PS. I know there is a filter to turn url found in Moodle texts but then, from what I read in the forum, you need to use the nolink tag if you don't want it to turn into hyperkink, which seems to be a lot of trouble to me. I would prefer to let users decide as they type in text if they want an hyperlink or not.

Thanks.

Average of ratings: Useful (1)
In reply to J D

Re: Autolink in Atto

by Mauno Korpelainen -

It's a feature of IE that could be fixed for

IE9-> , see for example http://dev.ckeditor.com/ticket/6307

In reply to Mauno Korpelainen

Re: Autolink in Atto

by J D -

OK, so IE turns URLs in hyperlink but shouldn't. And I understand that if Autolink don't work with Chrome or FF, it means it is actually disabled intentionally in the different Moodle editors (TinyMCE and Atto), right? Or it's just that nobody cared to enable it on or ask for it. For the reasons I've state above, it's a quick "nice to have" for the users, I think. I'll go then for a feature request.


Thanks.

In reply to J D

Re: Autolink in Atto

by Daniel Thies -
Picture of Core developers Picture of Plugin developers Picture of Testers

I think there may have been a reason why this would be disabled.  This sort of feature is potentially problematic during quizzes using the javascript security.  In this quiz mode the browser window does not allow  typing in addresses on the address bar.  However, autolink would subvert that. 

In reply to Daniel Thies

Re: Autolink in Atto

by Mauno Korpelainen -

It's not actually disabled at all in editors - that autolinking is just one of those "IE features" that is a default feature in all versions of IE and can be disabled in code only for versions 9 and newer. Browsers like FF or Chrome have never added hyperlinks automatically.

To enable autolinking of URLs in all browsers moodle has the "Convert URLs into links filter http://docs.moodle.org/27/en/Convert_URLs_into_links_filter that has also settings for different input formats but in editors (both Atto and TinyMCE) the behaviour is different in IE vs other browsers. When you paste an URL or press enter after an URL IE adds a hyperlink.

To disable autolinking in IE there is no setting in moodle - anywhere. Just the Unlink button in Atto and "Prevent automatic linking" button  in TinyMCE toolbar that create separate spans around URLs wink

But it is also true that some activities strip some HTML tags (or non valid attributes) and particularly some question types accept text only input ...

Average of ratings: Useful (1)
In reply to Mauno Korpelainen

Re: Autolink in Atto

by J D -

Mauno, I don't think that autolinking is only related to IE. Yes IE does it everywhere and should not. But, from the TinyMCE links I've posted in my original post, I still think it's an editor's feature. If you go to the second link with either Chrome or FF and type in the text box "www.moodle.org", as soon you hit space, it turn into an hyperlink. That's why I think it is intentionally disabled, for maybe reasons like the one stated by Daniel.


There is a noautolink button in Atto you can enable but (I think) it is there only to prevent the filter you mention, when activated, to turn things into hyperlink.

In reply to J D

Re: Autolink in Atto

by Mauno Korpelainen -

Ah, thanks for explaining - I was a little slow...

Yes, you can indeed add (enable) autolink functionality to TinyMCE with autolink plugin (if you change TinyMCE to your site default editor or select TinyMCE from your user profile to your editor).

You could edit file lib/editor/tinymce/lib.php and add to protected function get_init_params $params = array somewhere near line 135 for example

'plugins' => 'autolink,lists,table,style,layer,advhr,advlink,emotions,inlinepopups,' .

or you could simply go to Site administration > Plugins > Text editors > TinyMCE HTML editor > General settings and add to Custom configuration box

{"plugins" : "autolink"}

This plugin simply searches urls from editor content area and adds the link tags if the browser is not IE. Core developers should have no problems in creating a similar plugin for Atto if they see it necessary so adding a new feature request to tracker is ok. If I am not wrong there might not be such plugin for Atto available yet...

But IE is really the only browser doing this without asking your permission or coding tricks big grin

Here's the code of tinymce autolink plugin:

(function() {
    tinymce.create('tinymce.plugins.AutolinkPlugin', {
    /**
    * Initializes the plugin, this will be executed after the plugin has been created.
    * This call is done before the editor instance has finished it's initialization so use the onInit event
    * of the editor instance to intercept that event.
    *
    * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
    * @param {string} url Absolute URL to where the plugin is located.
    */

    init : function(ed, url) {
        var t = this;

        // Add a key down handler
        ed.onKeyDown.addToTop(function(ed, e) {
            if (e.keyCode == 13)
                return t.handleEnter(ed);
        });

        // Internet Explorer has built-in automatic linking for most cases
        if (tinyMCE.isIE)
            return;

        ed.onKeyPress.add(function(ed, e) {
            if (e.which == 41)
                return t.handleEclipse(ed);
        });

        // Add a key up handler
        ed.onKeyUp.add(function(ed, e) {
            if (e.keyCode == 32)
                return t.handleSpacebar(ed);
            });
           },

        handleEclipse : function(ed) {
            this.parseCurrentLine(ed, -1, '(', true);
        },

        handleSpacebar : function(ed) {
             this.parseCurrentLine(ed, 0, '', true);
         },

        handleEnter : function(ed) {
            this.parseCurrentLine(ed, -1, '', false);
        },

        parseCurrentLine : function(ed, end_offset, delimiter, goback) {
            var r, end, start, endContainer, bookmark, text, matches, prev, len;

            // We need at least five characters to form a URL,
            // hence, at minimum, five characters from the beginning of the line.
            r = ed.selection.getRng(true).cloneRange();
            if (r.startOffset < 5) {
                // During testing, the caret is placed inbetween two text nodes.
                // The previous text node contains the URL.
                prev = r.endContainer.previousSibling;
                if (prev == null) {
                    if (r.endContainer.firstChild == null || r.endContainer.firstChild.nextSibling == null)
                        return;

                    prev = r.endContainer.firstChild.nextSibling;
                }
                len = prev.length;
                r.setStart(prev, len);
                r.setEnd(prev, len);

                if (r.endOffset < 5)
                    return;

                end = r.endOffset;
                endContainer = prev;
            } else {
                endContainer = r.endContainer;

                // Get a text node
                if (endContainer.nodeType != 3 && endContainer.firstChild) {
                    while (endContainer.nodeType != 3 && endContainer.firstChild)
                        endContainer = endContainer.firstChild;

                    // Move range to text node
                    if (endContainer.nodeType == 3) {
                        r.setStart(endContainer, 0);
                        r.setEnd(endContainer, endContainer.nodeValue.length);
                    }
                }

                if (r.endOffset == 1)
                    end = 2;
                else
                    end = r.endOffset - 1 - end_offset;
            }

            start = end;

            do
            {
                // Move the selection one character backwards.
                r.setStart(endContainer, end >= 2 ? end - 2 : 0);
                r.setEnd(endContainer, end >= 1 ? end - 1 : 0);
                end -= 1;

                // Loop until one of the following is found: a blank space, &nbsp;, delimeter, (end-2) >= 0
            } while (r.toString() != ' ' && r.toString() != '' && r.toString().charCodeAt(0) != 160 && (end -2) >= 0 && r.toString() != delimiter);

            if (r.toString() == delimiter || r.toString().charCodeAt(0) == 160) {
                r.setStart(endContainer, end);
                r.setEnd(endContainer, start);
                end += 1;
            } else if (r.startOffset == 0) {
                r.setStart(endContainer, 0);
                r.setEnd(endContainer, start);
            }
            else {
                r.setStart(endContainer, end);
                r.setEnd(endContainer, start);
            }

            // Exclude last . from word like "www.site.com."
            var text = r.toString();
            if (text.charAt(text.length - 1) == '.') {
                r.setEnd(endContainer, start - 1);
            }

            text = r.toString();
            matches = text.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailtosmile?[A-Z0-9._%+-]+@)(.+)$/i);

            if (matches) {
                if (matches[1] == 'www.') {
                    matches[1] = 'http://www.';
                } else if (/@$/.test(matches[1]) && !/^mailto:/.test(matches[1])) {
                    matches[1] = 'mailto:' + matches[1];
                }

                bookmark = ed.selection.getBookmark();

                ed.selection.setRng(r);
                tinyMCE.execCommand('createlink',false, matches[1] + matches[2]);
                ed.selection.moveToBookmark(bookmark);
                ed.nodeChanged();

                // TODO: Determine if this is still needed.
                if (tinyMCE.isWebKit) {
                    // move the caret to its original position
                    ed.selection.collapse(false);
                    var max = Math.min(endContainer.length, start + 1);
                    r.setStart(endContainer, max);
                    r.setEnd(endContainer, max);
                    ed.selection.setRng(r);
                }
            }
        },

        /**
        * Returns information about the plugin as a name/value array.
        * The current keys are longname, author, authorurl, infourl and version.
        *
        * @return {Object} Name/value array containing information about the plugin.
        */
        getInfo : function() {
            return {
                longname : 'Autolink',
                author : 'Moxiecode Systems AB',
                authorurl : 'http://tinymce.moxiecode.com',
                infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink',
                version : tinymce.majorVersion + "." + tinymce.minorVersion
            };
        }
    });

    // Register plugin
    tinymce.PluginManager.add('autolink', tinymce.plugins.AutolinkPlugin);
})();