From b6b131cf50c881e6807452ab3d122b0e220da838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Tue, 3 Oct 2017 19:09:27 +0200 Subject: [PATCH 01/11] Use a new approach for multilang tags highlighting The previous approach had some assumptions (e.g., it assumed there was a singe instance of the editor on the same page) that lead to some bugs and corner cases. Now the plugin hooks on the form submit event and clean the highlighting tags there. The cleaning doesn't use any regexes any longer. Instead it uses DOM operations that are more robust and potentially faster. Also multilang tag highlighting 's are now removed/added when switching to/from HTML view, reducing clutter when editing the HTML code. Some initial coding style cleanup work has been carried out. More to follow. Tested in Firefox 56 and Chromium 61.0.3163.100. Closes #2 #18 #21 --- default-css.php | 7 +- settings.php | 2 +- version.php | 4 +- .../moodle-atto_multilang2-button-debug.js | 463 +++++++----------- .../moodle-atto_multilang2-button.js | 463 +++++++----------- yui/src/button/js/button.js | 463 +++++++----------- 6 files changed, 516 insertions(+), 886 deletions(-) diff --git a/default-css.php b/default-css.php index 5fdc0e5..0b5f0b0 100644 --- a/default-css.php +++ b/default-css.php @@ -22,12 +22,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -$multilang2_default_css = <<< EOF +defined('MOODLE_INTERNAL') || die(); +$multilang2defaultcss = " outline: 1px dotted; padding: 0.1em; margin: 0em 0.1em; background-color: #ffffaa; - -EOF -; +"; diff --git a/settings.php b/settings.php index ef0f539..d38e26e 100644 --- a/settings.php +++ b/settings.php @@ -30,4 +30,4 @@ get_string('highlight', 'atto_multilang2'), get_string('highlight_desc', 'atto_multilang2'), 1)); $settings->add(new admin_setting_configtextarea('atto_multilang2/customcss', get_string('customcss', 'atto_multilang2'), get_string('customcss_desc', 'atto_multilang2'), - $multilang2_default_css, PARAM_RAW)); + $multilang2defaultcss, PARAM_RAW)); diff --git a/version.php b/version.php index 41e089b..2772a37 100644 --- a/version.php +++ b/version.php @@ -24,8 +24,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017052400; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 'v3.3.1.7 (version v1.7 for Moodle 3.3) (2016121100)'; +$plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 'v3.3.1.8 (version v1.8 for Moodle 3.3) (2016121100)'; $plugin->requires = 2017051500; // Required Moodle version. $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js index 194364a..84421be 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js @@ -18,6 +18,7 @@ YUI.add('moodle-atto_multilang2-button', function (Y, NAME) { /** * @package atto_multilang2 * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea + * @copyright 2017 onwards Iñaki Arenaza & Mondragon Unibertsitatea * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -42,14 +43,14 @@ var CLASSES = { 'padding: 0.1em;' + 'margin: 0em 0.1em;' + 'background-color: #ffffaa;', + OPENING_SPAN = ''; + CLOSING_SPAN = ''; TEMPLATES = { - SPANED: ' {mlang ' + LANG_WILDCARD + '}' + - CONTENT_WILDCARD + - '{mlang} ', + SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', - NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' + NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' }, - OPENING_SPAN = ''; /** * Atto text editor multilanguage plugin. @@ -71,12 +72,10 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att _highlight: true, initializer: function() { - var hascapability = this.get(ATTR_CAPABILITY), - toolbarItems = []; + var hascapability = this.get(ATTR_CAPABILITY); if (hascapability) { - toolbarItems = this._initializeToolbarItems(); - this._highlight = this.get(ATTR_HIGHLIGHT); + var toolbarItems = this._initializeToolbarItems(); this.addToolbarMenu({ globalItemConfig: { @@ -87,32 +86,34 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att items: toolbarItems }); - this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - - this._addDelimiterCss(); + this._tagTemplate = TEMPLATES.NOT_SPANNED; + this._highlight = this.get(ATTR_HIGHLIGHT); if (this._highlight) { - this._decorateTagsOnInit(); - this._setSubmitListeners(); - } - } - }, + this._tagTemplate = TEMPLATES.SPANNED; + + // Attach a submit listener to the form, so we can remove + // the highlighting html before sending content to Moodle. + var host = this.get('host'); + var form = host.textarea.ancestor('form'); + if (form) { + form.on('submit', this._cleanMlangTags, this); + } - /** - * Adds the CSS rules for the delimiters, received as parameter from lib.php. - * - * @method _addDelimiterCss - * @private - */ - _addDelimiterCss: function() { - var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', - style; + // Listen to every change of the text cursor in the text area, to see if + // the cursor is placed within a multilang tag. + this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = css; + // Highlight the multilang tags once everything is loaded. + this.get('host').on('pluginsloaded', this._addHighlightingCss, this); + this.get('host').on('pluginsloaded', this._highlightMlangTags, this); - document.head.appendChild(style); + // Hook into host.updateOriginal() and host.updateFromTextArea() + // so we can add/remove highlighting when we switch to/from HTML view. + this._hookUpdateOriginal(); + this._hookUpdateFromTextArea(); + } + } }, /** @@ -129,7 +130,6 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att langCode; languages = JSON.parse(this.get(ATTR_LANGUAGES)); - for (langCode in languages) { if (languages.hasOwnProperty(langCode)) { toolbarItems.push({ @@ -142,6 +142,76 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att return toolbarItems; }, + /** + * Adds the CSS rules for the delimiters, received as parameter from lib.php. + * + * @method _addHighlightingCss + * @private + */ + _addHighlightingCss: function() { + var css = '.' + CLASSES.TAG + ' {' + this.get(ATTR_CSS) + '}', + style; + + style = document.createElement('style'); + style.type = 'text/css'; + style.innerHTML = css; + + document.head.appendChild(style); + }, + + /** + * Hook the host.updateOriginal() method to allow us to remove the highlighting html when + * switching to HTML view. As the HTML view plugin doesn't provide a hook or fire an event + * to notify about the switch to HTML view, we need to hijack host.updateOriginal and look + * for the caller. Once we've cleaned up the highlighting, we need to execute the original + * host.updateOriginal() method. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateOriginal + * @private + */ + _hookUpdateOriginal: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags() + + host.updateOriginal = (function() { + var _updateOriginal = host.updateOriginal; + return function() { + if (multilangplugin._highlight && (this.updateOriginal.caller.name === "_showHTML")) { + multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); + } + return _updateOriginal.apply(this, arguments); + } + })(); + }, + + /** + * Hook the host.updateFromTextAreal() method to allow us to re-add the highlighting + * html when switching from HTML view. As the HTML view plugin doesn't provide a hook + * or fire an event to notify about the switch from HTML view, we need to hijack + * host.updateFromTextArea and look for the caller. Once we've executed the original + * host.updateFromTextArea() method, we re-added the highlighting. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateFromTextArea + * @private + */ + _hookUpdateFromTextArea: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags() + + host.updateFromTextArea = (function() { + var _updateFromTextArea = host.updateFromTextArea; + return function() { + var ret = _updateFromTextArea.apply(this, arguments); + if (multilangplugin._highlight && (this.updateFromTextArea.caller.name === "_showHTML")) { + multilangplugin._highlightMlangTags(); + } + return ret; + } + })(); + }, + /** * Retrieves the selected text, wraps it with the multilang tags, * and replaces the selected text in the editor with with it. @@ -164,7 +234,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att taggedContent, content; - taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; + taggedContent = this._tagTemplate; selection = this._getSelectionHTML(); content = (host.getSelection().toString().length === 0) ? ' ' : selection; @@ -179,7 +249,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Retrieves selected text with its HTML. - * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 + * Taken from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 * * @method _getSelectionHTML * @private @@ -196,8 +266,8 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att selection = window.getSelection(); if (selection.rangeCount) { - container = document.createElement('div'); - for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { + var container = document.createElement('div'); + for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } html = container.innerHTML; @@ -214,232 +284,44 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Listens to every change of the text cursor in the text area. If the - * cursor is placed within a multilang tag, the whole tag is selected. + * cursor is placed within a highlighted multilang tag, the whole tag is selected. * * @method _checkSelectionChange + * @param {EventFacade} e An event object. * @private */ - _checkSelectionChange: function() { + _checkSelectionChange: function(e) { var host = this.get('host'), - node = host.getSelectionParentNode(), - nodeValue = Y.one(node).get('text'), - isTextNode, - isLangTag; - - isTextNode = Y.one(node).toString().indexOf('#text') > - 1; - isLangTag = (nodeValue.match(/\{mlang/g).length === 1); + node = host.getSelectionParentNode(); - if (isTextNode && isLangTag) { - host.setSelection(host.getSelectionFromNode(Y.one(node))); + // If the event fires without a parent node, ignore the whole thing. + if ((typeof node === 'undefined') || (node === null)) { + return; } - }, - - /** - * Retrieves the inputs of type submit, and, for each element, calls the function - * that sets the submit listener. Is not made in this function because there is - * not any (apparent) way to access class scope from YUI closure. - * - * @method _setSubmitListeners - * @private - */ - _setSubmitListeners: function() { - var submitButtons = Y.all('input[type=submit]'); - - submitButtons.each(this._addListenerToSubmitButtons, this); - }, - - /** - * Adds the clean tags submit listener of each input[type="submit"], but only if - * it's not 'cancel' type, and if its parent form is of 'mform' class, because there - * may be any other submit type (such us administrator's search button). - * - * @method _addListenerToSubmitButtons - * @param {Node} buttonNode - * @private - */ - _addListenerToSubmitButtons: function(buttonNode) { - var buttonObject, - className, - parentFormClassName, - notCancelButton, - notSearchButton; - - buttonObject = document.getElementById(buttonNode.get('id')); - if (buttonObject !== null) { - className = buttonObject.className; - parentFormClassName = buttonObject.form.className; - - notCancelButton = className.match(/btn-cancel/g) === null; - notSearchButton = parentFormClassName.match(/mform/g).length > 0; - - if (notCancelButton && notSearchButton) { - buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); - } + if ((node.parentElement.nodeName === 'SPAN') && + (node.parentElement.getAttributeNode('class').value.indexOf(CLASSES.TAG) !== -1)) { + selection = host.getSelectionFromNode(Y.one(node)); + host.setSelection(selection); + return; } }, /** - * When submit button clicked, this function is invoked. It has to stop the submission, - * in order to process the textarea to clean the tags. - * - * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, - * an then simulates the click, to submit the form. - * - * @method _cleanTagsOnSubmit - * @param {EventFacade} event - * @param {Node} submitButton - * @private - */ - _cleanTagsOnSubmit: function(event, submitButton) { - event.preventDefault(); - - this._cleanTagsWithNoYuiId(); - this._cleanTagsWithYuiId(); - - submitButton.detach('click', this._cleanTagsOnSubmit); - submitButton.simulate('click'); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that do not have "id" attribute. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. - * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method _cleanTagsWithNoYuiId - * @private - */ - _cleanTagsWithNoYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtags, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression; - - regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags === null) { - continue; - } - - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - } - - this.markUpdated(); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. + * When submitting the form, this function is invoked to clean the highlighting html code. * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method anTagsWithYuiId + * @method _cleanMlangTags * @private */ - _cleanTagsWithYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression, - openingspanwithyui, - spanedmlangtagsdwithyui, - mlangtag; - - openingspanwithyui = OPENING_SPAN.replace('', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtagsdwithyui = innerHTML.match(regularExpression); - - if (spanedmlangtagsdwithyui === null) { - continue; - } - - for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { - spanedmlangtag = spanedmlangtagsdwithyui[index]; - mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; - - cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - + _cleanMlangTags: function() { + if (this._highlight) { + this.editor.setHTML(this._getHTMLwithCleanedTags(this.editor.getHTML())); this.markUpdated(); } }, /** - * Adds the tags to the {mlang} tags when the editor is loaded. - * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, - * I don't know. But, if we call it after setting the HTML, the {mlang} - * tags flicker with the decoration, and returns to their original state. + * Adds the tags to the {mlang} tags if highlighting is enable. * * Instead of taking the HTML directly from the textarea, we have to * retrieve it, first, without the tags that can be stored @@ -449,84 +331,79 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * * Every different {mlang} tag has to be replaced only once, otherwise, * nested s will be created in every repeated replacement. So, we - * have to have a track of which replacements have been made. + * need to track which replacements have been made. * - * @method _decorateTagsOnInit + * @method _highlightMlangTags * @private */ - _decorateTagsOnInit: function() { - var textarea = Y.all('.editor_atto_content'), - innerHTML, + _highlightMlangTags: function() { + var editorHTML, regularExpression, mlangtags, mlangtag, index, - decoratedmlangtag, + highlightedmlangtag, replacementsmade = [], notreplacedyet; - - innerHTML = this._getHTMLwithCleanedTags(); - - regularExpression = new RegExp('{mlang.*?}', 'g'); - mlangtags = innerHTML.match(regularExpression); - - if (mlangtags !== null) { - for (index = 0; index < mlangtags.length; index++) { - mlangtag = mlangtags[index]; - - notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; - - if (notreplacedyet) { - replacementsmade.push(mlangtag); - - decoratedmlangtag = OPENING_SPAN + mlangtag + ''; - regularExpression = new RegExp(mlangtag, 'g'); - - innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); + if (this._highlight){ + editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); + + regularExpression = new RegExp('{mlang.*?}', 'g'); + mlangtags = editorHTML.match(regularExpression); + if (mlangtags !== null) { + for (index = 0; index < mlangtags.length; index++) { + mlangtag = mlangtags[index]; + + notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; + if (notreplacedyet) { + replacementsmade.push(mlangtag); + highlightedmlangtag = OPENING_SPAN + mlangtag + CLOSING_SPAN; + regularExpression = new RegExp(mlangtag, 'g'); + editorHTML = editorHTML.replace(regularExpression, highlightedmlangtag); + } } + + this.editor.setHTML(editorHTML); } - textarea.set('innerHTML', innerHTML); + this.markUpdated(); } - }, /** - * This function returns the HTML as it is in the textarea, but cleaning every + * This function returns the HTML passed in as parameter, but cleaning every multilang * tag around the {mlang} tags. This is necessary for decorating tags on * init, because it could happen that in database are stored the {mlang} tags with * their tags, due to a bug in version 2015120501. * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 + * Implementation based on code from EditorClean._clearSpans() * * @method _getHTMLwithCleanedTags - * @return {string} HTML in textarea, without any around {mlang} tags + * @param {string} content The to be cleaned. + * @return {string} HTML in editor, without any around {mlang} tags. */ - _getHTMLwithCleanedTags: function() { - var host = this.get('host'), - innerHTML = host.getCleanHTML(), - regexString, - regularExpression, - spanedmlangtags, - spanedmlangtag, - cleanmlangtag, - index; - - regexString = OPENING_SPAN + '.*?' + ''; - regularExpression = new RegExp(regexString, 'g'); - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags !== null) { - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - cleanmlangtag = cleanmlangtag.replace('', ''); + _getHTMLwithCleanedTags: function(content) { + // This is better to run detached from the DOM, so the browser doesn't try to update on each change. + var holder = document.createElement('div'); + holder.innerHTML = content; + var spans = holder.getElementsByTagName('span'); + + // Since we will be removing elements from the list, we should copy it to an array, making it static. + var spansarr = Array.prototype.slice.call(spans, 0); + + spansarr.forEach(function(span) { + if (span.className.indexOf(CLASSES.TAG) !== -1) { + // Move each child (if they exist) to the parent in place of this span. + while (span.firstChild) { + span.parentNode.insertBefore(span.firstChild, span); + } - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); + // Remove the now empty span. + span.parentNode.removeChild(span); } - } + }); - return innerHTML; + return holder.innerHTML; } }, { diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js index 194364a..84421be 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js @@ -18,6 +18,7 @@ YUI.add('moodle-atto_multilang2-button', function (Y, NAME) { /** * @package atto_multilang2 * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea + * @copyright 2017 onwards Iñaki Arenaza & Mondragon Unibertsitatea * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -42,14 +43,14 @@ var CLASSES = { 'padding: 0.1em;' + 'margin: 0em 0.1em;' + 'background-color: #ffffaa;', + OPENING_SPAN = ''; + CLOSING_SPAN = ''; TEMPLATES = { - SPANED: ' {mlang ' + LANG_WILDCARD + '}' + - CONTENT_WILDCARD + - '{mlang} ', + SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', - NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' + NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' }, - OPENING_SPAN = ''; /** * Atto text editor multilanguage plugin. @@ -71,12 +72,10 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att _highlight: true, initializer: function() { - var hascapability = this.get(ATTR_CAPABILITY), - toolbarItems = []; + var hascapability = this.get(ATTR_CAPABILITY); if (hascapability) { - toolbarItems = this._initializeToolbarItems(); - this._highlight = this.get(ATTR_HIGHLIGHT); + var toolbarItems = this._initializeToolbarItems(); this.addToolbarMenu({ globalItemConfig: { @@ -87,32 +86,34 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att items: toolbarItems }); - this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - - this._addDelimiterCss(); + this._tagTemplate = TEMPLATES.NOT_SPANNED; + this._highlight = this.get(ATTR_HIGHLIGHT); if (this._highlight) { - this._decorateTagsOnInit(); - this._setSubmitListeners(); - } - } - }, + this._tagTemplate = TEMPLATES.SPANNED; + + // Attach a submit listener to the form, so we can remove + // the highlighting html before sending content to Moodle. + var host = this.get('host'); + var form = host.textarea.ancestor('form'); + if (form) { + form.on('submit', this._cleanMlangTags, this); + } - /** - * Adds the CSS rules for the delimiters, received as parameter from lib.php. - * - * @method _addDelimiterCss - * @private - */ - _addDelimiterCss: function() { - var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', - style; + // Listen to every change of the text cursor in the text area, to see if + // the cursor is placed within a multilang tag. + this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = css; + // Highlight the multilang tags once everything is loaded. + this.get('host').on('pluginsloaded', this._addHighlightingCss, this); + this.get('host').on('pluginsloaded', this._highlightMlangTags, this); - document.head.appendChild(style); + // Hook into host.updateOriginal() and host.updateFromTextArea() + // so we can add/remove highlighting when we switch to/from HTML view. + this._hookUpdateOriginal(); + this._hookUpdateFromTextArea(); + } + } }, /** @@ -129,7 +130,6 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att langCode; languages = JSON.parse(this.get(ATTR_LANGUAGES)); - for (langCode in languages) { if (languages.hasOwnProperty(langCode)) { toolbarItems.push({ @@ -142,6 +142,76 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att return toolbarItems; }, + /** + * Adds the CSS rules for the delimiters, received as parameter from lib.php. + * + * @method _addHighlightingCss + * @private + */ + _addHighlightingCss: function() { + var css = '.' + CLASSES.TAG + ' {' + this.get(ATTR_CSS) + '}', + style; + + style = document.createElement('style'); + style.type = 'text/css'; + style.innerHTML = css; + + document.head.appendChild(style); + }, + + /** + * Hook the host.updateOriginal() method to allow us to remove the highlighting html when + * switching to HTML view. As the HTML view plugin doesn't provide a hook or fire an event + * to notify about the switch to HTML view, we need to hijack host.updateOriginal and look + * for the caller. Once we've cleaned up the highlighting, we need to execute the original + * host.updateOriginal() method. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateOriginal + * @private + */ + _hookUpdateOriginal: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags() + + host.updateOriginal = (function() { + var _updateOriginal = host.updateOriginal; + return function() { + if (multilangplugin._highlight && (this.updateOriginal.caller.name === "_showHTML")) { + multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); + } + return _updateOriginal.apply(this, arguments); + } + })(); + }, + + /** + * Hook the host.updateFromTextAreal() method to allow us to re-add the highlighting + * html when switching from HTML view. As the HTML view plugin doesn't provide a hook + * or fire an event to notify about the switch from HTML view, we need to hijack + * host.updateFromTextArea and look for the caller. Once we've executed the original + * host.updateFromTextArea() method, we re-added the highlighting. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateFromTextArea + * @private + */ + _hookUpdateFromTextArea: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags() + + host.updateFromTextArea = (function() { + var _updateFromTextArea = host.updateFromTextArea; + return function() { + var ret = _updateFromTextArea.apply(this, arguments); + if (multilangplugin._highlight && (this.updateFromTextArea.caller.name === "_showHTML")) { + multilangplugin._highlightMlangTags(); + } + return ret; + } + })(); + }, + /** * Retrieves the selected text, wraps it with the multilang tags, * and replaces the selected text in the editor with with it. @@ -164,7 +234,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att taggedContent, content; - taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; + taggedContent = this._tagTemplate; selection = this._getSelectionHTML(); content = (host.getSelection().toString().length === 0) ? ' ' : selection; @@ -179,7 +249,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Retrieves selected text with its HTML. - * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 + * Taken from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 * * @method _getSelectionHTML * @private @@ -196,8 +266,8 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att selection = window.getSelection(); if (selection.rangeCount) { - container = document.createElement('div'); - for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { + var container = document.createElement('div'); + for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } html = container.innerHTML; @@ -214,232 +284,44 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Listens to every change of the text cursor in the text area. If the - * cursor is placed within a multilang tag, the whole tag is selected. + * cursor is placed within a highlighted multilang tag, the whole tag is selected. * * @method _checkSelectionChange + * @param {EventFacade} e An event object. * @private */ - _checkSelectionChange: function() { + _checkSelectionChange: function(e) { var host = this.get('host'), - node = host.getSelectionParentNode(), - nodeValue = Y.one(node).get('text'), - isTextNode, - isLangTag; - - isTextNode = Y.one(node).toString().indexOf('#text') > - 1; - isLangTag = (nodeValue.match(/\{mlang/g).length === 1); + node = host.getSelectionParentNode(); - if (isTextNode && isLangTag) { - host.setSelection(host.getSelectionFromNode(Y.one(node))); + // If the event fires without a parent node, ignore the whole thing. + if ((typeof node === 'undefined') || (node === null)) { + return; } - }, - - /** - * Retrieves the inputs of type submit, and, for each element, calls the function - * that sets the submit listener. Is not made in this function because there is - * not any (apparent) way to access class scope from YUI closure. - * - * @method _setSubmitListeners - * @private - */ - _setSubmitListeners: function() { - var submitButtons = Y.all('input[type=submit]'); - - submitButtons.each(this._addListenerToSubmitButtons, this); - }, - - /** - * Adds the clean tags submit listener of each input[type="submit"], but only if - * it's not 'cancel' type, and if its parent form is of 'mform' class, because there - * may be any other submit type (such us administrator's search button). - * - * @method _addListenerToSubmitButtons - * @param {Node} buttonNode - * @private - */ - _addListenerToSubmitButtons: function(buttonNode) { - var buttonObject, - className, - parentFormClassName, - notCancelButton, - notSearchButton; - - buttonObject = document.getElementById(buttonNode.get('id')); - if (buttonObject !== null) { - className = buttonObject.className; - parentFormClassName = buttonObject.form.className; - - notCancelButton = className.match(/btn-cancel/g) === null; - notSearchButton = parentFormClassName.match(/mform/g).length > 0; - - if (notCancelButton && notSearchButton) { - buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); - } + if ((node.parentElement.nodeName === 'SPAN') && + (node.parentElement.getAttributeNode('class').value.indexOf(CLASSES.TAG) !== -1)) { + selection = host.getSelectionFromNode(Y.one(node)); + host.setSelection(selection); + return; } }, /** - * When submit button clicked, this function is invoked. It has to stop the submission, - * in order to process the textarea to clean the tags. - * - * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, - * an then simulates the click, to submit the form. - * - * @method _cleanTagsOnSubmit - * @param {EventFacade} event - * @param {Node} submitButton - * @private - */ - _cleanTagsOnSubmit: function(event, submitButton) { - event.preventDefault(); - - this._cleanTagsWithNoYuiId(); - this._cleanTagsWithYuiId(); - - submitButton.detach('click', this._cleanTagsOnSubmit); - submitButton.simulate('click'); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that do not have "id" attribute. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. - * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method _cleanTagsWithNoYuiId - * @private - */ - _cleanTagsWithNoYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtags, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression; - - regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags === null) { - continue; - } - - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - } - - this.markUpdated(); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. + * When submitting the form, this function is invoked to clean the highlighting html code. * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method anTagsWithYuiId + * @method _cleanMlangTags * @private */ - _cleanTagsWithYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression, - openingspanwithyui, - spanedmlangtagsdwithyui, - mlangtag; - - openingspanwithyui = OPENING_SPAN.replace('', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtagsdwithyui = innerHTML.match(regularExpression); - - if (spanedmlangtagsdwithyui === null) { - continue; - } - - for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { - spanedmlangtag = spanedmlangtagsdwithyui[index]; - mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; - - cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - + _cleanMlangTags: function() { + if (this._highlight) { + this.editor.setHTML(this._getHTMLwithCleanedTags(this.editor.getHTML())); this.markUpdated(); } }, /** - * Adds the tags to the {mlang} tags when the editor is loaded. - * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, - * I don't know. But, if we call it after setting the HTML, the {mlang} - * tags flicker with the decoration, and returns to their original state. + * Adds the tags to the {mlang} tags if highlighting is enable. * * Instead of taking the HTML directly from the textarea, we have to * retrieve it, first, without the tags that can be stored @@ -449,84 +331,79 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * * Every different {mlang} tag has to be replaced only once, otherwise, * nested s will be created in every repeated replacement. So, we - * have to have a track of which replacements have been made. + * need to track which replacements have been made. * - * @method _decorateTagsOnInit + * @method _highlightMlangTags * @private */ - _decorateTagsOnInit: function() { - var textarea = Y.all('.editor_atto_content'), - innerHTML, + _highlightMlangTags: function() { + var editorHTML, regularExpression, mlangtags, mlangtag, index, - decoratedmlangtag, + highlightedmlangtag, replacementsmade = [], notreplacedyet; - - innerHTML = this._getHTMLwithCleanedTags(); - - regularExpression = new RegExp('{mlang.*?}', 'g'); - mlangtags = innerHTML.match(regularExpression); - - if (mlangtags !== null) { - for (index = 0; index < mlangtags.length; index++) { - mlangtag = mlangtags[index]; - - notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; - - if (notreplacedyet) { - replacementsmade.push(mlangtag); - - decoratedmlangtag = OPENING_SPAN + mlangtag + ''; - regularExpression = new RegExp(mlangtag, 'g'); - - innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); + if (this._highlight){ + editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); + + regularExpression = new RegExp('{mlang.*?}', 'g'); + mlangtags = editorHTML.match(regularExpression); + if (mlangtags !== null) { + for (index = 0; index < mlangtags.length; index++) { + mlangtag = mlangtags[index]; + + notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; + if (notreplacedyet) { + replacementsmade.push(mlangtag); + highlightedmlangtag = OPENING_SPAN + mlangtag + CLOSING_SPAN; + regularExpression = new RegExp(mlangtag, 'g'); + editorHTML = editorHTML.replace(regularExpression, highlightedmlangtag); + } } + + this.editor.setHTML(editorHTML); } - textarea.set('innerHTML', innerHTML); + this.markUpdated(); } - }, /** - * This function returns the HTML as it is in the textarea, but cleaning every + * This function returns the HTML passed in as parameter, but cleaning every multilang * tag around the {mlang} tags. This is necessary for decorating tags on * init, because it could happen that in database are stored the {mlang} tags with * their tags, due to a bug in version 2015120501. * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 + * Implementation based on code from EditorClean._clearSpans() * * @method _getHTMLwithCleanedTags - * @return {string} HTML in textarea, without any around {mlang} tags + * @param {string} content The to be cleaned. + * @return {string} HTML in editor, without any around {mlang} tags. */ - _getHTMLwithCleanedTags: function() { - var host = this.get('host'), - innerHTML = host.getCleanHTML(), - regexString, - regularExpression, - spanedmlangtags, - spanedmlangtag, - cleanmlangtag, - index; - - regexString = OPENING_SPAN + '.*?' + ''; - regularExpression = new RegExp(regexString, 'g'); - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags !== null) { - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - cleanmlangtag = cleanmlangtag.replace('', ''); + _getHTMLwithCleanedTags: function(content) { + // This is better to run detached from the DOM, so the browser doesn't try to update on each change. + var holder = document.createElement('div'); + holder.innerHTML = content; + var spans = holder.getElementsByTagName('span'); + + // Since we will be removing elements from the list, we should copy it to an array, making it static. + var spansarr = Array.prototype.slice.call(spans, 0); + + spansarr.forEach(function(span) { + if (span.className.indexOf(CLASSES.TAG) !== -1) { + // Move each child (if they exist) to the parent in place of this span. + while (span.firstChild) { + span.parentNode.insertBefore(span.firstChild, span); + } - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); + // Remove the now empty span. + span.parentNode.removeChild(span); } - } + }); - return innerHTML; + return holder.innerHTML; } }, { diff --git a/yui/src/button/js/button.js b/yui/src/button/js/button.js index dca595a..e697af9 100644 --- a/yui/src/button/js/button.js +++ b/yui/src/button/js/button.js @@ -16,6 +16,7 @@ /** * @package atto_multilang2 * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea + * @copyright 2017 onwards Iñaki Arenaza & Mondragon Unibertsitatea * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -40,14 +41,14 @@ var CLASSES = { 'padding: 0.1em;' + 'margin: 0em 0.1em;' + 'background-color: #ffffaa;', + OPENING_SPAN = ''; + CLOSING_SPAN = ''; TEMPLATES = { - SPANED: ' {mlang ' + LANG_WILDCARD + '}' + - CONTENT_WILDCARD + - '{mlang} ', + SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', - NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' + NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' }, - OPENING_SPAN = ''; /** * Atto text editor multilanguage plugin. @@ -69,12 +70,10 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att _highlight: true, initializer: function() { - var hascapability = this.get(ATTR_CAPABILITY), - toolbarItems = []; + var hascapability = this.get(ATTR_CAPABILITY); if (hascapability) { - toolbarItems = this._initializeToolbarItems(); - this._highlight = this.get(ATTR_HIGHLIGHT); + var toolbarItems = this._initializeToolbarItems(); this.addToolbarMenu({ globalItemConfig: { @@ -85,32 +84,34 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att items: toolbarItems }); - this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - - this._addDelimiterCss(); + this._tagTemplate = TEMPLATES.NOT_SPANNED; + this._highlight = this.get(ATTR_HIGHLIGHT); if (this._highlight) { - this._decorateTagsOnInit(); - this._setSubmitListeners(); - } - } - }, + this._tagTemplate = TEMPLATES.SPANNED; + + // Attach a submit listener to the form, so we can remove + // the highlighting html before sending content to Moodle. + var host = this.get('host'); + var form = host.textarea.ancestor('form'); + if (form) { + form.on('submit', this._cleanMlangTags, this); + } - /** - * Adds the CSS rules for the delimiters, received as parameter from lib.php. - * - * @method _addDelimiterCss - * @private - */ - _addDelimiterCss: function() { - var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', - style; + // Listen to every change of the text cursor in the text area, to see if + // the cursor is placed within a multilang tag. + this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); - style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = css; + // Highlight the multilang tags once everything is loaded. + this.get('host').on('pluginsloaded', this._addHighlightingCss, this); + this.get('host').on('pluginsloaded', this._highlightMlangTags, this); - document.head.appendChild(style); + // Hook into host.updateOriginal() and host.updateFromTextArea() + // so we can add/remove highlighting when we switch to/from HTML view. + this._hookUpdateOriginal(); + this._hookUpdateFromTextArea(); + } + } }, /** @@ -127,7 +128,6 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att langCode; languages = JSON.parse(this.get(ATTR_LANGUAGES)); - for (langCode in languages) { if (languages.hasOwnProperty(langCode)) { toolbarItems.push({ @@ -140,6 +140,76 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att return toolbarItems; }, + /** + * Adds the CSS rules for the delimiters, received as parameter from lib.php. + * + * @method _addHighlightingCss + * @private + */ + _addHighlightingCss: function() { + var css = '.' + CLASSES.TAG + ' {' + this.get(ATTR_CSS) + '}', + style; + + style = document.createElement('style'); + style.type = 'text/css'; + style.innerHTML = css; + + document.head.appendChild(style); + }, + + /** + * Hook the host.updateOriginal() method to allow us to remove the highlighting html when + * switching to HTML view. As the HTML view plugin doesn't provide a hook or fire an event + * to notify about the switch to HTML view, we need to hijack host.updateOriginal and look + * for the caller. Once we've cleaned up the highlighting, we need to execute the original + * host.updateOriginal() method. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateOriginal + * @private + */ + _hookUpdateOriginal: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags() + + host.updateOriginal = (function() { + var _updateOriginal = host.updateOriginal; + return function() { + if (multilangplugin._highlight && (this.updateOriginal.caller.name === "_showHTML")) { + multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); + } + return _updateOriginal.apply(this, arguments); + } + })(); + }, + + /** + * Hook the host.updateFromTextAreal() method to allow us to re-add the highlighting + * html when switching from HTML view. As the HTML view plugin doesn't provide a hook + * or fire an event to notify about the switch from HTML view, we need to hijack + * host.updateFromTextArea and look for the caller. Once we've executed the original + * host.updateFromTextArea() method, we re-added the highlighting. + * Inspired by https://stackoverflow.com/a/16580937 + * + * @method _hookUpdateFromTextArea + * @private + */ + _hookUpdateFromTextArea: function() { + var host = this.get('host'), + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags() + + host.updateFromTextArea = (function() { + var _updateFromTextArea = host.updateFromTextArea; + return function() { + var ret = _updateFromTextArea.apply(this, arguments); + if (multilangplugin._highlight && (this.updateFromTextArea.caller.name === "_showHTML")) { + multilangplugin._highlightMlangTags(); + } + return ret; + } + })(); + }, + /** * Retrieves the selected text, wraps it with the multilang tags, * and replaces the selected text in the editor with with it. @@ -162,7 +232,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att taggedContent, content; - taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; + taggedContent = this._tagTemplate; selection = this._getSelectionHTML(); content = (host.getSelection().toString().length === 0) ? ' ' : selection; @@ -177,7 +247,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Retrieves selected text with its HTML. - * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 + * Taken from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 * * @method _getSelectionHTML * @private @@ -194,8 +264,8 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att selection = window.getSelection(); if (selection.rangeCount) { - container = document.createElement('div'); - for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { + var container = document.createElement('div'); + for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } html = container.innerHTML; @@ -212,232 +282,44 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att /** * Listens to every change of the text cursor in the text area. If the - * cursor is placed within a multilang tag, the whole tag is selected. + * cursor is placed within a highlighted multilang tag, the whole tag is selected. * * @method _checkSelectionChange + * @param {EventFacade} e An event object. * @private */ - _checkSelectionChange: function() { + _checkSelectionChange: function(e) { var host = this.get('host'), - node = host.getSelectionParentNode(), - nodeValue = Y.one(node).get('text'), - isTextNode, - isLangTag; - - isTextNode = Y.one(node).toString().indexOf('#text') > - 1; - isLangTag = (nodeValue.match(/\{mlang/g).length === 1); + node = host.getSelectionParentNode(); - if (isTextNode && isLangTag) { - host.setSelection(host.getSelectionFromNode(Y.one(node))); + // If the event fires without a parent node, ignore the whole thing. + if ((typeof node === 'undefined') || (node === null)) { + return; } - }, - - /** - * Retrieves the inputs of type submit, and, for each element, calls the function - * that sets the submit listener. Is not made in this function because there is - * not any (apparent) way to access class scope from YUI closure. - * - * @method _setSubmitListeners - * @private - */ - _setSubmitListeners: function() { - var submitButtons = Y.all('input[type=submit]'); - - submitButtons.each(this._addListenerToSubmitButtons, this); - }, - - /** - * Adds the clean tags submit listener of each input[type="submit"], but only if - * it's not 'cancel' type, and if its parent form is of 'mform' class, because there - * may be any other submit type (such us administrator's search button). - * - * @method _addListenerToSubmitButtons - * @param {Node} buttonNode - * @private - */ - _addListenerToSubmitButtons: function(buttonNode) { - var buttonObject, - className, - parentFormClassName, - notCancelButton, - notSearchButton; - - buttonObject = document.getElementById(buttonNode.get('id')); - if (buttonObject !== null) { - className = buttonObject.className; - parentFormClassName = buttonObject.form.className; - - notCancelButton = className.match(/btn-cancel/g) === null; - notSearchButton = parentFormClassName.match(/mform/g).length > 0; - - if (notCancelButton && notSearchButton) { - buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); - } + if ((node.parentElement.nodeName === 'SPAN') && + (node.parentElement.getAttributeNode('class').value.indexOf(CLASSES.TAG) !== -1)) { + selection = host.getSelectionFromNode(Y.one(node)); + host.setSelection(selection); + return; } }, /** - * When submit button clicked, this function is invoked. It has to stop the submission, - * in order to process the textarea to clean the tags. - * - * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, - * an then simulates the click, to submit the form. - * - * @method _cleanTagsOnSubmit - * @param {EventFacade} event - * @param {Node} submitButton - * @private - */ - _cleanTagsOnSubmit: function(event, submitButton) { - event.preventDefault(); - - this._cleanTagsWithNoYuiId(); - this._cleanTagsWithYuiId(); - - submitButton.detach('click', this._cleanTagsOnSubmit); - submitButton.simulate('click'); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that do not have "id" attribute. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. - * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method _cleanTagsWithNoYuiId - * @private - */ - _cleanTagsWithNoYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtags, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression; - - regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags === null) { - continue; - } - - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - } - - this.markUpdated(); - }, - - /** - * Cleans the tags around the {mlang} tags when the form is submitted, - * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. - * The cleanup with "id" attribute and without it is made separately, to avoid an evil - * regular expression. - * - * There may be more than one atto editor textarea in the page. So, we have to retrieve - * the textareas by the class name. If there is only one, the object will be only the - * reference, but, if there are more, we will have an array. So, the easiest way is to - * check if what we have is an array, and if it not, create it manually, and iterate it - * later. + * When submitting the form, this function is invoked to clean the highlighting html code. * - * issue #15: the textareas are now retrieved passing to YUI selector the whole element, - * instead of the id string, due to problems with special characters. - * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 - * - * @method anTagsWithYuiId + * @method _cleanMlangTags * @private */ - _cleanTagsWithYuiId: function() { - var textareas = Y.all('.editor_atto_content'), - textarea, - textareaIndex, - innerHTML, - spanedmlangtag, - index, - cleanmlangtag, - regularExpression, - openingspanwithyui, - spanedmlangtagsdwithyui, - mlangtag; - - openingspanwithyui = OPENING_SPAN.replace('', 'g'); - - if (!textareas instanceof Array) { - textarea = textareas; - textareas = []; - textareas[0] = textarea; - } - - for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { - textarea = textareas._nodes[textareaIndex].id; - textarea = Y.one(document.getElementById(textarea)); - - innerHTML = textarea.get('innerHTML'); - - spanedmlangtagsdwithyui = innerHTML.match(regularExpression); - - if (spanedmlangtagsdwithyui === null) { - continue; - } - - for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { - spanedmlangtag = spanedmlangtagsdwithyui[index]; - mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; - - cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); - cleanmlangtag = cleanmlangtag.replace('', ''); - - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); - } - - textarea.set('innerHTML', innerHTML); - + _cleanMlangTags: function() { + if (this._highlight) { + this.editor.setHTML(this._getHTMLwithCleanedTags(this.editor.getHTML())); this.markUpdated(); } }, /** - * Adds the tags to the {mlang} tags when the editor is loaded. - * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, - * I don't know. But, if we call it after setting the HTML, the {mlang} - * tags flicker with the decoration, and returns to their original state. + * Adds the tags to the {mlang} tags if highlighting is enable. * * Instead of taking the HTML directly from the textarea, we have to * retrieve it, first, without the tags that can be stored @@ -447,84 +329,79 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * * Every different {mlang} tag has to be replaced only once, otherwise, * nested s will be created in every repeated replacement. So, we - * have to have a track of which replacements have been made. + * need to track which replacements have been made. * - * @method _decorateTagsOnInit + * @method _highlightMlangTags * @private */ - _decorateTagsOnInit: function() { - var textarea = Y.all('.editor_atto_content'), - innerHTML, + _highlightMlangTags: function() { + var editorHTML, regularExpression, mlangtags, mlangtag, index, - decoratedmlangtag, + highlightedmlangtag, replacementsmade = [], notreplacedyet; - - innerHTML = this._getHTMLwithCleanedTags(); - - regularExpression = new RegExp('{mlang.*?}', 'g'); - mlangtags = innerHTML.match(regularExpression); - - if (mlangtags !== null) { - for (index = 0; index < mlangtags.length; index++) { - mlangtag = mlangtags[index]; - - notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; - - if (notreplacedyet) { - replacementsmade.push(mlangtag); - - decoratedmlangtag = OPENING_SPAN + mlangtag + ''; - regularExpression = new RegExp(mlangtag, 'g'); - - innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); + if (this._highlight){ + editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); + + regularExpression = new RegExp('{mlang.*?}', 'g'); + mlangtags = editorHTML.match(regularExpression); + if (mlangtags !== null) { + for (index = 0; index < mlangtags.length; index++) { + mlangtag = mlangtags[index]; + + notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; + if (notreplacedyet) { + replacementsmade.push(mlangtag); + highlightedmlangtag = OPENING_SPAN + mlangtag + CLOSING_SPAN; + regularExpression = new RegExp(mlangtag, 'g'); + editorHTML = editorHTML.replace(regularExpression, highlightedmlangtag); + } } + + this.editor.setHTML(editorHTML); } - textarea.set('innerHTML', innerHTML); + this.markUpdated(); } - }, /** - * This function returns the HTML as it is in the textarea, but cleaning every + * This function returns the HTML passed in as parameter, but cleaning every multilang * tag around the {mlang} tags. This is necessary for decorating tags on * init, because it could happen that in database are stored the {mlang} tags with * their tags, due to a bug in version 2015120501. * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 + * Implementation based on code from EditorClean._clearSpans() * * @method _getHTMLwithCleanedTags - * @return {string} HTML in textarea, without any around {mlang} tags + * @param {string} content The to be cleaned. + * @return {string} HTML in editor, without any around {mlang} tags. */ - _getHTMLwithCleanedTags: function() { - var host = this.get('host'), - innerHTML = host.getCleanHTML(), - regexString, - regularExpression, - spanedmlangtags, - spanedmlangtag, - cleanmlangtag, - index; - - regexString = OPENING_SPAN + '.*?' + ''; - regularExpression = new RegExp(regexString, 'g'); - spanedmlangtags = innerHTML.match(regularExpression); - - if (spanedmlangtags !== null) { - for (index = 0; index < spanedmlangtags.length; index++) { - spanedmlangtag = spanedmlangtags[index]; - - cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); - cleanmlangtag = cleanmlangtag.replace('', ''); + _getHTMLwithCleanedTags: function(content) { + // This is better to run detached from the DOM, so the browser doesn't try to update on each change. + var holder = document.createElement('div'); + holder.innerHTML = content; + var spans = holder.getElementsByTagName('span'); + + // Since we will be removing elements from the list, we should copy it to an array, making it static. + var spansarr = Array.prototype.slice.call(spans, 0); + + spansarr.forEach(function(span) { + if (span.className.indexOf(CLASSES.TAG) !== -1) { + // Move each child (if they exist) to the parent in place of this span. + while (span.firstChild) { + span.parentNode.insertBefore(span.firstChild, span); + } - innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); + // Remove the now empty span. + span.parentNode.removeChild(span); } - } + }); - return innerHTML; + return holder.innerHTML; } }, { From 63be9948ddd15c4723363a05ce5cf0cbccfb3548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Wed, 4 Oct 2017 01:26:05 +0200 Subject: [PATCH 02/11] Fix eslint errors and warnings --- .../moodle-atto_multilang2-button-debug.js | 33 +++++++++---------- .../moodle-atto_multilang2-button.js | 33 +++++++++---------- yui/src/button/js/button.js | 33 +++++++++---------- 3 files changed, 48 insertions(+), 51 deletions(-) diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js index 84421be..5163b84 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js @@ -39,18 +39,18 @@ var CLASSES = { DEFAULT_LANGUAGE = '{"en":"English (en)"}', DEFAULT_CAPABILITY = true, DEFAULT_HIGHLIGHT = true, - DEFAULT_CSS = 'outline: 1px dotted;' + - 'padding: 0.1em;' + - 'margin: 0em 0.1em;' + - 'background-color: #ffffaa;', - OPENING_SPAN = ''; - CLOSING_SPAN = ''; + DEFAULT_CSS = 'outline: 1px dotted;' + + 'padding: 0.1em;' + + 'margin: 0em 0.1em;' + + 'background-color: #ffffaa;', + OPENING_SPAN = '', + CLOSING_SPAN = '', TEMPLATES = { SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' - }, + }; /** * Atto text editor multilanguage plugin. @@ -181,7 +181,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); } return _updateOriginal.apply(this, arguments); - } + }; })(); }, @@ -208,7 +208,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin._highlightMlangTags(); } return ret; - } + }; })(); }, @@ -256,17 +256,16 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * @return {string} selected text's html; empty if nothing selected */ _getSelectionHTML: function() { - var html = '', - selection, - container, - index, - lenght; + var html = ''; if (typeof window.getSelection !== 'undefined') { - selection = window.getSelection(); + var selection = window.getSelection(), + container, + index, + length; if (selection.rangeCount) { - var container = document.createElement('div'); + container = document.createElement('div'); for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } @@ -345,7 +344,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att highlightedmlangtag, replacementsmade = [], notreplacedyet; - if (this._highlight){ + if (this._highlight) { editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); regularExpression = new RegExp('{mlang.*?}', 'g'); diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js index 84421be..5163b84 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js @@ -39,18 +39,18 @@ var CLASSES = { DEFAULT_LANGUAGE = '{"en":"English (en)"}', DEFAULT_CAPABILITY = true, DEFAULT_HIGHLIGHT = true, - DEFAULT_CSS = 'outline: 1px dotted;' + - 'padding: 0.1em;' + - 'margin: 0em 0.1em;' + - 'background-color: #ffffaa;', - OPENING_SPAN = ''; - CLOSING_SPAN = ''; + DEFAULT_CSS = 'outline: 1px dotted;' + + 'padding: 0.1em;' + + 'margin: 0em 0.1em;' + + 'background-color: #ffffaa;', + OPENING_SPAN = '', + CLOSING_SPAN = '', TEMPLATES = { SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' - }, + }; /** * Atto text editor multilanguage plugin. @@ -181,7 +181,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); } return _updateOriginal.apply(this, arguments); - } + }; })(); }, @@ -208,7 +208,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin._highlightMlangTags(); } return ret; - } + }; })(); }, @@ -256,17 +256,16 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * @return {string} selected text's html; empty if nothing selected */ _getSelectionHTML: function() { - var html = '', - selection, - container, - index, - lenght; + var html = ''; if (typeof window.getSelection !== 'undefined') { - selection = window.getSelection(); + var selection = window.getSelection(), + container, + index, + length; if (selection.rangeCount) { - var container = document.createElement('div'); + container = document.createElement('div'); for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } @@ -345,7 +344,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att highlightedmlangtag, replacementsmade = [], notreplacedyet; - if (this._highlight){ + if (this._highlight) { editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); regularExpression = new RegExp('{mlang.*?}', 'g'); diff --git a/yui/src/button/js/button.js b/yui/src/button/js/button.js index e697af9..be69904 100644 --- a/yui/src/button/js/button.js +++ b/yui/src/button/js/button.js @@ -37,18 +37,18 @@ var CLASSES = { DEFAULT_LANGUAGE = '{"en":"English (en)"}', DEFAULT_CAPABILITY = true, DEFAULT_HIGHLIGHT = true, - DEFAULT_CSS = 'outline: 1px dotted;' + - 'padding: 0.1em;' + - 'margin: 0em 0.1em;' + - 'background-color: #ffffaa;', - OPENING_SPAN = ''; - CLOSING_SPAN = ''; + DEFAULT_CSS = 'outline: 1px dotted;' + + 'padding: 0.1em;' + + 'margin: 0em 0.1em;' + + 'background-color: #ffffaa;', + OPENING_SPAN = '', + CLOSING_SPAN = '', TEMPLATES = { SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' - }, + }; /** * Atto text editor multilanguage plugin. @@ -179,7 +179,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); } return _updateOriginal.apply(this, arguments); - } + }; })(); }, @@ -206,7 +206,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att multilangplugin._highlightMlangTags(); } return ret; - } + }; })(); }, @@ -254,17 +254,16 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * @return {string} selected text's html; empty if nothing selected */ _getSelectionHTML: function() { - var html = '', - selection, - container, - index, - lenght; + var html = ''; if (typeof window.getSelection !== 'undefined') { - selection = window.getSelection(); + var selection = window.getSelection(), + container, + index, + length; if (selection.rangeCount) { - var container = document.createElement('div'); + container = document.createElement('div'); for (index = 0, length = selection.rangeCount; index < length; ++index) { container.appendChild(selection.getRangeAt(index).cloneContents()); } @@ -343,7 +342,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att highlightedmlangtag, replacementsmade = [], notreplacedyet; - if (this._highlight){ + if (this._highlight) { editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML()); regularExpression = new RegExp('{mlang.*?}', 'g'); From 24888c44dd8496b467441ed631b999cc8151f9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Wed, 4 Oct 2017 02:17:43 +0200 Subject: [PATCH 03/11] Fix shifter errors and warnings --- .../moodle-atto_multilang2-button-coverage.js | 6 --- .../moodle-atto_multilang2-button-debug.js | 39 +++++++++++-------- .../moodle-atto_multilang2-button-min.js | 2 +- .../moodle-atto_multilang2-button.js | 39 +++++++++++-------- yui/src/button/js/button.js | 39 +++++++++++-------- 5 files changed, 70 insertions(+), 55 deletions(-) delete mode 100644 yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js deleted file mode 100644 index 8aa9f76..0000000 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } -if (!__coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']) { - __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js'] = {"path":"build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"fnMap":{"1":{"name":"(anonymous_1)","line":1,"loc":{"start":{"line":1,"column":41},"end":{"line":1,"column":60}}},"2":{"name":"(anonymous_2)","line":73,"loc":{"start":{"line":73,"column":17},"end":{"line":73,"column":28}}},"3":{"name":"(anonymous_3)","line":107,"loc":{"start":{"line":107,"column":22},"end":{"line":107,"column":33}}},"4":{"name":"(anonymous_4)","line":126,"loc":{"start":{"line":126,"column":29},"end":{"line":126,"column":40}}},"5":{"name":"(anonymous_5)","line":161,"loc":{"start":{"line":161,"column":14},"end":{"line":161,"column":40}}},"6":{"name":"(anonymous_6)","line":188,"loc":{"start":{"line":188,"column":23},"end":{"line":188,"column":34}}},"7":{"name":"(anonymous_7)","line":222,"loc":{"start":{"line":222,"column":27},"end":{"line":222,"column":38}}},"8":{"name":"(anonymous_8)","line":245,"loc":{"start":{"line":245,"column":25},"end":{"line":245,"column":36}}},"9":{"name":"(anonymous_9)","line":260,"loc":{"start":{"line":260,"column":33},"end":{"line":260,"column":54}}},"10":{"name":"(anonymous_10)","line":294,"loc":{"start":{"line":294,"column":24},"end":{"line":294,"column":54}}},"11":{"name":"(anonymous_11)","line":323,"loc":{"start":{"line":323,"column":27},"end":{"line":323,"column":38}}},"12":{"name":"(anonymous_12)","line":388,"loc":{"start":{"line":388,"column":26},"end":{"line":388,"column":37}}},"13":{"name":"(anonymous_13)","line":457,"loc":{"start":{"line":457,"column":25},"end":{"line":457,"column":36}}},"14":{"name":"(anonymous_14)","line":504,"loc":{"start":{"line":504,"column":29},"end":{"line":504,"column":40}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":573,"column":61}},"2":{"start":{"line":28,"column":0},"end":{"line":52,"column":56}},"3":{"start":{"line":62,"column":0},"end":{"line":570,"column":3}},"4":{"start":{"line":74,"column":8},"end":{"line":75,"column":30}},"5":{"start":{"line":77,"column":8},"end":{"line":98,"column":9}},"6":{"start":{"line":78,"column":12},"end":{"line":78,"column":58}},"7":{"start":{"line":79,"column":12},"end":{"line":79,"column":55}},"8":{"start":{"line":81,"column":12},"end":{"line":88,"column":15}},"9":{"start":{"line":90,"column":12},"end":{"line":90,"column":91}},"10":{"start":{"line":92,"column":12},"end":{"line":92,"column":36}},"11":{"start":{"line":94,"column":12},"end":{"line":97,"column":13}},"12":{"start":{"line":95,"column":16},"end":{"line":95,"column":43}},"13":{"start":{"line":96,"column":16},"end":{"line":96,"column":43}},"14":{"start":{"line":108,"column":8},"end":{"line":109,"column":18}},"15":{"start":{"line":111,"column":8},"end":{"line":111,"column":48}},"16":{"start":{"line":112,"column":8},"end":{"line":112,"column":32}},"17":{"start":{"line":113,"column":8},"end":{"line":113,"column":30}},"18":{"start":{"line":115,"column":8},"end":{"line":115,"column":41}},"19":{"start":{"line":127,"column":8},"end":{"line":129,"column":21}},"20":{"start":{"line":131,"column":8},"end":{"line":131,"column":57}},"21":{"start":{"line":133,"column":8},"end":{"line":140,"column":9}},"22":{"start":{"line":134,"column":12},"end":{"line":139,"column":13}},"23":{"start":{"line":135,"column":16},"end":{"line":138,"column":19}},"24":{"start":{"line":142,"column":8},"end":{"line":142,"column":28}},"25":{"start":{"line":162,"column":8},"end":{"line":165,"column":20}},"26":{"start":{"line":167,"column":8},"end":{"line":167,"column":84}},"27":{"start":{"line":169,"column":8},"end":{"line":169,"column":45}},"28":{"start":{"line":170,"column":8},"end":{"line":170,"column":87}},"29":{"start":{"line":172,"column":8},"end":{"line":172,"column":71}},"30":{"start":{"line":173,"column":8},"end":{"line":173,"column":73}},"31":{"start":{"line":175,"column":8},"end":{"line":175,"column":54}},"32":{"start":{"line":177,"column":8},"end":{"line":177,"column":27}},"33":{"start":{"line":189,"column":8},"end":{"line":193,"column":19}},"34":{"start":{"line":195,"column":8},"end":{"line":210,"column":9}},"35":{"start":{"line":196,"column":12},"end":{"line":196,"column":46}},"36":{"start":{"line":198,"column":12},"end":{"line":204,"column":13}},"37":{"start":{"line":199,"column":16},"end":{"line":199,"column":58}},"38":{"start":{"line":200,"column":16},"end":{"line":202,"column":17}},"39":{"start":{"line":201,"column":20},"end":{"line":201,"column":87}},"40":{"start":{"line":203,"column":16},"end":{"line":203,"column":43}},"41":{"start":{"line":206,"column":15},"end":{"line":210,"column":9}},"42":{"start":{"line":207,"column":12},"end":{"line":209,"column":13}},"43":{"start":{"line":208,"column":16},"end":{"line":208,"column":65}},"44":{"start":{"line":212,"column":8},"end":{"line":212,"column":20}},"45":{"start":{"line":223,"column":8},"end":{"line":227,"column":22}},"46":{"start":{"line":229,"column":8},"end":{"line":229,"column":67}},"47":{"start":{"line":230,"column":8},"end":{"line":230,"column":63}},"48":{"start":{"line":232,"column":8},"end":{"line":234,"column":9}},"49":{"start":{"line":233,"column":12},"end":{"line":233,"column":70}},"50":{"start":{"line":246,"column":8},"end":{"line":246,"column":56}},"51":{"start":{"line":248,"column":8},"end":{"line":248,"column":67}},"52":{"start":{"line":261,"column":8},"end":{"line":265,"column":28}},"53":{"start":{"line":267,"column":8},"end":{"line":267,"column":69}},"54":{"start":{"line":269,"column":8},"end":{"line":279,"column":9}},"55":{"start":{"line":270,"column":12},"end":{"line":270,"column":47}},"56":{"start":{"line":271,"column":12},"end":{"line":271,"column":62}},"57":{"start":{"line":273,"column":12},"end":{"line":273,"column":70}},"58":{"start":{"line":274,"column":12},"end":{"line":274,"column":77}},"59":{"start":{"line":276,"column":12},"end":{"line":278,"column":13}},"60":{"start":{"line":277,"column":16},"end":{"line":277,"column":82}},"61":{"start":{"line":295,"column":8},"end":{"line":295,"column":31}},"62":{"start":{"line":297,"column":8},"end":{"line":297,"column":37}},"63":{"start":{"line":298,"column":8},"end":{"line":298,"column":35}},"64":{"start":{"line":300,"column":8},"end":{"line":300,"column":62}},"65":{"start":{"line":301,"column":8},"end":{"line":301,"column":39}},"66":{"start":{"line":324,"column":8},"end":{"line":332,"column":30}},"67":{"start":{"line":334,"column":8},"end":{"line":334,"column":78}},"68":{"start":{"line":336,"column":8},"end":{"line":340,"column":9}},"69":{"start":{"line":337,"column":12},"end":{"line":337,"column":33}},"70":{"start":{"line":338,"column":12},"end":{"line":338,"column":27}},"71":{"start":{"line":339,"column":12},"end":{"line":339,"column":36}},"72":{"start":{"line":342,"column":8},"end":{"line":364,"column":9}},"73":{"start":{"line":343,"column":12},"end":{"line":343,"column":58}},"74":{"start":{"line":344,"column":12},"end":{"line":344,"column":64}},"75":{"start":{"line":346,"column":12},"end":{"line":346,"column":50}},"76":{"start":{"line":348,"column":12},"end":{"line":348,"column":65}},"77":{"start":{"line":350,"column":12},"end":{"line":352,"column":13}},"78":{"start":{"line":351,"column":16},"end":{"line":351,"column":25}},"79":{"start":{"line":354,"column":12},"end":{"line":361,"column":13}},"80":{"start":{"line":355,"column":16},"end":{"line":355,"column":56}},"81":{"start":{"line":356,"column":16},"end":{"line":356,"column":73}},"82":{"start":{"line":358,"column":16},"end":{"line":358,"column":69}},"83":{"start":{"line":360,"column":16},"end":{"line":360,"column":77}},"84":{"start":{"line":363,"column":12},"end":{"line":363,"column":49}},"85":{"start":{"line":366,"column":8},"end":{"line":366,"column":27}},"86":{"start":{"line":389,"column":8},"end":{"line":399,"column":21}},"87":{"start":{"line":401,"column":8},"end":{"line":401,"column":81}},"88":{"start":{"line":402,"column":8},"end":{"line":402,"column":89}},"89":{"start":{"line":404,"column":8},"end":{"line":408,"column":9}},"90":{"start":{"line":405,"column":12},"end":{"line":405,"column":33}},"91":{"start":{"line":406,"column":12},"end":{"line":406,"column":27}},"92":{"start":{"line":407,"column":12},"end":{"line":407,"column":36}},"93":{"start":{"line":410,"column":8},"end":{"line":435,"column":9}},"94":{"start":{"line":411,"column":12},"end":{"line":411,"column":58}},"95":{"start":{"line":412,"column":12},"end":{"line":412,"column":64}},"96":{"start":{"line":414,"column":12},"end":{"line":414,"column":50}},"97":{"start":{"line":416,"column":12},"end":{"line":416,"column":73}},"98":{"start":{"line":418,"column":12},"end":{"line":420,"column":13}},"99":{"start":{"line":419,"column":16},"end":{"line":419,"column":25}},"100":{"start":{"line":422,"column":12},"end":{"line":430,"column":13}},"101":{"start":{"line":423,"column":16},"end":{"line":423,"column":64}},"102":{"start":{"line":424,"column":16},"end":{"line":424,"column":68}},"103":{"start":{"line":426,"column":16},"end":{"line":426,"column":84}},"104":{"start":{"line":427,"column":16},"end":{"line":427,"column":69}},"105":{"start":{"line":429,"column":16},"end":{"line":429,"column":77}},"106":{"start":{"line":432,"column":12},"end":{"line":432,"column":49}},"107":{"start":{"line":434,"column":12},"end":{"line":434,"column":31}},"108":{"start":{"line":458,"column":8},"end":{"line":466,"column":27}},"109":{"start":{"line":468,"column":8},"end":{"line":468,"column":51}},"110":{"start":{"line":470,"column":8},"end":{"line":470,"column":58}},"111":{"start":{"line":471,"column":8},"end":{"line":471,"column":55}},"112":{"start":{"line":473,"column":8},"end":{"line":490,"column":9}},"113":{"start":{"line":474,"column":12},"end":{"line":487,"column":13}},"114":{"start":{"line":475,"column":16},"end":{"line":475,"column":44}},"115":{"start":{"line":477,"column":16},"end":{"line":477,"column":75}},"116":{"start":{"line":479,"column":16},"end":{"line":486,"column":17}},"117":{"start":{"line":480,"column":20},"end":{"line":480,"column":52}},"118":{"start":{"line":482,"column":20},"end":{"line":482,"column":76}},"119":{"start":{"line":483,"column":20},"end":{"line":483,"column":66}},"120":{"start":{"line":485,"column":20},"end":{"line":485,"column":88}},"121":{"start":{"line":489,"column":12},"end":{"line":489,"column":49}},"122":{"start":{"line":505,"column":8},"end":{"line":512,"column":18}},"123":{"start":{"line":514,"column":8},"end":{"line":514,"column":55}},"124":{"start":{"line":515,"column":8},"end":{"line":515,"column":57}},"125":{"start":{"line":516,"column":8},"end":{"line":516,"column":61}},"126":{"start":{"line":518,"column":8},"end":{"line":527,"column":9}},"127":{"start":{"line":519,"column":12},"end":{"line":526,"column":13}},"128":{"start":{"line":520,"column":16},"end":{"line":520,"column":56}},"129":{"start":{"line":522,"column":16},"end":{"line":522,"column":73}},"130":{"start":{"line":523,"column":16},"end":{"line":523,"column":69}},"131":{"start":{"line":525,"column":16},"end":{"line":525,"column":77}},"132":{"start":{"line":529,"column":8},"end":{"line":529,"column":25}}},"branchMap":{"1":{"line":77,"type":"if","locations":[{"start":{"line":77,"column":8},"end":{"line":77,"column":8}},{"start":{"line":77,"column":8},"end":{"line":77,"column":8}}]},"2":{"line":94,"type":"if","locations":[{"start":{"line":94,"column":12},"end":{"line":94,"column":12}},{"start":{"line":94,"column":12},"end":{"line":94,"column":12}}]},"3":{"line":134,"type":"if","locations":[{"start":{"line":134,"column":12},"end":{"line":134,"column":12}},{"start":{"line":134,"column":12},"end":{"line":134,"column":12}}]},"4":{"line":167,"type":"cond-expr","locations":[{"start":{"line":167,"column":44},"end":{"line":167,"column":60}},{"start":{"line":167,"column":63},"end":{"line":167,"column":83}}]},"5":{"line":170,"type":"cond-expr","locations":[{"start":{"line":170,"column":66},"end":{"line":170,"column":74}},{"start":{"line":170,"column":77},"end":{"line":170,"column":86}}]},"6":{"line":195,"type":"if","locations":[{"start":{"line":195,"column":8},"end":{"line":195,"column":8}},{"start":{"line":195,"column":8},"end":{"line":195,"column":8}}]},"7":{"line":198,"type":"if","locations":[{"start":{"line":198,"column":12},"end":{"line":198,"column":12}},{"start":{"line":198,"column":12},"end":{"line":198,"column":12}}]},"8":{"line":206,"type":"if","locations":[{"start":{"line":206,"column":15},"end":{"line":206,"column":15}},{"start":{"line":206,"column":15},"end":{"line":206,"column":15}}]},"9":{"line":207,"type":"if","locations":[{"start":{"line":207,"column":12},"end":{"line":207,"column":12}},{"start":{"line":207,"column":12},"end":{"line":207,"column":12}}]},"10":{"line":232,"type":"if","locations":[{"start":{"line":232,"column":8},"end":{"line":232,"column":8}},{"start":{"line":232,"column":8},"end":{"line":232,"column":8}}]},"11":{"line":232,"type":"binary-expr","locations":[{"start":{"line":232,"column":12},"end":{"line":232,"column":22}},{"start":{"line":232,"column":26},"end":{"line":232,"column":35}}]},"12":{"line":269,"type":"if","locations":[{"start":{"line":269,"column":8},"end":{"line":269,"column":8}},{"start":{"line":269,"column":8},"end":{"line":269,"column":8}}]},"13":{"line":276,"type":"if","locations":[{"start":{"line":276,"column":12},"end":{"line":276,"column":12}},{"start":{"line":276,"column":12},"end":{"line":276,"column":12}}]},"14":{"line":276,"type":"binary-expr","locations":[{"start":{"line":276,"column":16},"end":{"line":276,"column":31}},{"start":{"line":276,"column":35},"end":{"line":276,"column":50}}]},"15":{"line":336,"type":"if","locations":[{"start":{"line":336,"column":8},"end":{"line":336,"column":8}},{"start":{"line":336,"column":8},"end":{"line":336,"column":8}}]},"16":{"line":350,"type":"if","locations":[{"start":{"line":350,"column":12},"end":{"line":350,"column":12}},{"start":{"line":350,"column":12},"end":{"line":350,"column":12}}]},"17":{"line":404,"type":"if","locations":[{"start":{"line":404,"column":8},"end":{"line":404,"column":8}},{"start":{"line":404,"column":8},"end":{"line":404,"column":8}}]},"18":{"line":418,"type":"if","locations":[{"start":{"line":418,"column":12},"end":{"line":418,"column":12}},{"start":{"line":418,"column":12},"end":{"line":418,"column":12}}]},"19":{"line":473,"type":"if","locations":[{"start":{"line":473,"column":8},"end":{"line":473,"column":8}},{"start":{"line":473,"column":8},"end":{"line":473,"column":8}}]},"20":{"line":479,"type":"if","locations":[{"start":{"line":479,"column":16},"end":{"line":479,"column":16}},{"start":{"line":479,"column":16},"end":{"line":479,"column":16}}]},"21":{"line":518,"type":"if","locations":[{"start":{"line":518,"column":8},"end":{"line":518,"column":8}},{"start":{"line":518,"column":8},"end":{"line":518,"column":8}}]}},"code":["(function () { YUI.add('moodle-atto_multilang2-button', function (Y, NAME) {","","// This file is part of Moodle - http://moodle.org/","//","// Moodle is free software: you can redistribute it and/or modify","// it under the terms of the GNU General Public License as published by","// the Free Software Foundation, either version 3 of the License, or","// (at your option) any later version.","//","// Moodle is distributed in the hope that it will be useful,","// but WITHOUT ANY WARRANTY; without even the implied warranty of","// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the","// GNU General Public License for more details.","//","// You should have received a copy of the GNU General Public License","// along with Moodle. If not, see .","","/**"," * @package atto_multilang2"," * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea"," * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later"," */","","/**"," * @module moodle-atto_multilang2-button"," */","","var CLASSES = {"," TAG: 'filter-multilang-tag'"," },",""," LANG_WILDCARD = '%lang',"," CONTENT_WILDCARD = '%content',"," ATTR_LANGUAGES = 'languages',"," ATTR_CAPABILITY = 'capability',"," ATTR_HIGHLIGHT = 'highlight',"," ATTR_CSS = 'css',"," DEFAULT_LANGUAGE = '{\"en\":\"English (en)\"}',"," DEFAULT_CAPABILITY = true,"," DEFAULT_HIGHLIGHT = true,"," DEFAULT_CSS = 'outline: 1px dotted;' +"," 'padding: 0.1em;' +"," 'margin: 0em 0.1em;' +"," 'background-color: #ffffaa;',"," TEMPLATES = {"," SPANED: ' {mlang ' + LANG_WILDCARD + '}' +"," CONTENT_WILDCARD +"," '{mlang} ',",""," NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}'"," },"," OPENING_SPAN = '';","","/**"," * Atto text editor multilanguage plugin."," *"," * @namespace M.atto_multilang2"," * @class button"," * @extends M.editor_atto.EditorPlugin"," */","","Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {",""," /**"," * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php."," *"," * @property _highlight"," * @type boolean"," * @private"," */"," _highlight: true,",""," initializer: function() {"," var hascapability = this.get(ATTR_CAPABILITY),"," toolbarItems = [];",""," if (hascapability) {"," toolbarItems = this._initializeToolbarItems();"," this._highlight = this.get(ATTR_HIGHLIGHT);",""," this.addToolbarMenu({"," globalItemConfig: {"," callback: this._addTags"," },"," icon: 'icon',"," iconComponent: 'atto_multilang2',"," items: toolbarItems"," });",""," this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this);",""," this._addDelimiterCss();",""," if (this._highlight) {"," this._decorateTagsOnInit();"," this._setSubmitListeners();"," }"," }"," },",""," /**"," * Adds the CSS rules for the delimiters, received as parameter from lib.php."," *"," * @method _addDelimiterCss"," * @private"," */"," _addDelimiterCss: function() {"," var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}',"," style;",""," style = document.createElement('style');"," style.type = 'text/css';"," style.innerHTML = css;",""," document.head.appendChild(style);"," },",""," /**"," * Initializes the toolbar items, which will be the installed languages,"," * received as parameter."," *"," * @method _initializeToolbarItems"," * @private"," * @return {Array} installed language strings"," */"," _initializeToolbarItems: function() {"," var toolbarItems = [],"," languages,"," langCode;",""," languages = JSON.parse(this.get(ATTR_LANGUAGES));",""," for (langCode in languages) {"," if (languages.hasOwnProperty(langCode)) {"," toolbarItems.push({"," text: languages[langCode],"," callbackArgs: langCode"," });"," }"," }",""," return toolbarItems;"," },",""," /**"," * Retrieves the selected text, wraps it with the multilang tags,"," * and replaces the selected text in the editor with with it."," *"," * If the 'highlight' setting is checked, the {mlang} will be wrapped between"," * the tags with the class for the CSS highlight; if not, they will not"," * be wrapped."," *"," * If there is no content selected, a \" \" will be inserted; otherwhise,"," * it's impossible to place the cursor inside the {mlang} tags."," *"," * @method _addTags"," * @param {EventFacade} event"," * @param {string} langCode the language code"," * @private"," */"," _addTags: function(event, langCode) {"," var selection,"," host = this.get('host'),"," taggedContent,"," content;",""," taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED;",""," selection = this._getSelectionHTML();"," content = (host.getSelection().toString().length === 0) ? ' ' : selection;",""," taggedContent = taggedContent.replace(LANG_WILDCARD, langCode);"," taggedContent = taggedContent.replace(CONTENT_WILDCARD, content);",""," host.insertContentAtFocusPoint(taggedContent);",""," this.markUpdated();"," },",""," /**"," * Retrieves selected text with its HTML."," * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234"," *"," * @method _getSelectionHTML"," * @private"," * @return {string} selected text's html; empty if nothing selected"," */"," _getSelectionHTML: function() {"," var html = '',"," selection,"," container,"," index,"," lenght;",""," if (typeof window.getSelection !== 'undefined') {"," selection = window.getSelection();",""," if (selection.rangeCount) {"," container = document.createElement('div');"," for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) {"," container.appendChild(selection.getRangeAt(index).cloneContents());"," }"," html = container.innerHTML;"," }",""," } else if (typeof document.selection !== 'undefined') {"," if (document.selection.type === 'Text') {"," html = document.selection.createRange().htmlText;"," }"," }",""," return html;"," },",""," /**"," * Listens to every change of the text cursor in the text area. If the"," * cursor is placed within a multilang tag, the whole tag is selected."," *"," * @method _checkSelectionChange"," * @private"," */"," _checkSelectionChange: function() {"," var host = this.get('host'),"," node = host.getSelectionParentNode(),"," nodeValue = Y.one(node).get('text'),"," isTextNode,"," isLangTag;",""," isTextNode = Y.one(node).toString().indexOf('#text') > - 1;"," isLangTag = (nodeValue.match(/\\{mlang/g).length === 1);",""," if (isTextNode && isLangTag) {"," host.setSelection(host.getSelectionFromNode(Y.one(node)));"," }"," },",""," /**"," * Retrieves the inputs of type submit, and, for each element, calls the function"," * that sets the submit listener. Is not made in this function because there is"," * not any (apparent) way to access class scope from YUI closure."," *"," * @method _setSubmitListeners"," * @private"," */"," _setSubmitListeners: function() {"," var submitButtons = Y.all('input[type=submit]');",""," submitButtons.each(this._addListenerToSubmitButtons, this);"," },",""," /**"," * Adds the clean tags submit listener of each input[type=\"submit\"], but only if"," * it's not 'cancel' type, and if its parent form is of 'mform' class, because there"," * may be any other submit type (such us administrator's search button)."," *"," * @method _addListenerToSubmitButtons"," * @param {Node} buttonNode"," * @private"," */"," _addListenerToSubmitButtons: function(buttonNode) {"," var buttonObject,"," className,"," parentFormClassName,"," notCancelButton,"," notSearchButton;",""," buttonObject = document.getElementById(buttonNode.get('id'));",""," if (buttonObject !== null) {"," className = buttonObject.className;"," parentFormClassName = buttonObject.form.className;",""," notCancelButton = className.match(/btn-cancel/g) === null;"," notSearchButton = parentFormClassName.match(/mform/g).length > 0;",""," if (notCancelButton && notSearchButton) {"," buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode);"," }"," }"," },",""," /**"," * When submit button clicked, this function is invoked. It has to stop the submission,"," * in order to process the textarea to clean the tags."," *"," * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default,"," * an then simulates the click, to submit the form."," *"," * @method _cleanTagsOnSubmit"," * @param {EventFacade} event"," * @param {Node} submitButton"," * @private"," */"," _cleanTagsOnSubmit: function(event, submitButton) {"," event.preventDefault();",""," this._cleanTagsWithNoYuiId();"," this._cleanTagsWithYuiId();",""," submitButton.detach('click', this._cleanTagsOnSubmit);"," submitButton.simulate('click');"," },",""," /**"," * Cleans the tags around the {mlang} tags when the form is submitted,"," * that do not have \"id\" attribute."," * The cleanup with \"id\" attribute and without it is made separately, to avoid an evil"," * regular expression."," *"," * There may be more than one atto editor textarea in the page. So, we have to retrieve"," * the textareas by the class name. If there is only one, the object will be only the"," * reference, but, if there are more, we will have an array. So, the easiest way is to"," * check if what we have is an array, and if it not, create it manually, and iterate it"," * later."," *"," * issue #15: the textareas are now retrieved passing to YUI selector the whole element,"," * instead of the id string, due to problems with special characters."," * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217"," *"," * @method _cleanTagsWithNoYuiId"," * @private"," */"," _cleanTagsWithNoYuiId: function() {"," var textareas = Y.all('.editor_atto_content'),"," textarea,"," textareaIndex,"," innerHTML,"," spanedmlangtags,"," spanedmlangtag,"," index,"," cleanmlangtag,"," regularExpression;",""," regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g');",""," if (!textareas instanceof Array) {"," textarea = textareas;"," textareas = [];"," textareas[0] = textarea;"," }",""," for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) {"," textarea = textareas._nodes[textareaIndex].id;"," textarea = Y.one(document.getElementById(textarea));",""," innerHTML = textarea.get('innerHTML');",""," spanedmlangtags = innerHTML.match(regularExpression);",""," if (spanedmlangtags === null) {"," continue;"," }"," "," for (index = 0; index < spanedmlangtags.length; index++) {"," spanedmlangtag = spanedmlangtags[index];"," cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, '');",""," cleanmlangtag = cleanmlangtag.replace('', '');",""," innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag);"," }",""," textarea.set('innerHTML', innerHTML);"," }",""," this.markUpdated();"," },",""," /**"," * Cleans the tags around the {mlang} tags when the form is submitted,"," * that have \"id\" attribute, generated by YUI, when the cursor is placed on the tags."," * The cleanup with \"id\" attribute and without it is made separately, to avoid an evil"," * regular expression."," *"," * There may be more than one atto editor textarea in the page. So, we have to retrieve"," * the textareas by the class name. If there is only one, the object will be only the"," * reference, but, if there are more, we will have an array. So, the easiest way is to"," * check if what we have is an array, and if it not, create it manually, and iterate it"," * later."," *"," * issue #15: the textareas are now retrieved passing to YUI selector the whole element,"," * instead of the id string, due to problems with special characters."," * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217"," *"," * @method anTagsWithYuiId"," * @private"," */"," _cleanTagsWithYuiId: function() {"," var textareas = Y.all('.editor_atto_content'),"," textarea,"," textareaIndex,"," innerHTML,"," spanedmlangtag,"," index,"," cleanmlangtag,"," regularExpression,"," openingspanwithyui,"," spanedmlangtagsdwithyui,"," mlangtag;",""," openingspanwithyui = OPENING_SPAN.replace('', 'g');",""," if (!textareas instanceof Array) {"," textarea = textareas;"," textareas = [];"," textareas[0] = textarea;"," }"," "," for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) {"," textarea = textareas._nodes[textareaIndex].id;"," textarea = Y.one(document.getElementById(textarea));",""," innerHTML = textarea.get('innerHTML');",""," spanedmlangtagsdwithyui = innerHTML.match(regularExpression);",""," if (spanedmlangtagsdwithyui === null) {"," continue;"," }"," "," for (index = 0; index < spanedmlangtagsdwithyui.length; index++) {"," spanedmlangtag = spanedmlangtagsdwithyui[index];"," mlangtag = spanedmlangtag.match(/\\{mlang.*?\\}/g)[0];",""," cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag);"," cleanmlangtag = cleanmlangtag.replace('', '');",""," innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag);"," }",""," textarea.set('innerHTML', innerHTML);",""," this.markUpdated();"," }"," },",""," /**"," * Adds the tags to the {mlang} tags when the editor is loaded."," * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly,"," * I don't know. But, if we call it after setting the HTML, the {mlang}"," * tags flicker with the decoration, and returns to their original state."," *"," * Instead of taking the HTML directly from the textarea, we have to"," * retrieve it, first, without the tags that can be stored"," * in database, due to a bug in version 2015120501 that stores the"," * {mlang} tags in database, with the tags."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," *"," * Every different {mlang} tag has to be replaced only once, otherwise,"," * nested s will be created in every repeated replacement. So, we"," * have to have a track of which replacements have been made."," *"," * @method _decorateTagsOnInit"," * @private"," */"," _decorateTagsOnInit: function() {"," var textarea = Y.all('.editor_atto_content'),"," innerHTML,"," regularExpression,"," mlangtags,"," mlangtag,"," index,"," decoratedmlangtag,"," replacementsmade = [],"," notreplacedyet;",""," innerHTML = this._getHTMLwithCleanedTags();",""," regularExpression = new RegExp('{mlang.*?}', 'g');"," mlangtags = innerHTML.match(regularExpression);",""," if (mlangtags !== null) {"," for (index = 0; index < mlangtags.length; index++) {"," mlangtag = mlangtags[index];",""," notreplacedyet = replacementsmade.indexOf(mlangtag) === -1;",""," if (notreplacedyet) {"," replacementsmade.push(mlangtag);",""," decoratedmlangtag = OPENING_SPAN + mlangtag + '';"," regularExpression = new RegExp(mlangtag, 'g');",""," innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag);"," }"," }",""," textarea.set('innerHTML', innerHTML);"," }",""," },",""," /**"," * This function returns the HTML as it is in the textarea, but cleaning every"," * tag around the {mlang} tags. This is necessary for decorating tags on"," * init, because it could happen that in database are stored the {mlang} tags with"," * their tags, due to a bug in version 2015120501."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," *"," * @method _getHTMLwithCleanedTags"," * @return {string} HTML in textarea, without any around {mlang} tags"," */"," _getHTMLwithCleanedTags: function() {"," var host = this.get('host'),"," innerHTML = host.getCleanHTML(),"," regexString,"," regularExpression,"," spanedmlangtags,"," spanedmlangtag,"," cleanmlangtag,"," index;",""," regexString = OPENING_SPAN + '.*?' + '';"," regularExpression = new RegExp(regexString, 'g');"," spanedmlangtags = innerHTML.match(regularExpression);",""," if (spanedmlangtags !== null) {"," for (index = 0; index < spanedmlangtags.length; index++) {"," spanedmlangtag = spanedmlangtags[index];",""," cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, '');"," cleanmlangtag = cleanmlangtag.replace('', '');",""," innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag);"," }"," }",""," return innerHTML;"," }","","}, {"," ATTRS: {"," /**"," * The list of installed languages."," *"," * @attribute languages"," * @type array"," * @default {\"en\":\"English (en)\"}"," */"," languages: DEFAULT_LANGUAGE,",""," /**"," * If the current user has the capability to use the plugin."," *"," * @attribute capability"," * @type boolean"," * @default true"," */"," capability: DEFAULT_CAPABILITY,",""," /**"," * If the {mlang} tags have to be highlighted or not."," *"," * @property highlight"," * @type boolean"," * @default true"," */"," highlight: DEFAULT_HIGHLIGHT,",""," /**"," * The CSS for delimiters."," *"," * @property css"," * @type string"," * @default DEFAULT_CSS"," */"," css: DEFAULT_CSS"," }","});","","","}, '@VERSION@', {\"requires\": [\"moodle-editor_atto-plugin\"]});","","}());"]}; -} -var __cov_xlcucJyua6odXhLRctX6xg = __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']; -__cov_xlcucJyua6odXhLRctX6xg.s['1']++;YUI.add('moodle-atto_multilang2-button',function(Y,NAME){__cov_xlcucJyua6odXhLRctX6xg.f['1']++;__cov_xlcucJyua6odXhLRctX6xg.s['2']++;var CLASSES={TAG:'filter-multilang-tag'},LANG_WILDCARD='%lang',CONTENT_WILDCARD='%content',ATTR_LANGUAGES='languages',ATTR_CAPABILITY='capability',ATTR_HIGHLIGHT='highlight',ATTR_CSS='css',DEFAULT_LANGUAGE='{"en":"English (en)"}',DEFAULT_CAPABILITY=true,DEFAULT_HIGHLIGHT=true,DEFAULT_CSS='outline: 1px dotted;'+'padding: 0.1em;'+'margin: 0em 0.1em;'+'background-color: #ffffaa;',TEMPLATES={SPANED:' {mlang '+LANG_WILDCARD+'}'+CONTENT_WILDCARD+'{mlang} ',NOT_SPANED:'{mlang '+LANG_WILDCARD+'}'+CONTENT_WILDCARD+'{mlang}'},OPENING_SPAN='';__cov_xlcucJyua6odXhLRctX6xg.s['3']++;Y.namespace('M.atto_multilang2').Button=Y.Base.create('button',Y.M.editor_atto.EditorPlugin,[],{_highlight:true,initializer:function(){__cov_xlcucJyua6odXhLRctX6xg.f['2']++;__cov_xlcucJyua6odXhLRctX6xg.s['4']++;var hascapability=this.get(ATTR_CAPABILITY),toolbarItems=[];__cov_xlcucJyua6odXhLRctX6xg.s['5']++;if(hascapability){__cov_xlcucJyua6odXhLRctX6xg.b['1'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['6']++;toolbarItems=this._initializeToolbarItems();__cov_xlcucJyua6odXhLRctX6xg.s['7']++;this._highlight=this.get(ATTR_HIGHLIGHT);__cov_xlcucJyua6odXhLRctX6xg.s['8']++;this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:'icon',iconComponent:'atto_multilang2',items:toolbarItems});__cov_xlcucJyua6odXhLRctX6xg.s['9']++;this.get('host').on('atto:selectionchanged',this._checkSelectionChange,this);__cov_xlcucJyua6odXhLRctX6xg.s['10']++;this._addDelimiterCss();__cov_xlcucJyua6odXhLRctX6xg.s['11']++;if(this._highlight){__cov_xlcucJyua6odXhLRctX6xg.b['2'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['12']++;this._decorateTagsOnInit();__cov_xlcucJyua6odXhLRctX6xg.s['13']++;this._setSubmitListeners();}else{__cov_xlcucJyua6odXhLRctX6xg.b['2'][1]++;}}else{__cov_xlcucJyua6odXhLRctX6xg.b['1'][1]++;}},_addDelimiterCss:function(){__cov_xlcucJyua6odXhLRctX6xg.f['3']++;__cov_xlcucJyua6odXhLRctX6xg.s['14']++;var css='.'+CLASSES.TAG+'{'+this.get(ATTR_CSS)+'}',style;__cov_xlcucJyua6odXhLRctX6xg.s['15']++;style=document.createElement('style');__cov_xlcucJyua6odXhLRctX6xg.s['16']++;style.type='text/css';__cov_xlcucJyua6odXhLRctX6xg.s['17']++;style.innerHTML=css;__cov_xlcucJyua6odXhLRctX6xg.s['18']++;document.head.appendChild(style);},_initializeToolbarItems:function(){__cov_xlcucJyua6odXhLRctX6xg.f['4']++;__cov_xlcucJyua6odXhLRctX6xg.s['19']++;var toolbarItems=[],languages,langCode;__cov_xlcucJyua6odXhLRctX6xg.s['20']++;languages=JSON.parse(this.get(ATTR_LANGUAGES));__cov_xlcucJyua6odXhLRctX6xg.s['21']++;for(langCode in languages){__cov_xlcucJyua6odXhLRctX6xg.s['22']++;if(languages.hasOwnProperty(langCode)){__cov_xlcucJyua6odXhLRctX6xg.b['3'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['23']++;toolbarItems.push({text:languages[langCode],callbackArgs:langCode});}else{__cov_xlcucJyua6odXhLRctX6xg.b['3'][1]++;}}__cov_xlcucJyua6odXhLRctX6xg.s['24']++;return toolbarItems;},_addTags:function(event,langCode){__cov_xlcucJyua6odXhLRctX6xg.f['5']++;__cov_xlcucJyua6odXhLRctX6xg.s['25']++;var selection,host=this.get('host'),taggedContent,content;__cov_xlcucJyua6odXhLRctX6xg.s['26']++;taggedContent=this._highlight?(__cov_xlcucJyua6odXhLRctX6xg.b['4'][0]++,TEMPLATES.SPANED):(__cov_xlcucJyua6odXhLRctX6xg.b['4'][1]++,TEMPLATES.NOT_SPANED);__cov_xlcucJyua6odXhLRctX6xg.s['27']++;selection=this._getSelectionHTML();__cov_xlcucJyua6odXhLRctX6xg.s['28']++;content=host.getSelection().toString().length===0?(__cov_xlcucJyua6odXhLRctX6xg.b['5'][0]++,' '):(__cov_xlcucJyua6odXhLRctX6xg.b['5'][1]++,selection);__cov_xlcucJyua6odXhLRctX6xg.s['29']++;taggedContent=taggedContent.replace(LANG_WILDCARD,langCode);__cov_xlcucJyua6odXhLRctX6xg.s['30']++;taggedContent=taggedContent.replace(CONTENT_WILDCARD,content);__cov_xlcucJyua6odXhLRctX6xg.s['31']++;host.insertContentAtFocusPoint(taggedContent);__cov_xlcucJyua6odXhLRctX6xg.s['32']++;this.markUpdated();},_getSelectionHTML:function(){__cov_xlcucJyua6odXhLRctX6xg.f['6']++;__cov_xlcucJyua6odXhLRctX6xg.s['33']++;var html='',selection,container,index,lenght;__cov_xlcucJyua6odXhLRctX6xg.s['34']++;if(typeof window.getSelection!=='undefined'){__cov_xlcucJyua6odXhLRctX6xg.b['6'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['35']++;selection=window.getSelection();__cov_xlcucJyua6odXhLRctX6xg.s['36']++;if(selection.rangeCount){__cov_xlcucJyua6odXhLRctX6xg.b['7'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['37']++;container=document.createElement('div');__cov_xlcucJyua6odXhLRctX6xg.s['38']++;for(index=0,lenght=selection.rangeCount;index-1;__cov_xlcucJyua6odXhLRctX6xg.s['47']++;isLangTag=nodeValue.match(/\{mlang/g).length===1;__cov_xlcucJyua6odXhLRctX6xg.s['48']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['11'][0]++,isTextNode)&&(__cov_xlcucJyua6odXhLRctX6xg.b['11'][1]++,isLangTag)){__cov_xlcucJyua6odXhLRctX6xg.b['10'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['49']++;host.setSelection(host.getSelectionFromNode(Y.one(node)));}else{__cov_xlcucJyua6odXhLRctX6xg.b['10'][1]++;}},_setSubmitListeners:function(){__cov_xlcucJyua6odXhLRctX6xg.f['8']++;__cov_xlcucJyua6odXhLRctX6xg.s['50']++;var submitButtons=Y.all('input[type=submit]');__cov_xlcucJyua6odXhLRctX6xg.s['51']++;submitButtons.each(this._addListenerToSubmitButtons,this);},_addListenerToSubmitButtons:function(buttonNode){__cov_xlcucJyua6odXhLRctX6xg.f['9']++;__cov_xlcucJyua6odXhLRctX6xg.s['52']++;var buttonObject,className,parentFormClassName,notCancelButton,notSearchButton;__cov_xlcucJyua6odXhLRctX6xg.s['53']++;buttonObject=document.getElementById(buttonNode.get('id'));__cov_xlcucJyua6odXhLRctX6xg.s['54']++;if(buttonObject!==null){__cov_xlcucJyua6odXhLRctX6xg.b['12'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['55']++;className=buttonObject.className;__cov_xlcucJyua6odXhLRctX6xg.s['56']++;parentFormClassName=buttonObject.form.className;__cov_xlcucJyua6odXhLRctX6xg.s['57']++;notCancelButton=className.match(/btn-cancel/g)===null;__cov_xlcucJyua6odXhLRctX6xg.s['58']++;notSearchButton=parentFormClassName.match(/mform/g).length>0;__cov_xlcucJyua6odXhLRctX6xg.s['59']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['14'][0]++,notCancelButton)&&(__cov_xlcucJyua6odXhLRctX6xg.b['14'][1]++,notSearchButton)){__cov_xlcucJyua6odXhLRctX6xg.b['13'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['60']++;buttonNode.on('click',this._cleanTagsOnSubmit,this,buttonNode);}else{__cov_xlcucJyua6odXhLRctX6xg.b['13'][1]++;}}else{__cov_xlcucJyua6odXhLRctX6xg.b['12'][1]++;}},_cleanTagsOnSubmit:function(event,submitButton){__cov_xlcucJyua6odXhLRctX6xg.f['10']++;__cov_xlcucJyua6odXhLRctX6xg.s['61']++;event.preventDefault();__cov_xlcucJyua6odXhLRctX6xg.s['62']++;this._cleanTagsWithNoYuiId();__cov_xlcucJyua6odXhLRctX6xg.s['63']++;this._cleanTagsWithYuiId();__cov_xlcucJyua6odXhLRctX6xg.s['64']++;submitButton.detach('click',this._cleanTagsOnSubmit);__cov_xlcucJyua6odXhLRctX6xg.s['65']++;submitButton.simulate('click');},_cleanTagsWithNoYuiId:function(){__cov_xlcucJyua6odXhLRctX6xg.f['11']++;__cov_xlcucJyua6odXhLRctX6xg.s['66']++;var textareas=Y.all('.editor_atto_content'),textarea,textareaIndex,innerHTML,spanedmlangtags,spanedmlangtag,index,cleanmlangtag,regularExpression;__cov_xlcucJyua6odXhLRctX6xg.s['67']++;regularExpression=new RegExp(OPENING_SPAN+'.*?'+'','g');__cov_xlcucJyua6odXhLRctX6xg.s['68']++;if(!textareas instanceof Array){__cov_xlcucJyua6odXhLRctX6xg.b['15'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['69']++;textarea=textareas;__cov_xlcucJyua6odXhLRctX6xg.s['70']++;textareas=[];__cov_xlcucJyua6odXhLRctX6xg.s['71']++;textareas[0]=textarea;}else{__cov_xlcucJyua6odXhLRctX6xg.b['15'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['72']++;for(textareaIndex=0;textareaIndex','');__cov_xlcucJyua6odXhLRctX6xg.s['83']++;innerHTML=innerHTML.replace(spanedmlangtag,cleanmlangtag);}__cov_xlcucJyua6odXhLRctX6xg.s['84']++;textarea.set('innerHTML',innerHTML);}__cov_xlcucJyua6odXhLRctX6xg.s['85']++;this.markUpdated();},_cleanTagsWithYuiId:function(){__cov_xlcucJyua6odXhLRctX6xg.f['12']++;__cov_xlcucJyua6odXhLRctX6xg.s['86']++;var textareas=Y.all('.editor_atto_content'),textarea,textareaIndex,innerHTML,spanedmlangtag,index,cleanmlangtag,regularExpression,openingspanwithyui,spanedmlangtagsdwithyui,mlangtag;__cov_xlcucJyua6odXhLRctX6xg.s['87']++;openingspanwithyui=OPENING_SPAN.replace('','g');__cov_xlcucJyua6odXhLRctX6xg.s['89']++;if(!textareas instanceof Array){__cov_xlcucJyua6odXhLRctX6xg.b['17'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['90']++;textarea=textareas;__cov_xlcucJyua6odXhLRctX6xg.s['91']++;textareas=[];__cov_xlcucJyua6odXhLRctX6xg.s['92']++;textareas[0]=textarea;}else{__cov_xlcucJyua6odXhLRctX6xg.b['17'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['93']++;for(textareaIndex=0;textareaIndex','');__cov_xlcucJyua6odXhLRctX6xg.s['105']++;innerHTML=innerHTML.replace(spanedmlangtag,cleanmlangtag);}__cov_xlcucJyua6odXhLRctX6xg.s['106']++;textarea.set('innerHTML',innerHTML);__cov_xlcucJyua6odXhLRctX6xg.s['107']++;this.markUpdated();}},_decorateTagsOnInit:function(){__cov_xlcucJyua6odXhLRctX6xg.f['13']++;__cov_xlcucJyua6odXhLRctX6xg.s['108']++;var textarea=Y.all('.editor_atto_content'),innerHTML,regularExpression,mlangtags,mlangtag,index,decoratedmlangtag,replacementsmade=[],notreplacedyet;__cov_xlcucJyua6odXhLRctX6xg.s['109']++;innerHTML=this._getHTMLwithCleanedTags();__cov_xlcucJyua6odXhLRctX6xg.s['110']++;regularExpression=new RegExp('{mlang.*?}','g');__cov_xlcucJyua6odXhLRctX6xg.s['111']++;mlangtags=innerHTML.match(regularExpression);__cov_xlcucJyua6odXhLRctX6xg.s['112']++;if(mlangtags!==null){__cov_xlcucJyua6odXhLRctX6xg.b['19'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['113']++;for(index=0;index';__cov_xlcucJyua6odXhLRctX6xg.s['119']++;regularExpression=new RegExp(mlangtag,'g');__cov_xlcucJyua6odXhLRctX6xg.s['120']++;innerHTML=innerHTML.replace(regularExpression,decoratedmlangtag);}else{__cov_xlcucJyua6odXhLRctX6xg.b['20'][1]++;}}__cov_xlcucJyua6odXhLRctX6xg.s['121']++;textarea.set('innerHTML',innerHTML);}else{__cov_xlcucJyua6odXhLRctX6xg.b['19'][1]++;}},_getHTMLwithCleanedTags:function(){__cov_xlcucJyua6odXhLRctX6xg.f['14']++;__cov_xlcucJyua6odXhLRctX6xg.s['122']++;var host=this.get('host'),innerHTML=host.getCleanHTML(),regexString,regularExpression,spanedmlangtags,spanedmlangtag,cleanmlangtag,index;__cov_xlcucJyua6odXhLRctX6xg.s['123']++;regexString=OPENING_SPAN+'.*?'+'';__cov_xlcucJyua6odXhLRctX6xg.s['124']++;regularExpression=new RegExp(regexString,'g');__cov_xlcucJyua6odXhLRctX6xg.s['125']++;spanedmlangtags=innerHTML.match(regularExpression);__cov_xlcucJyua6odXhLRctX6xg.s['126']++;if(spanedmlangtags!==null){__cov_xlcucJyua6odXhLRctX6xg.b['21'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['127']++;for(index=0;index','');__cov_xlcucJyua6odXhLRctX6xg.s['131']++;innerHTML=innerHTML.replace(spanedmlangtag,cleanmlangtag);}}else{__cov_xlcucJyua6odXhLRctX6xg.b['21'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['132']++;return innerHTML;}},{ATTRS:{languages:DEFAULT_LANGUAGE,capability:DEFAULT_CAPABILITY,highlight:DEFAULT_HIGHLIGHT,css:DEFAULT_CSS}});},'@VERSION@',{'requires':['moodle-editor_atto-plugin']}); diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js index 5163b84..9514443 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js @@ -72,10 +72,13 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att _highlight: true, initializer: function() { - var hascapability = this.get(ATTR_CAPABILITY); + var hascapability = this.get(ATTR_CAPABILITY), + toolbarItems, + host, + form; if (hascapability) { - var toolbarItems = this._initializeToolbarItems(); + toolbarItems = this._initializeToolbarItems(); this.addToolbarMenu({ globalItemConfig: { @@ -94,8 +97,8 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att // Attach a submit listener to the form, so we can remove // the highlighting html before sending content to Moodle. - var host = this.get('host'); - var form = host.textarea.ancestor('form'); + host = this.get('host'); + form = host.textarea.ancestor('form'); if (form) { form.on('submit', this._cleanMlangTags, this); } @@ -256,13 +259,14 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * @return {string} selected text's html; empty if nothing selected */ _getSelectionHTML: function() { - var html = ''; + var html = '', + selection, + container, + index, + length; if (typeof window.getSelection !== 'undefined') { - var selection = window.getSelection(), - container, - index, - length; + selection = window.getSelection(); if (selection.rangeCount) { container = document.createElement('div'); @@ -286,12 +290,12 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att * cursor is placed within a highlighted multilang tag, the whole tag is selected. * * @method _checkSelectionChange - * @param {EventFacade} e An event object. * @private */ - _checkSelectionChange: function(e) { + _checkSelectionChange: function() { var host = this.get('host'), - node = host.getSelectionParentNode(); + node = host.getSelectionParentNode(), + selection; // If the event fires without a parent node, ignore the whole thing. if ((typeof node === 'undefined') || (node === null)) { @@ -299,7 +303,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att } if ((node.parentElement.nodeName === 'SPAN') && - (node.parentElement.getAttributeNode('class').value.indexOf(CLASSES.TAG) !== -1)) { + (node.parentElement.getAttribute('class').indexOf(CLASSES.TAG) !== -1)) { selection = host.getSelectionFromNode(Y.one(node)); host.setSelection(selection); return; @@ -383,12 +387,15 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att */ _getHTMLwithCleanedTags: function(content) { // This is better to run detached from the DOM, so the browser doesn't try to update on each change. - var holder = document.createElement('div'); + var holder = document.createElement('div'), + spans, + spansarr; + holder.innerHTML = content; - var spans = holder.getElementsByTagName('span'); + spans = holder.getElementsByTagName('span'); // Since we will be removing elements from the list, we should copy it to an array, making it static. - var spansarr = Array.prototype.slice.call(spans, 0); + spansarr = Array.prototype.slice.call(spans, 0); spansarr.forEach(function(span) { if (span.className.indexOf(CLASSES.TAG) !== -1) { diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js index f0d313e..964bc98 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js @@ -1 +1 @@ -YUI.add("moodle-atto_multilang2-button",function(e,t){var n={TAG:"filter-multilang-tag"},r="%lang",i="%content",s="languages",o="capability",u="highlight",a="css",f='{"en":"English (en)"}',l=!0,c=!0,h="outline: 1px dotted;padding: 0.1em;margin: 0em 0.1em;background-color: #ffffaa;",p={SPANED:' {mlang '+r+"}"+i+'{mlang} ',NOT_SPANED:"{mlang "+r+"}"+i+"{mlang}"},d='';e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t=[];e&&(t=this._initializeToolbarItems(),this._highlight=this.get(u),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this._addDelimiterCss(),this._highlight&&(this._decorateTagsOnInit(),this._setSubmitListeners()))},_addDelimiterCss:function(){var e="."+n.TAG+"{"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._highlight?p.SPANED:p.NOT_SPANED,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r-1,s=r.match(/\{mlang/g).length===1,i&&s&&t.setSelection(t.getSelectionFromNode(e.one(n)))},_setSubmitListeners:function(){var t=e.all("input[type=submit]");t.each(this._addListenerToSubmitButtons,this)},_addListenerToSubmitButtons:function(e){var t,n,r,i,s;t=document.getElementById(e.get("id")),t!==null&&(n=t.className,r=t.form.className,i=n.match(/btn-cancel/g)===null,s=r.match(/mform/g).length>0,i&&s&&e.on("click",this._cleanTagsOnSubmit,this,e))},_cleanTagsOnSubmit:function(e,t){e.preventDefault(),this._cleanTagsWithNoYuiId(),this._cleanTagsWithYuiId(),t.detach("click",this._cleanTagsOnSubmit),t.simulate("click")},_cleanTagsWithNoYuiId:function(){var t=e.all(".editor_atto_content"),n,r,i,s,o,u,a,f;f=new RegExp(d+".*?"+"","g"),!t instanceof Array&&(n=t,t=[],t[0]=n);for(r=0;r",""),i=i.replace(o,a);n.set("innerHTML",i)}this.markUpdated()},_cleanTagsWithYuiId:function(){var t=e.all(".editor_atto_content"),n,r,i,s,o,u,a,f,l,c;f=d.replace("","g"),!t instanceof Array&&(n=t,t=[],t[0]=n);for(r=0;r",""),i=i.replace(s,u);n.set("innerHTML",i),this.markUpdated()}},_decorateTagsOnInit:function(){var t=e.all(".editor_atto_content"),n,r,i,s,o,u,a=[],f;n=this._getHTMLwithCleanedTags(),r=new RegExp("{mlang.*?}","g"),i=n.match(r);if(i!==null){for(o=0;o",r=new RegExp(s,"g"),n=n.replace(r,u));t.set("innerHTML",n)}},_getHTMLwithCleanedTags:function(){var e=this.get("host"),t=e.getCleanHTML(),n,r,i,s,o,u;n=d+".*?"+"",r=new RegExp(n,"g"),i=t.match(r);if(i!==null)for(u=0;u",""),t=t.replace(s,o);return t}},{ATTRS:{languages:f,capability:l,highlight:c,css:h}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]}); +YUI.add("moodle-atto_multilang2-button",function(e,t){var n={TAG:"filter-multilang-tag"},r="%lang",i="%content",s="languages",o="capability",u="highlight",a="css",f='{"en":"English (en)"}',l=!0,c=!0,h="outline: 1px dotted;padding: 0.1em;margin: 0em 0.1em;background-color: #ffffaa;",p='',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller.name==="_showHTML"&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var e=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller.name==="_showHTML"&&t._highlightMlangTags(),e}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r Date: Wed, 4 Oct 2017 03:21:18 +0200 Subject: [PATCH 04/11] Mark the plugin compatible with 2.9 or later --- version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.php b/version.php index 2772a37..ad4674b 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 'v3.3.1.8 (version v1.8 for Moodle 3.3) (2016121100)'; -$plugin->requires = 2017051500; // Required Moodle version. +$plugin->release = 'v3.3.1.8 (v1.8 for Moodle 2.9 or later) (2017100200)'; +$plugin->requires = 2015051100; // Required Moodle version. $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; $plugin->dependencies = array( From 55bda03e065b580ff3ebd1063bbf483f373fe149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Wed, 4 Oct 2017 17:27:10 +0200 Subject: [PATCH 05/11] Fix 'Code checker' and 'PHPDoc check' errors and warnings --- db/access.php | 18 ++++++++++++++---- yui/src/button/js/button.js | 24 ++++++++++++------------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/db/access.php b/db/access.php index 2e9bb23..459e75c 100644 --- a/db/access.php +++ b/db/access.php @@ -14,12 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Atto text editor multilanguage plugin lib. + * + * @package atto_multilang2 + * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + defined('MOODLE_INTERNAL') || die(); -// Plugin for Moodle 'Multilingual content' drop down menu. -// @package atto_multilang2 -// @copyright 2016 onwards Julen Pardo & Mondragon Unibertsitatea -// @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. +/* + * Plugin for Moodle 'Multilingual content' drop down menu. + * @package atto_multilang2 + * @copyright 2016 onwards Julen Pardo & Mondragon Unibertsitatea + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. + */ $capabilities = array( 'atto/multilang2:viewlanguagemenu' => array( diff --git a/yui/src/button/js/button.js b/yui/src/button/js/button.js index f017236..43d72db 100644 --- a/yui/src/button/js/button.js +++ b/yui/src/button/js/button.js @@ -24,6 +24,14 @@ * @module moodle-atto_multilang2-button */ +/** + * Atto text editor multilanguage plugin. + * + * @namespace M.atto_multilang2 + * @class button + * @extends M.editor_atto.EditorPlugin. + */ + var CLASSES = { TAG: 'filter-multilang-tag' }, @@ -37,7 +45,7 @@ var CLASSES = { DEFAULT_LANGUAGE = '{"en":"English (en)"}', DEFAULT_CAPABILITY = true, DEFAULT_HIGHLIGHT = true, - DEFAULT_CSS = 'outline: 1px dotted;' + + DEFAULT_CSS = 'outline: 1px dotted' + 'padding: 0.1em;' + 'margin: 0em 0.1em;' + 'background-color: #ffffaa;', @@ -45,18 +53,10 @@ var CLASSES = { CLOSING_SPAN = '', TEMPLATES = { SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + - CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', - + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' }; -/** - * Atto text editor multilanguage plugin. - * - * @namespace M.atto_multilang2 - * @class button - * @extends M.editor_atto.EditorPlugin - */ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { @@ -173,7 +173,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att */ _hookUpdateOriginal: function() { var host = this.get('host'), - multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags() + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags(). host.updateOriginal = (function() { var _updateOriginal = host.updateOriginal; @@ -199,7 +199,7 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att */ _hookUpdateFromTextArea: function() { var host = this.get('host'), - multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags() + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags(). host.updateFromTextArea = (function() { var _updateFromTextArea = host.updateFromTextArea; From 8d7e1284c61405bf8e9c3e199b000418c403de8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Fri, 6 Oct 2017 17:56:06 +0200 Subject: [PATCH 06/11] Make switching to/from HTML view work in IE IE doesn't implement Function.caller.name (it's not part of the standard), so we directly use the function from the HTML view plugin object for the comparison. Tested with Internet Explorer 11.0.9600.18762 in Windows 7 SP1, and Firefox 56 and Chromium 61.0.3163.100 in Linux. Also fix CSS bug introduced in a previous commit (missing ';' at the end of the property). --- .../moodle-atto_multilang2-button-debug.js | 36 ++++++++++--------- .../moodle-atto_multilang2-button-min.js | 2 +- .../moodle-atto_multilang2-button.js | 36 ++++++++++--------- yui/src/button/js/button.js | 20 ++++++----- 4 files changed, 50 insertions(+), 44 deletions(-) diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js index 9514443..5875551 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js @@ -26,6 +26,14 @@ YUI.add('moodle-atto_multilang2-button', function (Y, NAME) { * @module moodle-atto_multilang2-button */ +/** + * Atto text editor multilanguage plugin. + * + * @namespace M.atto_multilang2 + * @class button + * @extends M.editor_atto.EditorPlugin. + */ + var CLASSES = { TAG: 'filter-multilang-tag' }, @@ -48,17 +56,9 @@ var CLASSES = { TEMPLATES = { SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN + CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ', - NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' }; -/** - * Atto text editor multilanguage plugin. - * - * @namespace M.atto_multilang2 - * @class button - * @extends M.editor_atto.EditorPlugin - */ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { @@ -175,12 +175,12 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att */ _hookUpdateOriginal: function() { var host = this.get('host'), - multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags() + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags(). host.updateOriginal = (function() { var _updateOriginal = host.updateOriginal; return function() { - if (multilangplugin._highlight && (this.updateOriginal.caller.name === "_showHTML")) { + if (multilangplugin._highlight && (this.updateOriginal.caller === host.plugins.html._showHTML)) { multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML())); } return _updateOriginal.apply(this, arguments); @@ -201,13 +201,13 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att */ _hookUpdateFromTextArea: function() { var host = this.get('host'), - multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags() + multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags(). host.updateFromTextArea = (function() { var _updateFromTextArea = host.updateFromTextArea; return function() { var ret = _updateFromTextArea.apply(this, arguments); - if (multilangplugin._highlight && (this.updateFromTextArea.caller.name === "_showHTML")) { + if (multilangplugin._highlight && (this.updateFromTextArea.caller === host.plugins.html._showHTML)) { multilangplugin._highlightMlangTags(); } return ret; @@ -297,16 +297,18 @@ Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_att node = host.getSelectionParentNode(), selection; - // If the event fires without a parent node, ignore the whole thing. - if ((typeof node === 'undefined') || (node === null)) { + // If the event fires without a parent node for the selection, ignore the whole thing. + if ((typeof node === 'undefined') || (node === null) || (node === false) || + (typeof node.parentNode === 'undefined') || (node.parentNode === null)) { return; } - if ((node.parentElement.nodeName === 'SPAN') && - (node.parentElement.getAttribute('class').indexOf(CLASSES.TAG) !== -1)) { + var parentNodeName = node.parentNode.nodeName, + parentClass = node.parentNode.hasAttribute('class') ? node.parentNode.getAttribute('class') : ''; + if ((typeof parentNodeName !== 'undefined') && (parentNodeName !== null) && (parentClass !== '') && + (parentNodeName === 'SPAN') && (parentClass.indexOf(CLASSES.TAG) !== -1)) { selection = host.getSelectionFromNode(Y.one(node)); host.setSelection(selection); - return; } }, diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js index 964bc98..07f0a8e 100644 --- a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js @@ -1 +1 @@ -YUI.add("moodle-atto_multilang2-button",function(e,t){var n={TAG:"filter-multilang-tag"},r="%lang",i="%content",s="languages",o="capability",u="highlight",a="css",f='{"en":"English (en)"}',l=!0,c=!0,h="outline: 1px dotted;padding: 0.1em;margin: 0em 0.1em;background-color: #ffffaa;",p='',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller.name==="_showHTML"&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var e=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller.name==="_showHTML"&&t._highlightMlangTags(),e}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller===e.plugins.html._showHTML&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var r=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller===e.plugins.html._showHTML&&t._highlightMlangTags(),r}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r Date: Thu, 5 Oct 2017 19:28:59 +0200 Subject: [PATCH 07/11] Update README and version.php for v1.8 release --- README.md | 18 +++++++++--------- version.php | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 894acea..a26524a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ Atto multilanguage plugin ========================= -![Release](https://img.shields.io/badge/release-v1.7-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3-green.svg) +![Release](https://img.shields.io/badge/release-v1.8-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3-green.svg) This plugin will make the creation of multilingual contents on Moodle much more easier with Atto editor. The plugin is developed to work with [Iñaki Arenaza's multilang2 filter](https://github.com/iarenaza/moodle-filter_multilang2), and the idea is based on [his plugin for TinyMCE editor](https://github.com/iarenaza/moodle-tinymce_moodlelang2). ## Current version -The latest release is the v1.7 (build 2016121100) for Moodle 2.9, 3.0, 3.1, 3.2 and 3.3 (Checkout [v2.9.1.7](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v2.9.1.6), [v3.0.1.7](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.0.1.6), [v3.1.1.7](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.1.1.6), [v3.2.1.7](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.2.1.6) and [v3.3.1.7](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.3.1.7) releases, respectively. -## Changes from v1.6 - - Add missing capability string +The latest release is v1.8 (build 2017100200) for Moodle 2.9, 3.0, 3.1, 3.2 and 3.3 (Checkout [v2.9.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v2.9.1.8), [v3.0.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.0.1.8), [v3.1.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.1.1.8), [v3.2.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.2.1.8) and [v3.3.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.3.1.8) releases, respectively. + +## Changes from v1.7 + - Multiple bug fixes, especially when there were more than one Atto editor in the same page. Closes issues #2, #18, #21. + - Multilang tag highlighting 's are now removed/added when switching to/from HTML view, reducing clutter when editing the HTML code. + - Cleaned the code to conform to PHP, PHPDoc and Javascript Moodle coding guidelines (all checks pass!) ## Requirements As mentioned before, [filter_multilang2](https://github.com/iarenaza/moodle-filter_multilang2) is required. @@ -19,11 +22,8 @@ As mentioned before, [filter_multilang2](https://github.com/iarenaza/moodle-filt ## Installation - Copy repository content in *moodleroot*/lib/editor/atto/plugins. The following can be omitted: - - moodle-javascript_style_checker/ - tests/ (if you're not going to test it with Behat) - .gitmodules - build.xml - - Install it from Moodle. - - Go to Site administration/Plugins/Text - editors/Atto HTML editor/Atto toolbar settings, and add *multilang2* - to the Toolbar config where you prefer. E.g. `multilang2 = multilang2` + - Install the plugin from Moodle. + - Go to "Site administration" >> "Plugins" >> "Text editors" >> "Atto HTML editor" >> "Atto toolbar settings", and add *multilang2* to the Toolbar config where you prefer. E.g. `multilang2 = multilang2` (see [Text editor - Toolbar settings](https://docs.moodle.org/en/Text_editor#Toolbar_settings) and [Text editor - Adding extra buttons](https://docs.moodle.org/en/Text_editor#Adding_extra_buttons) for instructions on how to add a plugin button to Atto toolbar. diff --git a/version.php b/version.php index ad4674b..9473e45 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 'v3.3.1.8 (v1.8 for Moodle 2.9 or later) (2017100200)'; +$plugin->release = 'master - Release v1.8 (Build 2017100200) for Moodle 2.9 or later.'; $plugin->requires = 2015051100; // Required Moodle version. $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; From c2afda030204e9f4d063df07e08a150bc9769823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Fri, 27 Oct 2017 22:17:31 +0200 Subject: [PATCH 08/11] Revert release data merged by mistake --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 9473e45..aedab38 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 'master - Release v1.8 (Build 2017100200) for Moodle 2.9 or later.'; +$plugin->release = 'v3.3.1.8 (version v1.8 for Moodle 3.3) (2017100200)'; $plugin->requires = 2015051100; // Required Moodle version. $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; From f5f13413ac5e47a483029917b95041348906240d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Fri, 27 Oct 2017 21:45:16 +0200 Subject: [PATCH 09/11] Add Grunt files from stock moodle And make sure we don't include in the repo the Node modules used by Grunt. --- .gitignore | 1 + Gruntfile.js | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 25 ++++ 3 files changed, 429 insertions(+) create mode 100644 .gitignore create mode 100644 Gruntfile.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..5f2302e --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,403 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . +/* jshint node: true, browser: false */ +/* eslint-env node */ + +/** + * @copyright 2014 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Grunt configuration + */ + +module.exports = function(grunt) { + var path = require('path'), + tasks = {}, + cwd = process.env.PWD || process.cwd(), + async = require('async'), + DOMParser = require('xmldom').DOMParser, + xpath = require('xpath'), + semver = require('semver'); + + // Verify the node version is new enough. + var expected = semver.validRange(grunt.file.readJSON('package.json').engines.node); + var actual = semver.valid(process.version); + if (!semver.satisfies(actual, expected)) { + grunt.fail.fatal('Node version too old. Require ' + expected + ', version installed: ' + actual); + } + + // Windows users can't run grunt in a subdirectory, so allow them to set + // the root by passing --root=path/to/dir. + if (grunt.option('root')) { + var root = grunt.option('root'); + if (grunt.file.exists(__dirname, root)) { + cwd = path.join(__dirname, root); + grunt.log.ok('Setting root to ' + cwd); + } else { + grunt.fail.fatal('Setting root to ' + root + ' failed - path does not exist'); + } + } + + var inAMD = path.basename(cwd) == 'amd'; + + // Globbing pattern for matching all AMD JS source files. + var amdSrc = [inAMD ? cwd + '/src/*.js' : '**/amd/src/*.js']; + + /** + * Function to generate the destination for the uglify task + * (e.g. build/file.min.js). This function will be passed to + * the rename property of files array when building dynamically: + * http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically + * + * @param {String} destPath the current destination + * @param {String} srcPath the matched src path + * @return {String} The rewritten destination path. + */ + var uglifyRename = function(destPath, srcPath) { + destPath = srcPath.replace('src', 'build'); + destPath = destPath.replace('.js', '.min.js'); + destPath = path.resolve(cwd, destPath); + return destPath; + }; + + /** + * Find thirdpartylibs.xml and generate an array of paths contained within + * them (used to generate ignore files and so on). + * + * @return {array} The list of thirdparty paths. + */ + var getThirdPartyPathsFromXML = function() { + var thirdpartyfiles = grunt.file.expand('*/**/thirdpartylibs.xml'); + var libs = ['node_modules/', 'vendor/']; + + thirdpartyfiles.forEach(function(file) { + var dirname = path.dirname(file); + + var doc = new DOMParser().parseFromString(grunt.file.read(file)); + var nodes = xpath.select("/libraries/library/location/text()", doc); + + nodes.forEach(function(node) { + var lib = path.join(dirname, node.toString()); + if (grunt.file.isDir(lib)) { + // Ensure trailing slash on dirs. + lib = lib.replace(/\/?$/, '/'); + } + + // Look for duplicate paths before adding to array. + if (libs.indexOf(lib) === -1) { + libs.push(lib); + } + }); + }); + return libs; + }; + + // Project configuration. + grunt.initConfig({ + eslint: { + // Even though warnings dont stop the build we don't display warnings by default because + // at this moment we've got too many core warnings. + options: {quiet: !grunt.option('show-lint-warnings')}, + amd: { + src: amdSrc, + // Check AMD with some slightly stricter rules. + rules: { + 'no-unused-vars': 'error', + 'no-implicit-globals': 'error' + } + }, + // Check YUI module source files. + yui: { + src: ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js'], + options: { + // Disable some rules which we can't safely define for YUI rollups. + rules: { + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'no-unused-expressions': 'off' + } + } + } + }, + uglify: { + amd: { + files: [{ + expand: true, + src: amdSrc, + rename: uglifyRename + }], + options: {report: 'none'} + } + }, + less: { + bootstrapbase: { + files: { + "theme/bootstrapbase/style/moodle.css": "theme/bootstrapbase/less/moodle.less", + "theme/bootstrapbase/style/editor.css": "theme/bootstrapbase/less/editor.less", + }, + options: { + compress: false // We must not compress to keep the comments. + } + } + }, + watch: { + options: { + nospawn: true // We need not to spawn so config can be changed dynamically. + }, + amd: { + files: ['**/amd/src/**/*.js'], + tasks: ['amd'] + }, + bootstrapbase: { + files: ["theme/bootstrapbase/less/**/*.less"], + tasks: ["css"] + }, + yui: { + files: ['**/yui/src/**/*.js'], + tasks: ['yui'] + }, + gherkinlint: { + files: ['**/tests/behat/*.feature'], + tasks: ['gherkinlint'] + } + }, + shifter: { + options: { + recursive: true, + paths: [cwd] + } + }, + gherkinlint: { + options: { + files: ['**/tests/behat/*.feature'], + } + }, + stylelint: { + less: { + options: { + syntax: 'less', + configOverrides: { + rules: { + // These rules have to be disabled in .stylelintrc for scss compat. + "at-rule-no-unknown": true, + "no-browser-hacks": [true, {"severity": "warning"}] + } + } + }, + src: ['theme/**/*.less'] + }, + scss: { + options: {syntax: 'scss'}, + src: ['*/**/*.scss'] + }, + css: { + src: ['*/**/*.css'], + options: { + configOverrides: { + rules: { + // These rules have to be disabled in .stylelintrc for scss compat. + "at-rule-no-unknown": true, + "no-browser-hacks": [true, {"severity": "warning"}] + } + } + } + } + } + }); + + /** + * Generate ignore files (utilising thirdpartylibs.xml data) + */ + tasks.ignorefiles = function() { + // An array of paths to third party directories. + var thirdPartyPaths = getThirdPartyPathsFromXML(); + // Generate .eslintignore. + var eslintIgnores = ['# Generated by "grunt ignorefiles"', '*/**/yui/src/*/meta/', '*/**/build/'].concat(thirdPartyPaths); + grunt.file.write('.eslintignore', eslintIgnores.join('\n')); + // Generate .stylelintignore. + var stylelintIgnores = [ + '# Generated by "grunt ignorefiles"', + 'theme/bootstrapbase/style/', + 'theme/clean/style/custom.css', + 'theme/more/style/custom.css' + ].concat(thirdPartyPaths); + grunt.file.write('.stylelintignore', stylelintIgnores.join('\n')); + }; + + /** + * Shifter task. Is configured with a path to a specific file or a directory, + * in the case of a specific file it will work out the right module to be built. + * + * Note that this task runs the invidiaul shifter jobs async (becase it spawns + * so be careful to to call done(). + */ + tasks.shifter = function() { + var done = this.async(), + options = grunt.config('shifter.options'); + + // Run the shifter processes one at a time to avoid confusing output. + async.eachSeries(options.paths, function(src, filedone) { + var args = []; + args.push(path.normalize(__dirname + '/node_modules/shifter/bin/shifter')); + + // Always ignore the node_modules directory. + args.push('--excludes', 'node_modules'); + + // Determine the most appropriate options to run with based upon the current location. + if (grunt.file.isMatch('**/yui/**/*.js', src)) { + // When passed a JS file, build our containing module (this happen with + // watch). + grunt.log.debug('Shifter passed a specific JS file'); + src = path.dirname(path.dirname(src)); + options.recursive = false; + } else if (grunt.file.isMatch('**/yui/src', src)) { + // When in a src directory --walk all modules. + grunt.log.debug('In a src directory'); + args.push('--walk'); + options.recursive = false; + } else if (grunt.file.isMatch('**/yui/src/*', src)) { + // When in module, only build our module. + grunt.log.debug('In a module directory'); + options.recursive = false; + } else if (grunt.file.isMatch('**/yui/src/*/js', src)) { + // When in module src, only build our module. + grunt.log.debug('In a source directory'); + src = path.dirname(src); + options.recursive = false; + } + + if (grunt.option('watch')) { + grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead'); + } + + // Add the stderr option if appropriate + if (grunt.option('verbose')) { + args.push('--lint-stderr'); + } + + if (grunt.option('no-color')) { + args.push('--color=false'); + } + + var execShifter = function() { + + grunt.log.ok("Running shifter on " + src); + grunt.util.spawn({ + cmd: "node", + args: args, + opts: {cwd: src, stdio: 'inherit', env: process.env} + }, function(error, result, code) { + if (code) { + grunt.fail.fatal('Shifter failed with code: ' + code); + } else { + grunt.log.ok('Shifter build complete.'); + filedone(); + } + }); + }; + + // Actually run shifter. + if (!options.recursive) { + execShifter(); + } else { + // Check that there are yui modules otherwise shifter ends with exit code 1. + if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) { + args.push('--recursive'); + execShifter(); + } else { + grunt.log.ok('No YUI modules to build.'); + filedone(); + } + } + }, done); + }; + + tasks.gherkinlint = function() { + var done = this.async(), + options = grunt.config('gherkinlint.options'); + + var args = grunt.file.expand(options.files); + args.unshift(path.normalize(__dirname + '/node_modules/.bin/gherkin-lint')); + grunt.util.spawn({ + cmd: 'node', + args: args, + opts: {stdio: 'inherit', env: process.env} + }, function(error, result, code) { + // Propagate the exit code. + done(code === 0); + }); + }; + + tasks.startup = function() { + // Are we in a YUI directory? + if (path.basename(path.resolve(cwd, '../../')) == 'yui') { + grunt.task.run('yui'); + // Are we in an AMD directory? + } else if (inAMD) { + grunt.task.run('amd'); + } else { + // Run them all!. + grunt.task.run('css'); + grunt.task.run('js'); + grunt.task.run('gherkinlint'); + } + }; + + // On watch, we dynamically modify config to build only affected files. This + // method is slightly complicated to deal with multiple changed files at once (copied + // from the grunt-contrib-watch readme). + var changedFiles = Object.create(null); + var onChange = grunt.util._.debounce(function() { + var files = Object.keys(changedFiles); + grunt.config('eslint.amd.src', files); + grunt.config('eslint.yui.src', files); + grunt.config('uglify.amd.files', [{expand: true, src: files, rename: uglifyRename}]); + grunt.config('shifter.options.paths', files); + grunt.config('stylelint.less.src', files); + grunt.config('gherkinlint.options.files', files); + changedFiles = Object.create(null); + }, 200); + + grunt.event.on('watch', function(action, filepath) { + changedFiles[filepath] = action; + onChange(); + }); + + // Register NPM tasks. + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-stylelint'); + + // Register JS tasks. + grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter); + grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', tasks.gherkinlint); + grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles); + grunt.registerTask('yui', ['eslint:yui', 'shifter']); + grunt.registerTask('amd', ['eslint:amd', 'uglify']); + grunt.registerTask('js', ['amd', 'yui']); + + // Register CSS taks. + grunt.registerTask('css', ['stylelint:scss', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']); + + // Register the startup task. + grunt.registerTask('startup', 'Run the correct tasks for the current directory', tasks.startup); + + // Register the default task. + grunt.registerTask('default', ['startup']); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..2a7d142 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "Moodle", + "private": true, + "description": "Moodle", + "devDependencies": { + "async": "1.5.2", + "eslint": "3.7.1", + "gherkin-lint": "1.1.3", + "grunt": "1.0.1", + "grunt-contrib-less": "1.3.0", + "grunt-contrib-uglify": "1.0.1", + "grunt-contrib-watch": "1.0.0", + "grunt-eslint": "19.0.0", + "grunt-stylelint": "0.6.0", + "semver": "5.3.0", + "shifter": "0.5.0", + "stylelint": "7.4.1", + "stylelint-checkstyle-formatter": "0.1.0", + "xmldom": "0.1.22", + "xpath": "0.0.23" + }, + "engines": { + "node": ">=4" + } +} From fc8dce4bdb5dae061cb0f96068cd75b617459dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Arenaza?= Date: Fri, 27 Oct 2017 21:46:55 +0200 Subject: [PATCH 10/11] Fix another warning from Grunt Shifter --- .../moodle-atto_multilang2-button-coverage.js | 6 ++++++ .../moodle-atto_multilang2-button-debug.js | 6 ++++-- .../moodle-atto_multilang2-button-min.js | 2 +- .../moodle-atto_multilang2-button.js | 6 ++++-- yui/src/button/js/button.js | 6 ++++-- 5 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js new file mode 100644 index 0000000..3eb8e65 --- /dev/null +++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js @@ -0,0 +1,6 @@ +if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } +if (!__coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']) { + __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js'] = {"path":"build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0,0,0,0],"16":[0,0],"17":[0,0],"18":[0,0,0,0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0},"fnMap":{"1":{"name":"(anonymous_1)","line":1,"loc":{"start":{"line":1,"column":41},"end":{"line":1,"column":60}}},"2":{"name":"(anonymous_2)","line":74,"loc":{"start":{"line":74,"column":17},"end":{"line":74,"column":28}}},"3":{"name":"(anonymous_3)","line":130,"loc":{"start":{"line":130,"column":29},"end":{"line":130,"column":40}}},"4":{"name":"(anonymous_4)","line":154,"loc":{"start":{"line":154,"column":25},"end":{"line":154,"column":36}}},"5":{"name":"(anonymous_5)","line":176,"loc":{"start":{"line":176,"column":25},"end":{"line":176,"column":36}}},"6":{"name":"(anonymous_6)","line":180,"loc":{"start":{"line":180,"column":31},"end":{"line":180,"column":42}}},"7":{"name":"(anonymous_7)","line":182,"loc":{"start":{"line":182,"column":19},"end":{"line":182,"column":30}}},"8":{"name":"(anonymous_8)","line":202,"loc":{"start":{"line":202,"column":29},"end":{"line":202,"column":40}}},"9":{"name":"(anonymous_9)","line":206,"loc":{"start":{"line":206,"column":35},"end":{"line":206,"column":46}}},"10":{"name":"(anonymous_10)","line":208,"loc":{"start":{"line":208,"column":19},"end":{"line":208,"column":30}}},"11":{"name":"(anonymous_11)","line":234,"loc":{"start":{"line":234,"column":14},"end":{"line":234,"column":40}}},"12":{"name":"(anonymous_12)","line":261,"loc":{"start":{"line":261,"column":23},"end":{"line":261,"column":34}}},"13":{"name":"(anonymous_13)","line":295,"loc":{"start":{"line":295,"column":27},"end":{"line":295,"column":38}}},"14":{"name":"(anonymous_14)","line":323,"loc":{"start":{"line":323,"column":21},"end":{"line":323,"column":32}}},"15":{"name":"(anonymous_15)","line":346,"loc":{"start":{"line":346,"column":25},"end":{"line":346,"column":36}}},"16":{"name":"(anonymous_16)","line":392,"loc":{"start":{"line":392,"column":29},"end":{"line":392,"column":47}}},"17":{"name":"(anonymous_17)","line":404,"loc":{"start":{"line":404,"column":25},"end":{"line":404,"column":40}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":460,"column":61}},"2":{"start":{"line":37,"column":0},"end":{"line":60,"column":6}},"3":{"start":{"line":63,"column":0},"end":{"line":457,"column":3}},"4":{"start":{"line":75,"column":8},"end":{"line":78,"column":17}},"5":{"start":{"line":80,"column":8},"end":{"line":119,"column":9}},"6":{"start":{"line":81,"column":12},"end":{"line":81,"column":58}},"7":{"start":{"line":83,"column":12},"end":{"line":90,"column":15}},"8":{"start":{"line":92,"column":12},"end":{"line":92,"column":54}},"9":{"start":{"line":94,"column":12},"end":{"line":94,"column":55}},"10":{"start":{"line":95,"column":12},"end":{"line":118,"column":13}},"11":{"start":{"line":96,"column":16},"end":{"line":96,"column":54}},"12":{"start":{"line":100,"column":16},"end":{"line":100,"column":40}},"13":{"start":{"line":101,"column":16},"end":{"line":101,"column":54}},"14":{"start":{"line":102,"column":16},"end":{"line":104,"column":17}},"15":{"start":{"line":103,"column":20},"end":{"line":103,"column":66}},"16":{"start":{"line":108,"column":16},"end":{"line":108,"column":95}},"17":{"start":{"line":111,"column":16},"end":{"line":111,"column":85}},"18":{"start":{"line":112,"column":16},"end":{"line":112,"column":85}},"19":{"start":{"line":116,"column":16},"end":{"line":116,"column":43}},"20":{"start":{"line":117,"column":16},"end":{"line":117,"column":47}},"21":{"start":{"line":131,"column":8},"end":{"line":133,"column":21}},"22":{"start":{"line":135,"column":8},"end":{"line":135,"column":57}},"23":{"start":{"line":136,"column":8},"end":{"line":143,"column":9}},"24":{"start":{"line":137,"column":12},"end":{"line":142,"column":13}},"25":{"start":{"line":138,"column":16},"end":{"line":141,"column":19}},"26":{"start":{"line":145,"column":8},"end":{"line":145,"column":28}},"27":{"start":{"line":155,"column":8},"end":{"line":156,"column":18}},"28":{"start":{"line":158,"column":8},"end":{"line":158,"column":48}},"29":{"start":{"line":159,"column":8},"end":{"line":159,"column":32}},"30":{"start":{"line":160,"column":8},"end":{"line":160,"column":30}},"31":{"start":{"line":162,"column":8},"end":{"line":162,"column":41}},"32":{"start":{"line":177,"column":8},"end":{"line":178,"column":35}},"33":{"start":{"line":180,"column":8},"end":{"line":188,"column":13}},"34":{"start":{"line":181,"column":12},"end":{"line":181,"column":54}},"35":{"start":{"line":182,"column":12},"end":{"line":187,"column":14}},"36":{"start":{"line":183,"column":16},"end":{"line":185,"column":17}},"37":{"start":{"line":184,"column":20},"end":{"line":184,"column":126}},"38":{"start":{"line":186,"column":16},"end":{"line":186,"column":62}},"39":{"start":{"line":203,"column":8},"end":{"line":204,"column":35}},"40":{"start":{"line":206,"column":8},"end":{"line":215,"column":13}},"41":{"start":{"line":207,"column":12},"end":{"line":207,"column":62}},"42":{"start":{"line":208,"column":12},"end":{"line":214,"column":14}},"43":{"start":{"line":209,"column":16},"end":{"line":209,"column":69}},"44":{"start":{"line":210,"column":16},"end":{"line":212,"column":17}},"45":{"start":{"line":211,"column":20},"end":{"line":211,"column":58}},"46":{"start":{"line":213,"column":16},"end":{"line":213,"column":27}},"47":{"start":{"line":235,"column":8},"end":{"line":238,"column":20}},"48":{"start":{"line":240,"column":8},"end":{"line":240,"column":42}},"49":{"start":{"line":242,"column":8},"end":{"line":242,"column":45}},"50":{"start":{"line":243,"column":8},"end":{"line":243,"column":87}},"51":{"start":{"line":245,"column":8},"end":{"line":245,"column":71}},"52":{"start":{"line":246,"column":8},"end":{"line":246,"column":73}},"53":{"start":{"line":248,"column":8},"end":{"line":248,"column":54}},"54":{"start":{"line":250,"column":8},"end":{"line":250,"column":27}},"55":{"start":{"line":262,"column":8},"end":{"line":266,"column":19}},"56":{"start":{"line":268,"column":8},"end":{"line":283,"column":9}},"57":{"start":{"line":269,"column":12},"end":{"line":269,"column":46}},"58":{"start":{"line":271,"column":12},"end":{"line":277,"column":13}},"59":{"start":{"line":272,"column":16},"end":{"line":272,"column":58}},"60":{"start":{"line":273,"column":16},"end":{"line":275,"column":17}},"61":{"start":{"line":274,"column":20},"end":{"line":274,"column":87}},"62":{"start":{"line":276,"column":16},"end":{"line":276,"column":43}},"63":{"start":{"line":279,"column":15},"end":{"line":283,"column":9}},"64":{"start":{"line":280,"column":12},"end":{"line":282,"column":13}},"65":{"start":{"line":281,"column":16},"end":{"line":281,"column":65}},"66":{"start":{"line":285,"column":8},"end":{"line":285,"column":20}},"67":{"start":{"line":296,"column":8},"end":{"line":300,"column":22}},"68":{"start":{"line":303,"column":8},"end":{"line":306,"column":9}},"69":{"start":{"line":305,"column":12},"end":{"line":305,"column":19}},"70":{"start":{"line":308,"column":8},"end":{"line":308,"column":50}},"71":{"start":{"line":309,"column":8},"end":{"line":309,"column":105}},"72":{"start":{"line":310,"column":8},"end":{"line":314,"column":9}},"73":{"start":{"line":312,"column":12},"end":{"line":312,"column":63}},"74":{"start":{"line":313,"column":12},"end":{"line":313,"column":41}},"75":{"start":{"line":324,"column":8},"end":{"line":327,"column":9}},"76":{"start":{"line":325,"column":12},"end":{"line":325,"column":85}},"77":{"start":{"line":326,"column":12},"end":{"line":326,"column":31}},"78":{"start":{"line":347,"column":8},"end":{"line":354,"column":27}},"79":{"start":{"line":355,"column":8},"end":{"line":377,"column":9}},"80":{"start":{"line":356,"column":12},"end":{"line":356,"column":77}},"81":{"start":{"line":358,"column":12},"end":{"line":358,"column":62}},"82":{"start":{"line":359,"column":12},"end":{"line":359,"column":60}},"83":{"start":{"line":360,"column":12},"end":{"line":374,"column":13}},"84":{"start":{"line":361,"column":16},"end":{"line":371,"column":17}},"85":{"start":{"line":362,"column":20},"end":{"line":362,"column":48}},"86":{"start":{"line":364,"column":20},"end":{"line":364,"column":79}},"87":{"start":{"line":365,"column":20},"end":{"line":370,"column":21}},"88":{"start":{"line":366,"column":24},"end":{"line":366,"column":56}},"89":{"start":{"line":367,"column":24},"end":{"line":367,"column":85}},"90":{"start":{"line":368,"column":24},"end":{"line":368,"column":70}},"91":{"start":{"line":369,"column":24},"end":{"line":369,"column":96}},"92":{"start":{"line":373,"column":16},"end":{"line":373,"column":48}},"93":{"start":{"line":376,"column":12},"end":{"line":376,"column":31}},"94":{"start":{"line":394,"column":8},"end":{"line":396,"column":21}},"95":{"start":{"line":398,"column":8},"end":{"line":398,"column":35}},"96":{"start":{"line":399,"column":8},"end":{"line":399,"column":52}},"97":{"start":{"line":402,"column":8},"end":{"line":402,"column":56}},"98":{"start":{"line":404,"column":8},"end":{"line":414,"column":11}},"99":{"start":{"line":405,"column":12},"end":{"line":413,"column":13}},"100":{"start":{"line":407,"column":16},"end":{"line":409,"column":17}},"101":{"start":{"line":408,"column":20},"end":{"line":408,"column":72}},"102":{"start":{"line":412,"column":16},"end":{"line":412,"column":50}},"103":{"start":{"line":416,"column":8},"end":{"line":416,"column":32}}},"branchMap":{"1":{"line":80,"type":"if","locations":[{"start":{"line":80,"column":8},"end":{"line":80,"column":8}},{"start":{"line":80,"column":8},"end":{"line":80,"column":8}}]},"2":{"line":95,"type":"if","locations":[{"start":{"line":95,"column":12},"end":{"line":95,"column":12}},{"start":{"line":95,"column":12},"end":{"line":95,"column":12}}]},"3":{"line":102,"type":"if","locations":[{"start":{"line":102,"column":16},"end":{"line":102,"column":16}},{"start":{"line":102,"column":16},"end":{"line":102,"column":16}}]},"4":{"line":137,"type":"if","locations":[{"start":{"line":137,"column":12},"end":{"line":137,"column":12}},{"start":{"line":137,"column":12},"end":{"line":137,"column":12}}]},"5":{"line":183,"type":"if","locations":[{"start":{"line":183,"column":16},"end":{"line":183,"column":16}},{"start":{"line":183,"column":16},"end":{"line":183,"column":16}}]},"6":{"line":183,"type":"binary-expr","locations":[{"start":{"line":183,"column":20},"end":{"line":183,"column":46}},{"start":{"line":183,"column":51},"end":{"line":183,"column":109}}]},"7":{"line":210,"type":"if","locations":[{"start":{"line":210,"column":16},"end":{"line":210,"column":16}},{"start":{"line":210,"column":16},"end":{"line":210,"column":16}}]},"8":{"line":210,"type":"binary-expr","locations":[{"start":{"line":210,"column":20},"end":{"line":210,"column":46}},{"start":{"line":210,"column":51},"end":{"line":210,"column":113}}]},"9":{"line":243,"type":"cond-expr","locations":[{"start":{"line":243,"column":66},"end":{"line":243,"column":74}},{"start":{"line":243,"column":77},"end":{"line":243,"column":86}}]},"10":{"line":268,"type":"if","locations":[{"start":{"line":268,"column":8},"end":{"line":268,"column":8}},{"start":{"line":268,"column":8},"end":{"line":268,"column":8}}]},"11":{"line":271,"type":"if","locations":[{"start":{"line":271,"column":12},"end":{"line":271,"column":12}},{"start":{"line":271,"column":12},"end":{"line":271,"column":12}}]},"12":{"line":279,"type":"if","locations":[{"start":{"line":279,"column":15},"end":{"line":279,"column":15}},{"start":{"line":279,"column":15},"end":{"line":279,"column":15}}]},"13":{"line":280,"type":"if","locations":[{"start":{"line":280,"column":12},"end":{"line":280,"column":12}},{"start":{"line":280,"column":12},"end":{"line":280,"column":12}}]},"14":{"line":303,"type":"if","locations":[{"start":{"line":303,"column":8},"end":{"line":303,"column":8}},{"start":{"line":303,"column":8},"end":{"line":303,"column":8}}]},"15":{"line":303,"type":"binary-expr","locations":[{"start":{"line":303,"column":13},"end":{"line":303,"column":40}},{"start":{"line":303,"column":46},"end":{"line":303,"column":59}},{"start":{"line":303,"column":65},"end":{"line":303,"column":79}},{"start":{"line":304,"column":17},"end":{"line":304,"column":55}},{"start":{"line":304,"column":61},"end":{"line":304,"column":85}}]},"16":{"line":309,"type":"cond-expr","locations":[{"start":{"line":309,"column":62},"end":{"line":309,"column":99}},{"start":{"line":309,"column":102},"end":{"line":309,"column":104}}]},"17":{"line":310,"type":"if","locations":[{"start":{"line":310,"column":8},"end":{"line":310,"column":8}},{"start":{"line":310,"column":8},"end":{"line":310,"column":8}}]},"18":{"line":310,"type":"binary-expr","locations":[{"start":{"line":310,"column":13},"end":{"line":310,"column":50}},{"start":{"line":310,"column":56},"end":{"line":310,"column":79}},{"start":{"line":310,"column":85},"end":{"line":310,"column":103}},{"start":{"line":311,"column":17},"end":{"line":311,"column":42}},{"start":{"line":311,"column":48},"end":{"line":311,"column":87}}]},"19":{"line":324,"type":"if","locations":[{"start":{"line":324,"column":8},"end":{"line":324,"column":8}},{"start":{"line":324,"column":8},"end":{"line":324,"column":8}}]},"20":{"line":355,"type":"if","locations":[{"start":{"line":355,"column":8},"end":{"line":355,"column":8}},{"start":{"line":355,"column":8},"end":{"line":355,"column":8}}]},"21":{"line":360,"type":"if","locations":[{"start":{"line":360,"column":12},"end":{"line":360,"column":12}},{"start":{"line":360,"column":12},"end":{"line":360,"column":12}}]},"22":{"line":365,"type":"if","locations":[{"start":{"line":365,"column":20},"end":{"line":365,"column":20}},{"start":{"line":365,"column":20},"end":{"line":365,"column":20}}]},"23":{"line":405,"type":"if","locations":[{"start":{"line":405,"column":12},"end":{"line":405,"column":12}},{"start":{"line":405,"column":12},"end":{"line":405,"column":12}}]}},"code":["(function () { YUI.add('moodle-atto_multilang2-button', function (Y, NAME) {","","// This file is part of Moodle - http://moodle.org/","//","// Moodle is free software: you can redistribute it and/or modify","// it under the terms of the GNU General Public License as published by","// the Free Software Foundation, either version 3 of the License, or","// (at your option) any later version.","//","// Moodle is distributed in the hope that it will be useful,","// but WITHOUT ANY WARRANTY; without even the implied warranty of","// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the","// GNU General Public License for more details.","//","// You should have received a copy of the GNU General Public License","// along with Moodle. If not, see .","","/**"," * @package atto_multilang2"," * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea"," * @copyright 2017 onwards Iñaki Arenaza & Mondragon Unibertsitatea"," * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later"," */","","/**"," * @module moodle-atto_multilang2-button"," */","","/**"," * Atto text editor multilanguage plugin."," *"," * @namespace M.atto_multilang2"," * @class button"," * @extends M.editor_atto.EditorPlugin."," */","","var CLASSES = {"," TAG: 'filter-multilang-tag'"," },",""," LANG_WILDCARD = '%lang',"," CONTENT_WILDCARD = '%content',"," ATTR_LANGUAGES = 'languages',"," ATTR_CAPABILITY = 'capability',"," ATTR_HIGHLIGHT = 'highlight',"," ATTR_CSS = 'css',"," DEFAULT_LANGUAGE = '{\"en\":\"English (en)\"}',"," DEFAULT_CAPABILITY = true,"," DEFAULT_HIGHLIGHT = true,"," DEFAULT_CSS = 'outline: 1px dotted;' +"," 'padding: 0.1em;' +"," 'margin: 0em 0.1em;' +"," 'background-color: #ffffaa;',"," OPENING_SPAN = '',"," CLOSING_SPAN = '',"," TEMPLATES = {"," SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN +"," CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ',"," NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}'"," };","","","Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {",""," /**"," * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php."," *"," * @property _highlight"," * @type boolean"," * @private"," */"," _highlight: true,",""," initializer: function() {"," var hascapability = this.get(ATTR_CAPABILITY),"," toolbarItems,"," host,"," form;",""," if (hascapability) {"," toolbarItems = this._initializeToolbarItems();",""," this.addToolbarMenu({"," globalItemConfig: {"," callback: this._addTags"," },"," icon: 'icon',"," iconComponent: 'atto_multilang2',"," items: toolbarItems"," });",""," this._tagTemplate = TEMPLATES.NOT_SPANNED;",""," this._highlight = this.get(ATTR_HIGHLIGHT);"," if (this._highlight) {"," this._tagTemplate = TEMPLATES.SPANNED;",""," // Attach a submit listener to the form, so we can remove"," // the highlighting html before sending content to Moodle."," host = this.get('host');"," form = host.textarea.ancestor('form');"," if (form) {"," form.on('submit', this._cleanMlangTags, this);"," }",""," // Listen to every change of the text cursor in the text area, to see if"," // the cursor is placed within a multilang tag."," this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this);",""," // Highlight the multilang tags once everything is loaded."," this.get('host').on('pluginsloaded', this._addHighlightingCss, this);"," this.get('host').on('pluginsloaded', this._highlightMlangTags, this);",""," // Hook into host.updateOriginal() and host.updateFromTextArea()"," // so we can add/remove highlighting when we switch to/from HTML view."," this._hookUpdateOriginal();"," this._hookUpdateFromTextArea();"," }"," }"," },",""," /**"," * Initializes the toolbar items, which will be the installed languages,"," * received as parameter."," *"," * @method _initializeToolbarItems"," * @private"," * @return {Array} installed language strings"," */"," _initializeToolbarItems: function() {"," var toolbarItems = [],"," languages,"," langCode;",""," languages = JSON.parse(this.get(ATTR_LANGUAGES));"," for (langCode in languages) {"," if (languages.hasOwnProperty(langCode)) {"," toolbarItems.push({"," text: languages[langCode],"," callbackArgs: langCode"," });"," }"," }",""," return toolbarItems;"," },",""," /**"," * Adds the CSS rules for the delimiters, received as parameter from lib.php."," *"," * @method _addHighlightingCss"," * @private"," */"," _addHighlightingCss: function() {"," var css = '.' + CLASSES.TAG + ' {' + this.get(ATTR_CSS) + '}',"," style;",""," style = document.createElement('style');"," style.type = 'text/css';"," style.innerHTML = css;",""," document.head.appendChild(style);"," },",""," /**"," * Hook the host.updateOriginal() method to allow us to remove the highlighting html when"," * switching to HTML view. As the HTML view plugin doesn't provide a hook or fire an event"," * to notify about the switch to HTML view, we need to hijack host.updateOriginal and look"," * for the caller. Once we've cleaned up the highlighting, we need to execute the original"," * host.updateOriginal() method."," * Inspired by https://stackoverflow.com/a/16580937"," *"," * @method _hookUpdateOriginal"," * @private"," */"," _hookUpdateOriginal: function() {"," var host = this.get('host'),"," multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags().",""," host.updateOriginal = (function() {"," var _updateOriginal = host.updateOriginal;"," return function() {"," if (multilangplugin._highlight && (this.updateOriginal.caller === host.plugins.html._showHTML)) {"," multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML()));"," }"," return _updateOriginal.apply(this, arguments);"," };"," })();"," },",""," /**"," * Hook the host.updateFromTextAreal() method to allow us to re-add the highlighting"," * html when switching from HTML view. As the HTML view plugin doesn't provide a hook"," * or fire an event to notify about the switch from HTML view, we need to hijack"," * host.updateFromTextArea and look for the caller. Once we've executed the original"," * host.updateFromTextArea() method, we re-added the highlighting."," * Inspired by https://stackoverflow.com/a/16580937"," *"," * @method _hookUpdateFromTextArea"," * @private"," */"," _hookUpdateFromTextArea: function() {"," var host = this.get('host'),"," multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags().",""," host.updateFromTextArea = (function() {"," var _updateFromTextArea = host.updateFromTextArea;"," return function() {"," var ret = _updateFromTextArea.apply(this, arguments);"," if (multilangplugin._highlight && (this.updateFromTextArea.caller === host.plugins.html._showHTML)) {"," multilangplugin._highlightMlangTags();"," }"," return ret;"," };"," })();"," },",""," /**"," * Retrieves the selected text, wraps it with the multilang tags,"," * and replaces the selected text in the editor with with it."," *"," * If the 'highlight' setting is checked, the {mlang} will be wrapped between"," * the tags with the class for the CSS highlight; if not, they will not"," * be wrapped."," *"," * If there is no content selected, a \" \" will be inserted; otherwhise,"," * it's impossible to place the cursor inside the {mlang} tags."," *"," * @method _addTags"," * @param {EventFacade} event"," * @param {string} langCode the language code"," * @private"," */"," _addTags: function(event, langCode) {"," var selection,"," host = this.get('host'),"," taggedContent,"," content;",""," taggedContent = this._tagTemplate;",""," selection = this._getSelectionHTML();"," content = (host.getSelection().toString().length === 0) ? ' ' : selection;",""," taggedContent = taggedContent.replace(LANG_WILDCARD, langCode);"," taggedContent = taggedContent.replace(CONTENT_WILDCARD, content);",""," host.insertContentAtFocusPoint(taggedContent);",""," this.markUpdated();"," },",""," /**"," * Retrieves selected text with its HTML."," * Taken from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234"," *"," * @method _getSelectionHTML"," * @private"," * @return {string} selected text's html; empty if nothing selected"," */"," _getSelectionHTML: function() {"," var html = '',"," selection,"," container,"," index,"," length;",""," if (typeof window.getSelection !== 'undefined') {"," selection = window.getSelection();",""," if (selection.rangeCount) {"," container = document.createElement('div');"," for (index = 0, length = selection.rangeCount; index < length; ++index) {"," container.appendChild(selection.getRangeAt(index).cloneContents());"," }"," html = container.innerHTML;"," }",""," } else if (typeof document.selection !== 'undefined') {"," if (document.selection.type === 'Text') {"," html = document.selection.createRange().htmlText;"," }"," }",""," return html;"," },",""," /**"," * Listens to every change of the text cursor in the text area. If the"," * cursor is placed within a highlighted multilang tag, the whole tag is selected."," *"," * @method _checkSelectionChange"," * @private"," */"," _checkSelectionChange: function() {"," var host = this.get('host'),"," node = host.getSelectionParentNode(),"," parentNodeName,"," parentClass,"," selection;",""," // If the event fires without a parent node for the selection, ignore the whole thing."," if ((typeof node === 'undefined') || (node === null) || (node === false) ||"," (typeof node.parentNode === 'undefined') || (node.parentNode === null)) {"," return;"," }",""," parentNodeName = node.parentNode.nodeName;"," parentClass = node.parentNode.hasAttribute('class') ? node.parentNode.getAttribute('class') : '';"," if ((typeof parentNodeName !== 'undefined') && (parentNodeName !== null) && (parentClass !== '') &&"," (parentNodeName === 'SPAN') && (parentClass.indexOf(CLASSES.TAG) !== -1)) {"," selection = host.getSelectionFromNode(Y.one(node));"," host.setSelection(selection);"," }"," },",""," /**"," * When submitting the form, this function is invoked to clean the highlighting html code."," *"," * @method _cleanMlangTags"," * @private"," */"," _cleanMlangTags: function() {"," if (this._highlight) {"," this.editor.setHTML(this._getHTMLwithCleanedTags(this.editor.getHTML()));"," this.markUpdated();"," }"," },",""," /**"," * Adds the tags to the {mlang} tags if highlighting is enable."," *"," * Instead of taking the HTML directly from the textarea, we have to"," * retrieve it, first, without the tags that can be stored"," * in database, due to a bug in version 2015120501 that stores the"," * {mlang} tags in database, with the tags."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," *"," * Every different {mlang} tag has to be replaced only once, otherwise,"," * nested s will be created in every repeated replacement. So, we"," * need to track which replacements have been made."," *"," * @method _highlightMlangTags"," * @private"," */"," _highlightMlangTags: function() {"," var editorHTML,"," regularExpression,"," mlangtags,"," mlangtag,"," index,"," highlightedmlangtag,"," replacementsmade = [],"," notreplacedyet;"," if (this._highlight) {"," editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML());",""," regularExpression = new RegExp('{mlang.*?}', 'g');"," mlangtags = editorHTML.match(regularExpression);"," if (mlangtags !== null) {"," for (index = 0; index < mlangtags.length; index++) {"," mlangtag = mlangtags[index];",""," notreplacedyet = replacementsmade.indexOf(mlangtag) === -1;"," if (notreplacedyet) {"," replacementsmade.push(mlangtag);"," highlightedmlangtag = OPENING_SPAN + mlangtag + CLOSING_SPAN;"," regularExpression = new RegExp(mlangtag, 'g');"," editorHTML = editorHTML.replace(regularExpression, highlightedmlangtag);"," }"," }",""," this.editor.setHTML(editorHTML);"," }",""," this.markUpdated();"," }"," },",""," /**"," * This function returns the HTML passed in as parameter, but cleaning every multilang"," * tag around the {mlang} tags. This is necessary for decorating tags on"," * init, because it could happen that in database are stored the {mlang} tags with"," * their tags, due to a bug in version 2015120501."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," * Implementation based on code from EditorClean._clearSpans()"," *"," * @method _getHTMLwithCleanedTags"," * @param {string} content The to be cleaned."," * @return {string} HTML in editor, without any around {mlang} tags."," */"," _getHTMLwithCleanedTags: function(content) {"," // This is better to run detached from the DOM, so the browser doesn't try to update on each change."," var holder = document.createElement('div'),"," spans,"," spansarr;",""," holder.innerHTML = content;"," spans = holder.getElementsByTagName('span');",""," // Since we will be removing elements from the list, we should copy it to an array, making it static."," spansarr = Array.prototype.slice.call(spans, 0);",""," spansarr.forEach(function(span) {"," if (span.className.indexOf(CLASSES.TAG) !== -1) {"," // Move each child (if they exist) to the parent in place of this span."," while (span.firstChild) {"," span.parentNode.insertBefore(span.firstChild, span);"," }",""," // Remove the now empty span."," span.parentNode.removeChild(span);"," }"," });",""," return holder.innerHTML;"," }","","}, {"," ATTRS: {"," /**"," * The list of installed languages."," *"," * @attribute languages"," * @type array"," * @default {\"en\":\"English (en)\"}"," */"," languages: DEFAULT_LANGUAGE,",""," /**"," * If the current user has the capability to use the plugin."," *"," * @attribute capability"," * @type boolean"," * @default true"," */"," capability: DEFAULT_CAPABILITY,",""," /**"," * If the {mlang} tags have to be highlighted or not."," *"," * @property highlight"," * @type boolean"," * @default true"," */"," highlight: DEFAULT_HIGHLIGHT,",""," /**"," * The CSS for delimiters."," *"," * @property css"," * @type string"," * @default DEFAULT_CSS"," */"," css: DEFAULT_CSS"," }","});","","","}, '@VERSION@', {\"requires\": [\"moodle-editor_atto-plugin\"]});","","}());"]}; +} +var __cov_xlcucJyua6odXhLRctX6xg = __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']; +__cov_xlcucJyua6odXhLRctX6xg.s['1']++;YUI.add('moodle-atto_multilang2-button',function(Y,NAME){__cov_xlcucJyua6odXhLRctX6xg.f['1']++;__cov_xlcucJyua6odXhLRctX6xg.s['2']++;var CLASSES={TAG:'filter-multilang-tag'},LANG_WILDCARD='%lang',CONTENT_WILDCARD='%content',ATTR_LANGUAGES='languages',ATTR_CAPABILITY='capability',ATTR_HIGHLIGHT='highlight',ATTR_CSS='css',DEFAULT_LANGUAGE='{"en":"English (en)"}',DEFAULT_CAPABILITY=true,DEFAULT_HIGHLIGHT=true,DEFAULT_CSS='outline: 1px dotted;'+'padding: 0.1em;'+'margin: 0em 0.1em;'+'background-color: #ffffaa;',OPENING_SPAN='',CLOSING_SPAN='',TEMPLATES={SPANNED:' '+OPENING_SPAN+'{mlang '+LANG_WILDCARD+'}'+CLOSING_SPAN+CONTENT_WILDCARD+OPENING_SPAN+'{mlang}'+CLOSING_SPAN+' ',NOT_SPANNED:'{mlang '+LANG_WILDCARD+'}'+CONTENT_WILDCARD+'{mlang}'};__cov_xlcucJyua6odXhLRctX6xg.s['3']++;Y.namespace('M.atto_multilang2').Button=Y.Base.create('button',Y.M.editor_atto.EditorPlugin,[],{_highlight:true,initializer:function(){__cov_xlcucJyua6odXhLRctX6xg.f['2']++;__cov_xlcucJyua6odXhLRctX6xg.s['4']++;var hascapability=this.get(ATTR_CAPABILITY),toolbarItems,host,form;__cov_xlcucJyua6odXhLRctX6xg.s['5']++;if(hascapability){__cov_xlcucJyua6odXhLRctX6xg.b['1'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['6']++;toolbarItems=this._initializeToolbarItems();__cov_xlcucJyua6odXhLRctX6xg.s['7']++;this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:'icon',iconComponent:'atto_multilang2',items:toolbarItems});__cov_xlcucJyua6odXhLRctX6xg.s['8']++;this._tagTemplate=TEMPLATES.NOT_SPANNED;__cov_xlcucJyua6odXhLRctX6xg.s['9']++;this._highlight=this.get(ATTR_HIGHLIGHT);__cov_xlcucJyua6odXhLRctX6xg.s['10']++;if(this._highlight){__cov_xlcucJyua6odXhLRctX6xg.b['2'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['11']++;this._tagTemplate=TEMPLATES.SPANNED;__cov_xlcucJyua6odXhLRctX6xg.s['12']++;host=this.get('host');__cov_xlcucJyua6odXhLRctX6xg.s['13']++;form=host.textarea.ancestor('form');__cov_xlcucJyua6odXhLRctX6xg.s['14']++;if(form){__cov_xlcucJyua6odXhLRctX6xg.b['3'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['15']++;form.on('submit',this._cleanMlangTags,this);}else{__cov_xlcucJyua6odXhLRctX6xg.b['3'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['16']++;this.get('host').on('atto:selectionchanged',this._checkSelectionChange,this);__cov_xlcucJyua6odXhLRctX6xg.s['17']++;this.get('host').on('pluginsloaded',this._addHighlightingCss,this);__cov_xlcucJyua6odXhLRctX6xg.s['18']++;this.get('host').on('pluginsloaded',this._highlightMlangTags,this);__cov_xlcucJyua6odXhLRctX6xg.s['19']++;this._hookUpdateOriginal();__cov_xlcucJyua6odXhLRctX6xg.s['20']++;this._hookUpdateFromTextArea();}else{__cov_xlcucJyua6odXhLRctX6xg.b['2'][1]++;}}else{__cov_xlcucJyua6odXhLRctX6xg.b['1'][1]++;}},_initializeToolbarItems:function(){__cov_xlcucJyua6odXhLRctX6xg.f['3']++;__cov_xlcucJyua6odXhLRctX6xg.s['21']++;var toolbarItems=[],languages,langCode;__cov_xlcucJyua6odXhLRctX6xg.s['22']++;languages=JSON.parse(this.get(ATTR_LANGUAGES));__cov_xlcucJyua6odXhLRctX6xg.s['23']++;for(langCode in languages){__cov_xlcucJyua6odXhLRctX6xg.s['24']++;if(languages.hasOwnProperty(langCode)){__cov_xlcucJyua6odXhLRctX6xg.b['4'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['25']++;toolbarItems.push({text:languages[langCode],callbackArgs:langCode});}else{__cov_xlcucJyua6odXhLRctX6xg.b['4'][1]++;}}__cov_xlcucJyua6odXhLRctX6xg.s['26']++;return toolbarItems;},_addHighlightingCss:function(){__cov_xlcucJyua6odXhLRctX6xg.f['4']++;__cov_xlcucJyua6odXhLRctX6xg.s['27']++;var css='.'+CLASSES.TAG+' {'+this.get(ATTR_CSS)+'}',style;__cov_xlcucJyua6odXhLRctX6xg.s['28']++;style=document.createElement('style');__cov_xlcucJyua6odXhLRctX6xg.s['29']++;style.type='text/css';__cov_xlcucJyua6odXhLRctX6xg.s['30']++;style.innerHTML=css;__cov_xlcucJyua6odXhLRctX6xg.s['31']++;document.head.appendChild(style);},_hookUpdateOriginal:function(){__cov_xlcucJyua6odXhLRctX6xg.f['5']++;__cov_xlcucJyua6odXhLRctX6xg.s['32']++;var host=this.get('host'),multilangplugin=this;__cov_xlcucJyua6odXhLRctX6xg.s['33']++;host.updateOriginal=function(){__cov_xlcucJyua6odXhLRctX6xg.f['6']++;__cov_xlcucJyua6odXhLRctX6xg.s['34']++;var _updateOriginal=host.updateOriginal;__cov_xlcucJyua6odXhLRctX6xg.s['35']++;return function(){__cov_xlcucJyua6odXhLRctX6xg.f['7']++;__cov_xlcucJyua6odXhLRctX6xg.s['36']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['6'][0]++,multilangplugin._highlight)&&(__cov_xlcucJyua6odXhLRctX6xg.b['6'][1]++,this.updateOriginal.caller===host.plugins.html._showHTML)){__cov_xlcucJyua6odXhLRctX6xg.b['5'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['37']++;multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML()));}else{__cov_xlcucJyua6odXhLRctX6xg.b['5'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['38']++;return _updateOriginal.apply(this,arguments);};}();},_hookUpdateFromTextArea:function(){__cov_xlcucJyua6odXhLRctX6xg.f['8']++;__cov_xlcucJyua6odXhLRctX6xg.s['39']++;var host=this.get('host'),multilangplugin=this;__cov_xlcucJyua6odXhLRctX6xg.s['40']++;host.updateFromTextArea=function(){__cov_xlcucJyua6odXhLRctX6xg.f['9']++;__cov_xlcucJyua6odXhLRctX6xg.s['41']++;var _updateFromTextArea=host.updateFromTextArea;__cov_xlcucJyua6odXhLRctX6xg.s['42']++;return function(){__cov_xlcucJyua6odXhLRctX6xg.f['10']++;__cov_xlcucJyua6odXhLRctX6xg.s['43']++;var ret=_updateFromTextArea.apply(this,arguments);__cov_xlcucJyua6odXhLRctX6xg.s['44']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['8'][0]++,multilangplugin._highlight)&&(__cov_xlcucJyua6odXhLRctX6xg.b['8'][1]++,this.updateFromTextArea.caller===host.plugins.html._showHTML)){__cov_xlcucJyua6odXhLRctX6xg.b['7'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['45']++;multilangplugin._highlightMlangTags();}else{__cov_xlcucJyua6odXhLRctX6xg.b['7'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['46']++;return ret;};}();},_addTags:function(event,langCode){__cov_xlcucJyua6odXhLRctX6xg.f['11']++;__cov_xlcucJyua6odXhLRctX6xg.s['47']++;var selection,host=this.get('host'),taggedContent,content;__cov_xlcucJyua6odXhLRctX6xg.s['48']++;taggedContent=this._tagTemplate;__cov_xlcucJyua6odXhLRctX6xg.s['49']++;selection=this._getSelectionHTML();__cov_xlcucJyua6odXhLRctX6xg.s['50']++;content=host.getSelection().toString().length===0?(__cov_xlcucJyua6odXhLRctX6xg.b['9'][0]++,' '):(__cov_xlcucJyua6odXhLRctX6xg.b['9'][1]++,selection);__cov_xlcucJyua6odXhLRctX6xg.s['51']++;taggedContent=taggedContent.replace(LANG_WILDCARD,langCode);__cov_xlcucJyua6odXhLRctX6xg.s['52']++;taggedContent=taggedContent.replace(CONTENT_WILDCARD,content);__cov_xlcucJyua6odXhLRctX6xg.s['53']++;host.insertContentAtFocusPoint(taggedContent);__cov_xlcucJyua6odXhLRctX6xg.s['54']++;this.markUpdated();},_getSelectionHTML:function(){__cov_xlcucJyua6odXhLRctX6xg.f['12']++;__cov_xlcucJyua6odXhLRctX6xg.s['55']++;var html='',selection,container,index,length;__cov_xlcucJyua6odXhLRctX6xg.s['56']++;if(typeof window.getSelection!=='undefined'){__cov_xlcucJyua6odXhLRctX6xg.b['10'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['57']++;selection=window.getSelection();__cov_xlcucJyua6odXhLRctX6xg.s['58']++;if(selection.rangeCount){__cov_xlcucJyua6odXhLRctX6xg.b['11'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['59']++;container=document.createElement('div');__cov_xlcucJyua6odXhLRctX6xg.s['60']++;for(index=0,length=selection.rangeCount;index',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller===e.plugins.html._showHTML&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var r=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller===e.plugins.html._showHTML&&t._highlightMlangTags(),r}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller===e.plugins.html._showHTML&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var r=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller===e.plugins.html._showHTML&&t._highlightMlangTags(),r}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r Date: Fri, 27 Oct 2017 22:01:09 +0200 Subject: [PATCH 11/11] Update README and version.php for v1.9 release --- README.md | 8 ++++++-- version.php | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a26524a..16fc65e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Atto multilanguage plugin ========================= -![Release](https://img.shields.io/badge/release-v1.8-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3-green.svg) +![Release](https://img.shields.io/badge/release-v1.9-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3%2C%203.4-green.svg) This plugin will make the creation of multilingual contents on Moodle much more easier with Atto editor. @@ -9,8 +9,12 @@ The plugin is developed to work with [Iñaki Arenaza's multilang2 filter](https: ## Current version -The latest release is v1.8 (build 2017100200) for Moodle 2.9, 3.0, 3.1, 3.2 and 3.3 (Checkout [v2.9.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v2.9.1.8), [v3.0.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.0.1.8), [v3.1.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.1.1.8), [v3.2.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.2.1.8) and [v3.3.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.3.1.8) releases, respectively. +The latest release is v1.9 (build 2017102700) for Moodle 2.9, 3.0, 3.1, 3.2, 3.4 and 3.4 (Checkout [v2.9.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v2.9.1.9), [v3.0.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.0.1.9), [v3.1.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.1.1.9), [v3.2.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.2.1.9) and [v3.3.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.3.1.9) releases, respectively. +## Changes from v1.8 + - Added Grunt support files. No we can check Moodle Javascript coding guidelines and minify the plugin code without installing the plugins. + - Minor Javascript coding guidelines fix (signalled by the minifier, but ignored by the linter!) + ## Changes from v1.7 - Multiple bug fixes, especially when there were more than one Atto editor in the same page. Closes issues #2, #18, #21. - Multilang tag highlighting 's are now removed/added when switching to/from HTML view, reducing clutter when editing the HTML code. diff --git a/version.php b/version.php index aedab38..5050629 100644 --- a/version.php +++ b/version.php @@ -24,8 +24,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 'v3.3.1.8 (version v1.8 for Moodle 3.3) (2017100200)'; +$plugin->version = 2017102700; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 'v3.3.1.9 (version v1.9 for Moodle 3.3) (2017102700)'; $plugin->requires = 2015051100; // Required Moodle version. $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE;