├── yui ├── src │ └── button │ │ ├── meta │ │ └── button.json │ │ ├── build.json │ │ └── js │ │ └── button.js └── build │ └── moodle-atto_multilang2-button │ ├── moodle-atto_multilang2-button-min.js │ ├── moodle-atto_multilang2-button.js │ ├── moodle-atto_multilang2-button-debug.js │ └── moodle-atto_multilang2-button-coverage.js ├── pix └── icon.svg ├── default-css.php ├── tests └── behat │ └── multilang2.feature ├── db └── access.php ├── lang ├── eu │ └── atto_multilang2.php ├── en │ └── atto_multilang2.php └── es │ └── atto_multilang2.php ├── settings.php ├── version.php ├── README.md └── lib.php /yui/src/button/meta/button.json: -------------------------------------------------------------------------------- 1 | { 2 | "moodle-atto_multilang2-button": { 3 | "requires": [ 4 | "moodle-editor_atto-plugin" 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /yui/src/button/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moodle-atto_multilang2-button", 3 | "builds": { 4 | "moodle-atto_multilang2-button": { 5 | "jsfiles": [ 6 | "button.js" 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /pix/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /default-css.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Multi-language default CSS settings. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $multilang2_default_css = <<< EOF 26 | 27 | outline: 1px dotted; 28 | padding: 0.1em; 29 | margin: 0em 0.1em; 30 | background-color: #ffffaa; 31 | 32 | EOF 33 | ; 34 | -------------------------------------------------------------------------------- /tests/behat/multilang2.feature: -------------------------------------------------------------------------------- 1 | @editor @editor_atto @atto @atto_multilang2 2 | Feature: Atto multilanguage list 3 | To write multilingual text in Atto, I need to use multi-language content button. 4 | 5 | @javascript 6 | Scenario: Tag some text with multilang labels 7 | Given I log in as "admin" 8 | And the following config values are set as admin: 9 | | toolbar | multilang2 = multilang2, table | editor_atto | # Needed table button, otherwise multilang list doesn't spread out... 10 | And I am on homepage 11 | And I follow "Profile" in the user menu 12 | And I follow "Edit profile" 13 | And I set the field "Description" to "Multilingual content" 14 | And I select the text in the "Description" Atto editor 15 | When I click on "Multi-Language Content (v2)" "button" 16 | When I click on "English" "link" 17 | And I press "Update profile" 18 | And I follow "Preferences" in the user menu 19 | And I follow "Editor preferences" 20 | And I set the field "Text editor" to "Plain text area" 21 | And I press "Save changes" 22 | And I follow "Edit profile" 23 | Then I should see "{mlang en}Multilingual content{mlang}" 24 | And I should not see "{mlang en}" 25 | -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | defined('MOODLE_INTERNAL') || die(); 18 | 19 | // Plugin for Moodle 'Multilingual content' drop down menu. 20 | // @package atto_multilang2 21 | // @copyright 2016 onwards Julen Pardo & Mondragon Unibertsitatea 22 | // @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. 23 | 24 | $capabilities = array( 25 | 'atto/multilang2:viewlanguagemenu' => array( 26 | 'captype' => 'read', 27 | 'contextlevel' => CONTEXT_COURSE, 28 | 'archetypes' => array( 29 | 'teacher' => CAP_ALLOW, 30 | 'manager' => CAP_ALLOW, 31 | 'editingteacher' => CAP_ALLOW, 32 | 'student' => CAP_PREVENT 33 | ), 34 | ), 35 | ); 36 | 37 | -------------------------------------------------------------------------------- /lang/eu/atto_multilang2.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Strings for 'Atto Multilang v2' plugin. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $string['pluginname'] = 'Eduki Eleanitza (v2)'; 26 | $string['highlight'] = 'Mugatzaileak nabarmendu'; 27 | $string['highlight_desc'] = 'Bisualki nabarmendu multi-hizkuntza eduki mugatzaileak (hau da, {mlang XX} eta {mlang}) editorean.'; 28 | $string['customcss'] = 'CSS mugatzaileentzat'; 29 | $string['customcss_desc'] = "
Erabilitako CSS-a multi-hizkuntza eduki mugatzaileentzat. 30 |

Kontuz: jarri soilik CSS GORPUTZAREN ARAUAK, selektoreak baztertuz, lehenetsitako balorean bezala.

"; 31 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Atto text editor multilanguage plugin settings. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2016 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require(dirname(__FILE__)) . '/default-css.php'; 28 | 29 | $settings->add(new admin_setting_configcheckbox('atto_multilang2/highlight', 30 | get_string('highlight', 'atto_multilang2'), get_string('highlight_desc', 'atto_multilang2'), 1)); 31 | $settings->add(new admin_setting_configtextarea('atto_multilang2/customcss', 32 | get_string('customcss', 'atto_multilang2'), get_string('customcss_desc', 'atto_multilang2'), 33 | $multilang2_default_css, PARAM_RAW)); 34 | -------------------------------------------------------------------------------- /lang/en/atto_multilang2.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Strings for 'Atto Multilang v2' plugin. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $string['pluginname'] = 'Multi-Language Content (v2)'; 26 | $string['highlight'] = 'Highlight delimiters'; 27 | $string['highlight_desc'] = 'Visually highlight the multi-language content delimiters (i.e., {mlang XX} and {mlang}) in the editor.'; 28 | $string['customcss'] = 'CSS for delimiters'; 29 | $string['customcss_desc'] = "
CSS used to highlight the multi-language content delimiters. 30 |

Attention: just put the CSS BODY RULES, excluding the selectors, just as it appears in the default value.

"; 31 | -------------------------------------------------------------------------------- /lang/es/atto_multilang2.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Strings for 'Atto Multilang v2' plugin. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $string['pluginname'] = 'Contenido Multi-Idioma (v2)'; 26 | $string['highlight'] = 'Destacar los delimitadores'; 27 | $string['highlight_desc'] = 'Destacar visualmente los delimitadores del contenido multi-idioma (esto es, {mlang XX} y {mlang}) en el editor.'; 28 | $string['customcss'] = 'CSS para los delimitadores'; 29 | $string['customcss_desc'] = "
CSS usado para destacar los delimitadores del contenido multi-idioma. 30 |

Atención: poner solo las EL CUERPO DE LAS REGLAS CSS, excluyendo los selectores, tal y como aparece en el valor por defecto.

"; 31 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Atto Multilingual content plugin version details. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $plugin->version = 2016052600; // The current plugin version (Date: YYYYMMDDXX). 28 | $plugin->release = 'master - Release v1.6 (Build 2016052600) for Moodle 2.9, 3.0 and 3.1'; 29 | $plugin->requires = 2015051100; // Required Moodle version. 30 | $plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics). 31 | $plugin->maturity = MATURITY_STABLE; 32 | $plugin->dependencies = array( 33 | 'filter_multilang2' => ANY_VERSION // The filter_multilang2 must be present (any version). 34 | ); 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Atto multilanguage plugin 2 | ========================= 3 | 4 | ![Release](https://img.shields.io/badge/release-v1.6-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1-green.svg) 5 | 6 | This plugin will make the creation of multilingual contents on Moodle much more easier with Atto editor. 7 | 8 | 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). 9 | 10 | ## Current version 11 | The latest release is the v1.6 (build 2016042800) for Moodle 2.9, 3.0 and 3.1. Checkout [v2.9.1.6](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v2.9.1.6), [v3.0.1.6](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.0.1.6), and [v3.1.1.6](https://github.com/julenpardo/moodle-atto_multilang2/releases/tag/v3.1.1.6) releases, respectively. 12 | 13 | ## Changes from v1.5 14 | - Fix issue "Filepicker not loading when grading assignment" (see [issue 17](https://github.com/julenpardo/moodle-atto_multilang2/issues/17)). 15 | 16 | ## Requirements 17 | As mentioned before, [filter_multilang2](https://github.com/iarenaza/moodle-filter_multilang2) is required. 18 | 19 | ## Installation 20 | 21 | - Copy repository content in *moodleroot*/lib/editor/atto/plugins. The following can be omitted: 22 | - moodle-javascript_style_checker/ 23 | - tests/ (if you're not going to test it with Behat) 24 | - .gitmodules 25 | - build.xml 26 | - Install it from Moodle. 27 | - Go to Site administration/Plugins/Text 28 | editors/Atto HTML editor/Atto toolbar settings, and add *multilang2* 29 | to the Toolbar config where you prefer. E.g. `multilang2 = multilang2` 30 | -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Atto text editor multilanguage plugin lib. 19 | * 20 | * @package atto_multilang2 21 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | /** 28 | * Set parameters for this plugin. 29 | * 30 | * @return array The JSON encoding of the installed languages. 31 | */ 32 | function atto_multilang2_params_for_js() { 33 | $languages = json_encode(get_string_manager()->get_list_of_translations()); 34 | $capability = get_capability(); 35 | $highlight = (get_config('atto_multilang2', 'highlight') === '1') ? true : false; 36 | $css = get_config('atto_multilang2', 'customcss'); 37 | 38 | return array('languages' => $languages, 39 | 'capability' => $capability, 40 | 'highlight' => $highlight, 41 | 'css' => $css); 42 | } 43 | 44 | /** 45 | * Gets the defined capability for the plugin for the current user, to decide later to show or not to show the plugin. 46 | * 47 | * @return boolean If the user has the capability to see the plugin or not. 48 | */ 49 | function get_capability() { 50 | global $COURSE; 51 | 52 | $context = context_course::instance($COURSE->id); 53 | 54 | return has_capability('atto/multilang2:viewlanguagemenu', $context); 55 | } 56 | -------------------------------------------------------------------------------- /yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-min.js: -------------------------------------------------------------------------------- 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"]}); 2 | -------------------------------------------------------------------------------- /yui/src/button/js/button.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * @package atto_multilang2 18 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 19 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 20 | */ 21 | 22 | /** 23 | * @module moodle-atto_multilang2-button 24 | */ 25 | 26 | var CLASSES = { 27 | TAG: 'filter-multilang-tag' 28 | }, 29 | 30 | LANG_WILDCARD = '%lang', 31 | CONTENT_WILDCARD = '%content', 32 | ATTR_LANGUAGES = 'languages', 33 | ATTR_CAPABILITY = 'capability', 34 | ATTR_HIGHLIGHT = 'highlight', 35 | ATTR_CSS = 'css', 36 | DEFAULT_LANGUAGE = '{"en":"English (en)"}', 37 | DEFAULT_CAPABILITY = true, 38 | DEFAULT_HIGHLIGHT = true, 39 | DEFAULT_CSS = 'outline: 1px dotted;' + 40 | 'padding: 0.1em;' + 41 | 'margin: 0em 0.1em;' + 42 | 'background-color: #ffffaa;', 43 | TEMPLATES = { 44 | SPANED: ' {mlang ' + LANG_WILDCARD + '}' + 45 | CONTENT_WILDCARD + 46 | '{mlang} ', 47 | 48 | NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' 49 | }, 50 | OPENING_SPAN = ''; 51 | 52 | /** 53 | * Atto text editor multilanguage plugin. 54 | * 55 | * @namespace M.atto_multilang2 56 | * @class button 57 | * @extends M.editor_atto.EditorPlugin 58 | */ 59 | 60 | Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { 61 | 62 | /** 63 | * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php. 64 | * 65 | * @property _highlight 66 | * @type boolean 67 | * @private 68 | */ 69 | _highlight: true, 70 | 71 | initializer: function() { 72 | var hascapability = this.get(ATTR_CAPABILITY), 73 | toolbarItems = []; 74 | 75 | if (hascapability) { 76 | toolbarItems = this._initializeToolbarItems(); 77 | this._highlight = this.get(ATTR_HIGHLIGHT); 78 | 79 | this.addToolbarMenu({ 80 | globalItemConfig: { 81 | callback: this._addTags 82 | }, 83 | icon: 'icon', 84 | iconComponent: 'atto_multilang2', 85 | items: toolbarItems 86 | }); 87 | 88 | this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); 89 | 90 | this._addDelimiterCss(); 91 | 92 | if (this._highlight) { 93 | this._decorateTagsOnInit(); 94 | this._setSubmitListeners(); 95 | } 96 | } 97 | }, 98 | 99 | /** 100 | * Adds the CSS rules for the delimiters, received as parameter from lib.php. 101 | * 102 | * @method _addDelimiterCss 103 | * @private 104 | */ 105 | _addDelimiterCss: function() { 106 | var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', 107 | style; 108 | 109 | style = document.createElement('style'); 110 | style.type = 'text/css'; 111 | style.innerHTML = css; 112 | 113 | document.head.appendChild(style); 114 | }, 115 | 116 | /** 117 | * Initializes the toolbar items, which will be the installed languages, 118 | * received as parameter. 119 | * 120 | * @method _initializeToolbarItems 121 | * @private 122 | * @return {Array} installed language strings 123 | */ 124 | _initializeToolbarItems: function() { 125 | var toolbarItems = [], 126 | languages, 127 | langCode; 128 | 129 | languages = JSON.parse(this.get(ATTR_LANGUAGES)); 130 | 131 | for (langCode in languages) { 132 | if (languages.hasOwnProperty(langCode)) { 133 | toolbarItems.push({ 134 | text: languages[langCode], 135 | callbackArgs: langCode 136 | }); 137 | } 138 | } 139 | 140 | return toolbarItems; 141 | }, 142 | 143 | /** 144 | * Retrieves the selected text, wraps it with the multilang tags, 145 | * and replaces the selected text in the editor with with it. 146 | * 147 | * If the 'highlight' setting is checked, the {mlang} will be wrapped between 148 | * the tags with the class for the CSS highlight; if not, they will not 149 | * be wrapped. 150 | * 151 | * If there is no content selected, a " " will be inserted; otherwhise, 152 | * it's impossible to place the cursor inside the {mlang} tags. 153 | * 154 | * @method _addTags 155 | * @param {EventFacade} event 156 | * @param {string} langCode the language code 157 | * @private 158 | */ 159 | _addTags: function(event, langCode) { 160 | var selection, 161 | host = this.get('host'), 162 | taggedContent, 163 | content; 164 | 165 | taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; 166 | 167 | selection = this._getSelectionHTML(); 168 | content = (host.getSelection().toString().length === 0) ? ' ' : selection; 169 | 170 | taggedContent = taggedContent.replace(LANG_WILDCARD, langCode); 171 | taggedContent = taggedContent.replace(CONTENT_WILDCARD, content); 172 | 173 | host.insertContentAtFocusPoint(taggedContent); 174 | 175 | this.markUpdated(); 176 | }, 177 | 178 | /** 179 | * Retrieves selected text with its HTML. 180 | * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 181 | * 182 | * @method _getSelectionHTML 183 | * @private 184 | * @return {string} selected text's html; empty if nothing selected 185 | */ 186 | _getSelectionHTML: function() { 187 | var html = '', 188 | selection, 189 | container, 190 | index, 191 | lenght; 192 | 193 | if (typeof window.getSelection !== 'undefined') { 194 | selection = window.getSelection(); 195 | 196 | if (selection.rangeCount) { 197 | container = document.createElement('div'); 198 | for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { 199 | container.appendChild(selection.getRangeAt(index).cloneContents()); 200 | } 201 | html = container.innerHTML; 202 | } 203 | 204 | } else if (typeof document.selection !== 'undefined') { 205 | if (document.selection.type === 'Text') { 206 | html = document.selection.createRange().htmlText; 207 | } 208 | } 209 | 210 | return html; 211 | }, 212 | 213 | /** 214 | * Listens to every change of the text cursor in the text area. If the 215 | * cursor is placed within a multilang tag, the whole tag is selected. 216 | * 217 | * @method _checkSelectionChange 218 | * @private 219 | */ 220 | _checkSelectionChange: function() { 221 | var host = this.get('host'), 222 | node = host.getSelectionParentNode(), 223 | nodeValue = Y.one(node).get('text'), 224 | isTextNode, 225 | isLangTag; 226 | 227 | isTextNode = Y.one(node).toString().indexOf('#text') > - 1; 228 | isLangTag = (nodeValue.match(/\{mlang/g).length === 1); 229 | 230 | if (isTextNode && isLangTag) { 231 | host.setSelection(host.getSelectionFromNode(Y.one(node))); 232 | } 233 | }, 234 | 235 | /** 236 | * Retrieves the inputs of type submit, and, for each element, calls the function 237 | * that sets the submit listener. Is not made in this function because there is 238 | * not any (apparent) way to access class scope from YUI closure. 239 | * 240 | * @method _setSubmitListeners 241 | * @private 242 | */ 243 | _setSubmitListeners: function() { 244 | var submitButtons = Y.all('input[type=submit]'); 245 | 246 | submitButtons.each(this._addListenerToSubmitButtons, this); 247 | }, 248 | 249 | /** 250 | * Adds the clean tags submit listener of each input[type="submit"], but only if 251 | * it's not 'cancel' type, and if its parent form is of 'mform' class, because there 252 | * may be any other submit type (such us administrator's search button). 253 | * 254 | * @method _addListenerToSubmitButtons 255 | * @param {Node} buttonNode 256 | * @private 257 | */ 258 | _addListenerToSubmitButtons: function(buttonNode) { 259 | var buttonObject, 260 | className, 261 | parentFormClassName, 262 | notCancelButton, 263 | notSearchButton; 264 | 265 | buttonObject = document.getElementById(buttonNode.get('id')); 266 | 267 | if (buttonObject !== null) { 268 | className = buttonObject.className; 269 | parentFormClassName = buttonObject.form.className; 270 | 271 | notCancelButton = className.match(/btn-cancel/g) === null; 272 | notSearchButton = parentFormClassName.match(/mform/g).length > 0; 273 | 274 | if (notCancelButton && notSearchButton) { 275 | buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); 276 | } 277 | } 278 | }, 279 | 280 | /** 281 | * When submit button clicked, this function is invoked. It has to stop the submission, 282 | * in order to process the textarea to clean the tags. 283 | * 284 | * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, 285 | * an then simulates the click, to submit the form. 286 | * 287 | * @method _cleanTagsOnSubmit 288 | * @param {EventFacade} event 289 | * @param {Node} submitButton 290 | * @private 291 | */ 292 | _cleanTagsOnSubmit: function(event, submitButton) { 293 | event.preventDefault(); 294 | 295 | this._cleanTagsWithNoYuiId(); 296 | this._cleanTagsWithYuiId(); 297 | 298 | submitButton.detach('click', this._cleanTagsOnSubmit); 299 | submitButton.simulate('click'); 300 | }, 301 | 302 | /** 303 | * Cleans the tags around the {mlang} tags when the form is submitted, 304 | * that do not have "id" attribute. 305 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 306 | * regular expression. 307 | * 308 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 309 | * the textareas by the class name. If there is only one, the object will be only the 310 | * reference, but, if there are more, we will have an array. So, the easiest way is to 311 | * check if what we have is an array, and if it not, create it manually, and iterate it 312 | * later. 313 | * 314 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 315 | * instead of the id string, due to problems with special characters. 316 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 317 | * 318 | * @method _cleanTagsWithNoYuiId 319 | * @private 320 | */ 321 | _cleanTagsWithNoYuiId: function() { 322 | var textareas = Y.all('.editor_atto_content'), 323 | textarea, 324 | textareaIndex, 325 | innerHTML, 326 | spanedmlangtags, 327 | spanedmlangtag, 328 | index, 329 | cleanmlangtag, 330 | regularExpression; 331 | 332 | regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); 333 | 334 | if (!textareas instanceof Array) { 335 | textarea = textareas; 336 | textareas = []; 337 | textareas[0] = textarea; 338 | } 339 | 340 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 341 | textarea = textareas._nodes[textareaIndex].id; 342 | textarea = Y.one(document.getElementById(textarea)); 343 | 344 | innerHTML = textarea.get('innerHTML'); 345 | 346 | spanedmlangtags = innerHTML.match(regularExpression); 347 | 348 | if (spanedmlangtags === null) { 349 | continue; 350 | } 351 | 352 | for (index = 0; index < spanedmlangtags.length; index++) { 353 | spanedmlangtag = spanedmlangtags[index]; 354 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 355 | 356 | cleanmlangtag = cleanmlangtag.replace('', ''); 357 | 358 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 359 | } 360 | 361 | textarea.set('innerHTML', innerHTML); 362 | } 363 | 364 | this.markUpdated(); 365 | }, 366 | 367 | /** 368 | * Cleans the tags around the {mlang} tags when the form is submitted, 369 | * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. 370 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 371 | * regular expression. 372 | * 373 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 374 | * the textareas by the class name. If there is only one, the object will be only the 375 | * reference, but, if there are more, we will have an array. So, the easiest way is to 376 | * check if what we have is an array, and if it not, create it manually, and iterate it 377 | * later. 378 | * 379 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 380 | * instead of the id string, due to problems with special characters. 381 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 382 | * 383 | * @method anTagsWithYuiId 384 | * @private 385 | */ 386 | _cleanTagsWithYuiId: function() { 387 | var textareas = Y.all('.editor_atto_content'), 388 | textarea, 389 | textareaIndex, 390 | innerHTML, 391 | spanedmlangtag, 392 | index, 393 | cleanmlangtag, 394 | regularExpression, 395 | openingspanwithyui, 396 | spanedmlangtagsdwithyui, 397 | mlangtag; 398 | 399 | openingspanwithyui = OPENING_SPAN.replace('', 'g'); 401 | 402 | if (!textareas instanceof Array) { 403 | textarea = textareas; 404 | textareas = []; 405 | textareas[0] = textarea; 406 | } 407 | 408 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 409 | textarea = textareas._nodes[textareaIndex].id; 410 | textarea = Y.one(document.getElementById(textarea)); 411 | 412 | innerHTML = textarea.get('innerHTML'); 413 | 414 | spanedmlangtagsdwithyui = innerHTML.match(regularExpression); 415 | 416 | if (spanedmlangtagsdwithyui === null) { 417 | continue; 418 | } 419 | 420 | for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { 421 | spanedmlangtag = spanedmlangtagsdwithyui[index]; 422 | mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; 423 | 424 | cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); 425 | cleanmlangtag = cleanmlangtag.replace('', ''); 426 | 427 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 428 | } 429 | 430 | textarea.set('innerHTML', innerHTML); 431 | 432 | this.markUpdated(); 433 | } 434 | }, 435 | 436 | /** 437 | * Adds the tags to the {mlang} tags when the editor is loaded. 438 | * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, 439 | * I don't know. But, if we call it after setting the HTML, the {mlang} 440 | * tags flicker with the decoration, and returns to their original state. 441 | * 442 | * Instead of taking the HTML directly from the textarea, we have to 443 | * retrieve it, first, without the tags that can be stored 444 | * in database, due to a bug in version 2015120501 that stores the 445 | * {mlang} tags in database, with the tags. 446 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 447 | * 448 | * Every different {mlang} tag has to be replaced only once, otherwise, 449 | * nested s will be created in every repeated replacement. So, we 450 | * have to have a track of which replacements have been made. 451 | * 452 | * @method _decorateTagsOnInit 453 | * @private 454 | */ 455 | _decorateTagsOnInit: function() { 456 | var textarea = Y.all('.editor_atto_content'), 457 | innerHTML, 458 | regularExpression, 459 | mlangtags, 460 | mlangtag, 461 | index, 462 | decoratedmlangtag, 463 | replacementsmade = [], 464 | notreplacedyet; 465 | 466 | innerHTML = this._getHTMLwithCleanedTags(); 467 | 468 | regularExpression = new RegExp('{mlang.*?}', 'g'); 469 | mlangtags = innerHTML.match(regularExpression); 470 | 471 | if (mlangtags !== null) { 472 | for (index = 0; index < mlangtags.length; index++) { 473 | mlangtag = mlangtags[index]; 474 | 475 | notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; 476 | 477 | if (notreplacedyet) { 478 | replacementsmade.push(mlangtag); 479 | 480 | decoratedmlangtag = OPENING_SPAN + mlangtag + ''; 481 | regularExpression = new RegExp(mlangtag, 'g'); 482 | 483 | innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); 484 | } 485 | } 486 | 487 | textarea.set('innerHTML', innerHTML); 488 | } 489 | 490 | }, 491 | 492 | /** 493 | * This function returns the HTML as it is in the textarea, but cleaning every 494 | * tag around the {mlang} tags. This is necessary for decorating tags on 495 | * init, because it could happen that in database are stored the {mlang} tags with 496 | * their tags, due to a bug in version 2015120501. 497 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 498 | * 499 | * @method _getHTMLwithCleanedTags 500 | * @return {string} HTML in textarea, without any around {mlang} tags 501 | */ 502 | _getHTMLwithCleanedTags: function() { 503 | var host = this.get('host'), 504 | innerHTML = host.getCleanHTML(), 505 | regexString, 506 | regularExpression, 507 | spanedmlangtags, 508 | spanedmlangtag, 509 | cleanmlangtag, 510 | index; 511 | 512 | regexString = OPENING_SPAN + '.*?' + ''; 513 | regularExpression = new RegExp(regexString, 'g'); 514 | spanedmlangtags = innerHTML.match(regularExpression); 515 | 516 | if (spanedmlangtags !== null) { 517 | for (index = 0; index < spanedmlangtags.length; index++) { 518 | spanedmlangtag = spanedmlangtags[index]; 519 | 520 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 521 | cleanmlangtag = cleanmlangtag.replace('', ''); 522 | 523 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 524 | } 525 | } 526 | 527 | return innerHTML; 528 | } 529 | 530 | }, { 531 | ATTRS: { 532 | /** 533 | * The list of installed languages. 534 | * 535 | * @attribute languages 536 | * @type array 537 | * @default {"en":"English (en)"} 538 | */ 539 | languages: DEFAULT_LANGUAGE, 540 | 541 | /** 542 | * If the current user has the capability to use the plugin. 543 | * 544 | * @attribute capability 545 | * @type boolean 546 | * @default true 547 | */ 548 | capability: DEFAULT_CAPABILITY, 549 | 550 | /** 551 | * If the {mlang} tags have to be highlighted or not. 552 | * 553 | * @property highlight 554 | * @type boolean 555 | * @default true 556 | */ 557 | highlight: DEFAULT_HIGHLIGHT, 558 | 559 | /** 560 | * The CSS for delimiters. 561 | * 562 | * @property css 563 | * @type string 564 | * @default DEFAULT_CSS 565 | */ 566 | css: DEFAULT_CSS 567 | } 568 | }); 569 | -------------------------------------------------------------------------------- /yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js: -------------------------------------------------------------------------------- 1 | YUI.add('moodle-atto_multilang2-button', function (Y, NAME) { 2 | 3 | // This file is part of Moodle - http://moodle.org/ 4 | // 5 | // Moodle is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Moodle is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Moodle. If not, see . 17 | 18 | /** 19 | * @package atto_multilang2 20 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 | */ 23 | 24 | /** 25 | * @module moodle-atto_multilang2-button 26 | */ 27 | 28 | var CLASSES = { 29 | TAG: 'filter-multilang-tag' 30 | }, 31 | 32 | LANG_WILDCARD = '%lang', 33 | CONTENT_WILDCARD = '%content', 34 | ATTR_LANGUAGES = 'languages', 35 | ATTR_CAPABILITY = 'capability', 36 | ATTR_HIGHLIGHT = 'highlight', 37 | ATTR_CSS = 'css', 38 | DEFAULT_LANGUAGE = '{"en":"English (en)"}', 39 | DEFAULT_CAPABILITY = true, 40 | DEFAULT_HIGHLIGHT = true, 41 | DEFAULT_CSS = 'outline: 1px dotted;' + 42 | 'padding: 0.1em;' + 43 | 'margin: 0em 0.1em;' + 44 | 'background-color: #ffffaa;', 45 | TEMPLATES = { 46 | SPANED: ' {mlang ' + LANG_WILDCARD + '}' + 47 | CONTENT_WILDCARD + 48 | '{mlang} ', 49 | 50 | NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' 51 | }, 52 | OPENING_SPAN = ''; 53 | 54 | /** 55 | * Atto text editor multilanguage plugin. 56 | * 57 | * @namespace M.atto_multilang2 58 | * @class button 59 | * @extends M.editor_atto.EditorPlugin 60 | */ 61 | 62 | Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { 63 | 64 | /** 65 | * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php. 66 | * 67 | * @property _highlight 68 | * @type boolean 69 | * @private 70 | */ 71 | _highlight: true, 72 | 73 | initializer: function() { 74 | var hascapability = this.get(ATTR_CAPABILITY), 75 | toolbarItems = []; 76 | 77 | if (hascapability) { 78 | toolbarItems = this._initializeToolbarItems(); 79 | this._highlight = this.get(ATTR_HIGHLIGHT); 80 | 81 | this.addToolbarMenu({ 82 | globalItemConfig: { 83 | callback: this._addTags 84 | }, 85 | icon: 'icon', 86 | iconComponent: 'atto_multilang2', 87 | items: toolbarItems 88 | }); 89 | 90 | this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); 91 | 92 | this._addDelimiterCss(); 93 | 94 | if (this._highlight) { 95 | this._decorateTagsOnInit(); 96 | this._setSubmitListeners(); 97 | } 98 | } 99 | }, 100 | 101 | /** 102 | * Adds the CSS rules for the delimiters, received as parameter from lib.php. 103 | * 104 | * @method _addDelimiterCss 105 | * @private 106 | */ 107 | _addDelimiterCss: function() { 108 | var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', 109 | style; 110 | 111 | style = document.createElement('style'); 112 | style.type = 'text/css'; 113 | style.innerHTML = css; 114 | 115 | document.head.appendChild(style); 116 | }, 117 | 118 | /** 119 | * Initializes the toolbar items, which will be the installed languages, 120 | * received as parameter. 121 | * 122 | * @method _initializeToolbarItems 123 | * @private 124 | * @return {Array} installed language strings 125 | */ 126 | _initializeToolbarItems: function() { 127 | var toolbarItems = [], 128 | languages, 129 | langCode; 130 | 131 | languages = JSON.parse(this.get(ATTR_LANGUAGES)); 132 | 133 | for (langCode in languages) { 134 | if (languages.hasOwnProperty(langCode)) { 135 | toolbarItems.push({ 136 | text: languages[langCode], 137 | callbackArgs: langCode 138 | }); 139 | } 140 | } 141 | 142 | return toolbarItems; 143 | }, 144 | 145 | /** 146 | * Retrieves the selected text, wraps it with the multilang tags, 147 | * and replaces the selected text in the editor with with it. 148 | * 149 | * If the 'highlight' setting is checked, the {mlang} will be wrapped between 150 | * the tags with the class for the CSS highlight; if not, they will not 151 | * be wrapped. 152 | * 153 | * If there is no content selected, a " " will be inserted; otherwhise, 154 | * it's impossible to place the cursor inside the {mlang} tags. 155 | * 156 | * @method _addTags 157 | * @param {EventFacade} event 158 | * @param {string} langCode the language code 159 | * @private 160 | */ 161 | _addTags: function(event, langCode) { 162 | var selection, 163 | host = this.get('host'), 164 | taggedContent, 165 | content; 166 | 167 | taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; 168 | 169 | selection = this._getSelectionHTML(); 170 | content = (host.getSelection().toString().length === 0) ? ' ' : selection; 171 | 172 | taggedContent = taggedContent.replace(LANG_WILDCARD, langCode); 173 | taggedContent = taggedContent.replace(CONTENT_WILDCARD, content); 174 | 175 | host.insertContentAtFocusPoint(taggedContent); 176 | 177 | this.markUpdated(); 178 | }, 179 | 180 | /** 181 | * Retrieves selected text with its HTML. 182 | * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 183 | * 184 | * @method _getSelectionHTML 185 | * @private 186 | * @return {string} selected text's html; empty if nothing selected 187 | */ 188 | _getSelectionHTML: function() { 189 | var html = '', 190 | selection, 191 | container, 192 | index, 193 | lenght; 194 | 195 | if (typeof window.getSelection !== 'undefined') { 196 | selection = window.getSelection(); 197 | 198 | if (selection.rangeCount) { 199 | container = document.createElement('div'); 200 | for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { 201 | container.appendChild(selection.getRangeAt(index).cloneContents()); 202 | } 203 | html = container.innerHTML; 204 | } 205 | 206 | } else if (typeof document.selection !== 'undefined') { 207 | if (document.selection.type === 'Text') { 208 | html = document.selection.createRange().htmlText; 209 | } 210 | } 211 | 212 | return html; 213 | }, 214 | 215 | /** 216 | * Listens to every change of the text cursor in the text area. If the 217 | * cursor is placed within a multilang tag, the whole tag is selected. 218 | * 219 | * @method _checkSelectionChange 220 | * @private 221 | */ 222 | _checkSelectionChange: function() { 223 | var host = this.get('host'), 224 | node = host.getSelectionParentNode(), 225 | nodeValue = Y.one(node).get('text'), 226 | isTextNode, 227 | isLangTag; 228 | 229 | isTextNode = Y.one(node).toString().indexOf('#text') > - 1; 230 | isLangTag = (nodeValue.match(/\{mlang/g).length === 1); 231 | 232 | if (isTextNode && isLangTag) { 233 | host.setSelection(host.getSelectionFromNode(Y.one(node))); 234 | } 235 | }, 236 | 237 | /** 238 | * Retrieves the inputs of type submit, and, for each element, calls the function 239 | * that sets the submit listener. Is not made in this function because there is 240 | * not any (apparent) way to access class scope from YUI closure. 241 | * 242 | * @method _setSubmitListeners 243 | * @private 244 | */ 245 | _setSubmitListeners: function() { 246 | var submitButtons = Y.all('input[type=submit]'); 247 | 248 | submitButtons.each(this._addListenerToSubmitButtons, this); 249 | }, 250 | 251 | /** 252 | * Adds the clean tags submit listener of each input[type="submit"], but only if 253 | * it's not 'cancel' type, and if its parent form is of 'mform' class, because there 254 | * may be any other submit type (such us administrator's search button). 255 | * 256 | * @method _addListenerToSubmitButtons 257 | * @param {Node} buttonNode 258 | * @private 259 | */ 260 | _addListenerToSubmitButtons: function(buttonNode) { 261 | var buttonObject, 262 | className, 263 | parentFormClassName, 264 | notCancelButton, 265 | notSearchButton; 266 | 267 | buttonObject = document.getElementById(buttonNode.get('id')); 268 | 269 | if (buttonObject !== null) { 270 | className = buttonObject.className; 271 | parentFormClassName = buttonObject.form.className; 272 | 273 | notCancelButton = className.match(/btn-cancel/g) === null; 274 | notSearchButton = parentFormClassName.match(/mform/g).length > 0; 275 | 276 | if (notCancelButton && notSearchButton) { 277 | buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); 278 | } 279 | } 280 | }, 281 | 282 | /** 283 | * When submit button clicked, this function is invoked. It has to stop the submission, 284 | * in order to process the textarea to clean the tags. 285 | * 286 | * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, 287 | * an then simulates the click, to submit the form. 288 | * 289 | * @method _cleanTagsOnSubmit 290 | * @param {EventFacade} event 291 | * @param {Node} submitButton 292 | * @private 293 | */ 294 | _cleanTagsOnSubmit: function(event, submitButton) { 295 | event.preventDefault(); 296 | 297 | this._cleanTagsWithNoYuiId(); 298 | this._cleanTagsWithYuiId(); 299 | 300 | submitButton.detach('click', this._cleanTagsOnSubmit); 301 | submitButton.simulate('click'); 302 | }, 303 | 304 | /** 305 | * Cleans the tags around the {mlang} tags when the form is submitted, 306 | * that do not have "id" attribute. 307 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 308 | * regular expression. 309 | * 310 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 311 | * the textareas by the class name. If there is only one, the object will be only the 312 | * reference, but, if there are more, we will have an array. So, the easiest way is to 313 | * check if what we have is an array, and if it not, create it manually, and iterate it 314 | * later. 315 | * 316 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 317 | * instead of the id string, due to problems with special characters. 318 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 319 | * 320 | * @method _cleanTagsWithNoYuiId 321 | * @private 322 | */ 323 | _cleanTagsWithNoYuiId: function() { 324 | var textareas = Y.all('.editor_atto_content'), 325 | textarea, 326 | textareaIndex, 327 | innerHTML, 328 | spanedmlangtags, 329 | spanedmlangtag, 330 | index, 331 | cleanmlangtag, 332 | regularExpression; 333 | 334 | regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); 335 | 336 | if (!textareas instanceof Array) { 337 | textarea = textareas; 338 | textareas = []; 339 | textareas[0] = textarea; 340 | } 341 | 342 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 343 | textarea = textareas._nodes[textareaIndex].id; 344 | textarea = Y.one(document.getElementById(textarea)); 345 | 346 | innerHTML = textarea.get('innerHTML'); 347 | 348 | spanedmlangtags = innerHTML.match(regularExpression); 349 | 350 | if (spanedmlangtags === null) { 351 | continue; 352 | } 353 | 354 | for (index = 0; index < spanedmlangtags.length; index++) { 355 | spanedmlangtag = spanedmlangtags[index]; 356 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 357 | 358 | cleanmlangtag = cleanmlangtag.replace('', ''); 359 | 360 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 361 | } 362 | 363 | textarea.set('innerHTML', innerHTML); 364 | } 365 | 366 | this.markUpdated(); 367 | }, 368 | 369 | /** 370 | * Cleans the tags around the {mlang} tags when the form is submitted, 371 | * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. 372 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 373 | * regular expression. 374 | * 375 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 376 | * the textareas by the class name. If there is only one, the object will be only the 377 | * reference, but, if there are more, we will have an array. So, the easiest way is to 378 | * check if what we have is an array, and if it not, create it manually, and iterate it 379 | * later. 380 | * 381 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 382 | * instead of the id string, due to problems with special characters. 383 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 384 | * 385 | * @method anTagsWithYuiId 386 | * @private 387 | */ 388 | _cleanTagsWithYuiId: function() { 389 | var textareas = Y.all('.editor_atto_content'), 390 | textarea, 391 | textareaIndex, 392 | innerHTML, 393 | spanedmlangtag, 394 | index, 395 | cleanmlangtag, 396 | regularExpression, 397 | openingspanwithyui, 398 | spanedmlangtagsdwithyui, 399 | mlangtag; 400 | 401 | openingspanwithyui = OPENING_SPAN.replace('', 'g'); 403 | 404 | if (!textareas instanceof Array) { 405 | textarea = textareas; 406 | textareas = []; 407 | textareas[0] = textarea; 408 | } 409 | 410 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 411 | textarea = textareas._nodes[textareaIndex].id; 412 | textarea = Y.one(document.getElementById(textarea)); 413 | 414 | innerHTML = textarea.get('innerHTML'); 415 | 416 | spanedmlangtagsdwithyui = innerHTML.match(regularExpression); 417 | 418 | if (spanedmlangtagsdwithyui === null) { 419 | continue; 420 | } 421 | 422 | for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { 423 | spanedmlangtag = spanedmlangtagsdwithyui[index]; 424 | mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; 425 | 426 | cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); 427 | cleanmlangtag = cleanmlangtag.replace('', ''); 428 | 429 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 430 | } 431 | 432 | textarea.set('innerHTML', innerHTML); 433 | 434 | this.markUpdated(); 435 | } 436 | }, 437 | 438 | /** 439 | * Adds the tags to the {mlang} tags when the editor is loaded. 440 | * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, 441 | * I don't know. But, if we call it after setting the HTML, the {mlang} 442 | * tags flicker with the decoration, and returns to their original state. 443 | * 444 | * Instead of taking the HTML directly from the textarea, we have to 445 | * retrieve it, first, without the tags that can be stored 446 | * in database, due to a bug in version 2015120501 that stores the 447 | * {mlang} tags in database, with the tags. 448 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 449 | * 450 | * Every different {mlang} tag has to be replaced only once, otherwise, 451 | * nested s will be created in every repeated replacement. So, we 452 | * have to have a track of which replacements have been made. 453 | * 454 | * @method _decorateTagsOnInit 455 | * @private 456 | */ 457 | _decorateTagsOnInit: function() { 458 | var textarea = Y.all('.editor_atto_content'), 459 | innerHTML, 460 | regularExpression, 461 | mlangtags, 462 | mlangtag, 463 | index, 464 | decoratedmlangtag, 465 | replacementsmade = [], 466 | notreplacedyet; 467 | 468 | innerHTML = this._getHTMLwithCleanedTags(); 469 | 470 | regularExpression = new RegExp('{mlang.*?}', 'g'); 471 | mlangtags = innerHTML.match(regularExpression); 472 | 473 | if (mlangtags !== null) { 474 | for (index = 0; index < mlangtags.length; index++) { 475 | mlangtag = mlangtags[index]; 476 | 477 | notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; 478 | 479 | if (notreplacedyet) { 480 | replacementsmade.push(mlangtag); 481 | 482 | decoratedmlangtag = OPENING_SPAN + mlangtag + ''; 483 | regularExpression = new RegExp(mlangtag, 'g'); 484 | 485 | innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); 486 | } 487 | } 488 | 489 | textarea.set('innerHTML', innerHTML); 490 | } 491 | 492 | }, 493 | 494 | /** 495 | * This function returns the HTML as it is in the textarea, but cleaning every 496 | * tag around the {mlang} tags. This is necessary for decorating tags on 497 | * init, because it could happen that in database are stored the {mlang} tags with 498 | * their tags, due to a bug in version 2015120501. 499 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 500 | * 501 | * @method _getHTMLwithCleanedTags 502 | * @return {string} HTML in textarea, without any around {mlang} tags 503 | */ 504 | _getHTMLwithCleanedTags: function() { 505 | var host = this.get('host'), 506 | innerHTML = host.getCleanHTML(), 507 | regexString, 508 | regularExpression, 509 | spanedmlangtags, 510 | spanedmlangtag, 511 | cleanmlangtag, 512 | index; 513 | 514 | regexString = OPENING_SPAN + '.*?' + ''; 515 | regularExpression = new RegExp(regexString, 'g'); 516 | spanedmlangtags = innerHTML.match(regularExpression); 517 | 518 | if (spanedmlangtags !== null) { 519 | for (index = 0; index < spanedmlangtags.length; index++) { 520 | spanedmlangtag = spanedmlangtags[index]; 521 | 522 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 523 | cleanmlangtag = cleanmlangtag.replace('', ''); 524 | 525 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 526 | } 527 | } 528 | 529 | return innerHTML; 530 | } 531 | 532 | }, { 533 | ATTRS: { 534 | /** 535 | * The list of installed languages. 536 | * 537 | * @attribute languages 538 | * @type array 539 | * @default {"en":"English (en)"} 540 | */ 541 | languages: DEFAULT_LANGUAGE, 542 | 543 | /** 544 | * If the current user has the capability to use the plugin. 545 | * 546 | * @attribute capability 547 | * @type boolean 548 | * @default true 549 | */ 550 | capability: DEFAULT_CAPABILITY, 551 | 552 | /** 553 | * If the {mlang} tags have to be highlighted or not. 554 | * 555 | * @property highlight 556 | * @type boolean 557 | * @default true 558 | */ 559 | highlight: DEFAULT_HIGHLIGHT, 560 | 561 | /** 562 | * The CSS for delimiters. 563 | * 564 | * @property css 565 | * @type string 566 | * @default DEFAULT_CSS 567 | */ 568 | css: DEFAULT_CSS 569 | } 570 | }); 571 | 572 | 573 | }, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]}); 574 | -------------------------------------------------------------------------------- /yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-debug.js: -------------------------------------------------------------------------------- 1 | YUI.add('moodle-atto_multilang2-button', function (Y, NAME) { 2 | 3 | // This file is part of Moodle - http://moodle.org/ 4 | // 5 | // Moodle is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Moodle is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Moodle. If not, see . 17 | 18 | /** 19 | * @package atto_multilang2 20 | * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea 21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 | */ 23 | 24 | /** 25 | * @module moodle-atto_multilang2-button 26 | */ 27 | 28 | var CLASSES = { 29 | TAG: 'filter-multilang-tag' 30 | }, 31 | 32 | LANG_WILDCARD = '%lang', 33 | CONTENT_WILDCARD = '%content', 34 | ATTR_LANGUAGES = 'languages', 35 | ATTR_CAPABILITY = 'capability', 36 | ATTR_HIGHLIGHT = 'highlight', 37 | ATTR_CSS = 'css', 38 | DEFAULT_LANGUAGE = '{"en":"English (en)"}', 39 | DEFAULT_CAPABILITY = true, 40 | DEFAULT_HIGHLIGHT = true, 41 | DEFAULT_CSS = 'outline: 1px dotted;' + 42 | 'padding: 0.1em;' + 43 | 'margin: 0em 0.1em;' + 44 | 'background-color: #ffffaa;', 45 | TEMPLATES = { 46 | SPANED: ' {mlang ' + LANG_WILDCARD + '}' + 47 | CONTENT_WILDCARD + 48 | '{mlang} ', 49 | 50 | NOT_SPANED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}' 51 | }, 52 | OPENING_SPAN = ''; 53 | 54 | /** 55 | * Atto text editor multilanguage plugin. 56 | * 57 | * @namespace M.atto_multilang2 58 | * @class button 59 | * @extends M.editor_atto.EditorPlugin 60 | */ 61 | 62 | Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { 63 | 64 | /** 65 | * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php. 66 | * 67 | * @property _highlight 68 | * @type boolean 69 | * @private 70 | */ 71 | _highlight: true, 72 | 73 | initializer: function() { 74 | var hascapability = this.get(ATTR_CAPABILITY), 75 | toolbarItems = []; 76 | 77 | if (hascapability) { 78 | toolbarItems = this._initializeToolbarItems(); 79 | this._highlight = this.get(ATTR_HIGHLIGHT); 80 | 81 | this.addToolbarMenu({ 82 | globalItemConfig: { 83 | callback: this._addTags 84 | }, 85 | icon: 'icon', 86 | iconComponent: 'atto_multilang2', 87 | items: toolbarItems 88 | }); 89 | 90 | this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this); 91 | 92 | this._addDelimiterCss(); 93 | 94 | if (this._highlight) { 95 | this._decorateTagsOnInit(); 96 | this._setSubmitListeners(); 97 | } 98 | } 99 | }, 100 | 101 | /** 102 | * Adds the CSS rules for the delimiters, received as parameter from lib.php. 103 | * 104 | * @method _addDelimiterCss 105 | * @private 106 | */ 107 | _addDelimiterCss: function() { 108 | var css = '.' + CLASSES.TAG + '{' + this.get(ATTR_CSS) + '}', 109 | style; 110 | 111 | style = document.createElement('style'); 112 | style.type = 'text/css'; 113 | style.innerHTML = css; 114 | 115 | document.head.appendChild(style); 116 | }, 117 | 118 | /** 119 | * Initializes the toolbar items, which will be the installed languages, 120 | * received as parameter. 121 | * 122 | * @method _initializeToolbarItems 123 | * @private 124 | * @return {Array} installed language strings 125 | */ 126 | _initializeToolbarItems: function() { 127 | var toolbarItems = [], 128 | languages, 129 | langCode; 130 | 131 | languages = JSON.parse(this.get(ATTR_LANGUAGES)); 132 | 133 | for (langCode in languages) { 134 | if (languages.hasOwnProperty(langCode)) { 135 | toolbarItems.push({ 136 | text: languages[langCode], 137 | callbackArgs: langCode 138 | }); 139 | } 140 | } 141 | 142 | return toolbarItems; 143 | }, 144 | 145 | /** 146 | * Retrieves the selected text, wraps it with the multilang tags, 147 | * and replaces the selected text in the editor with with it. 148 | * 149 | * If the 'highlight' setting is checked, the {mlang} will be wrapped between 150 | * the tags with the class for the CSS highlight; if not, they will not 151 | * be wrapped. 152 | * 153 | * If there is no content selected, a " " will be inserted; otherwhise, 154 | * it's impossible to place the cursor inside the {mlang} tags. 155 | * 156 | * @method _addTags 157 | * @param {EventFacade} event 158 | * @param {string} langCode the language code 159 | * @private 160 | */ 161 | _addTags: function(event, langCode) { 162 | var selection, 163 | host = this.get('host'), 164 | taggedContent, 165 | content; 166 | 167 | taggedContent = (this._highlight) ? TEMPLATES.SPANED : TEMPLATES.NOT_SPANED; 168 | 169 | selection = this._getSelectionHTML(); 170 | content = (host.getSelection().toString().length === 0) ? ' ' : selection; 171 | 172 | taggedContent = taggedContent.replace(LANG_WILDCARD, langCode); 173 | taggedContent = taggedContent.replace(CONTENT_WILDCARD, content); 174 | 175 | host.insertContentAtFocusPoint(taggedContent); 176 | 177 | this.markUpdated(); 178 | }, 179 | 180 | /** 181 | * Retrieves selected text with its HTML. 182 | * Took from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234 183 | * 184 | * @method _getSelectionHTML 185 | * @private 186 | * @return {string} selected text's html; empty if nothing selected 187 | */ 188 | _getSelectionHTML: function() { 189 | var html = '', 190 | selection, 191 | container, 192 | index, 193 | lenght; 194 | 195 | if (typeof window.getSelection !== 'undefined') { 196 | selection = window.getSelection(); 197 | 198 | if (selection.rangeCount) { 199 | container = document.createElement('div'); 200 | for (index = 0, lenght = selection.rangeCount; index < lenght; ++index) { 201 | container.appendChild(selection.getRangeAt(index).cloneContents()); 202 | } 203 | html = container.innerHTML; 204 | } 205 | 206 | } else if (typeof document.selection !== 'undefined') { 207 | if (document.selection.type === 'Text') { 208 | html = document.selection.createRange().htmlText; 209 | } 210 | } 211 | 212 | return html; 213 | }, 214 | 215 | /** 216 | * Listens to every change of the text cursor in the text area. If the 217 | * cursor is placed within a multilang tag, the whole tag is selected. 218 | * 219 | * @method _checkSelectionChange 220 | * @private 221 | */ 222 | _checkSelectionChange: function() { 223 | var host = this.get('host'), 224 | node = host.getSelectionParentNode(), 225 | nodeValue = Y.one(node).get('text'), 226 | isTextNode, 227 | isLangTag; 228 | 229 | isTextNode = Y.one(node).toString().indexOf('#text') > - 1; 230 | isLangTag = (nodeValue.match(/\{mlang/g).length === 1); 231 | 232 | if (isTextNode && isLangTag) { 233 | host.setSelection(host.getSelectionFromNode(Y.one(node))); 234 | } 235 | }, 236 | 237 | /** 238 | * Retrieves the inputs of type submit, and, for each element, calls the function 239 | * that sets the submit listener. Is not made in this function because there is 240 | * not any (apparent) way to access class scope from YUI closure. 241 | * 242 | * @method _setSubmitListeners 243 | * @private 244 | */ 245 | _setSubmitListeners: function() { 246 | var submitButtons = Y.all('input[type=submit]'); 247 | 248 | submitButtons.each(this._addListenerToSubmitButtons, this); 249 | }, 250 | 251 | /** 252 | * Adds the clean tags submit listener of each input[type="submit"], but only if 253 | * it's not 'cancel' type, and if its parent form is of 'mform' class, because there 254 | * may be any other submit type (such us administrator's search button). 255 | * 256 | * @method _addListenerToSubmitButtons 257 | * @param {Node} buttonNode 258 | * @private 259 | */ 260 | _addListenerToSubmitButtons: function(buttonNode) { 261 | var buttonObject, 262 | className, 263 | parentFormClassName, 264 | notCancelButton, 265 | notSearchButton; 266 | 267 | buttonObject = document.getElementById(buttonNode.get('id')); 268 | 269 | if (buttonObject !== null) { 270 | className = buttonObject.className; 271 | parentFormClassName = buttonObject.form.className; 272 | 273 | notCancelButton = className.match(/btn-cancel/g) === null; 274 | notSearchButton = parentFormClassName.match(/mform/g).length > 0; 275 | 276 | if (notCancelButton && notSearchButton) { 277 | buttonNode.on('click', this._cleanTagsOnSubmit, this, buttonNode); 278 | } 279 | } 280 | }, 281 | 282 | /** 283 | * When submit button clicked, this function is invoked. It has to stop the submission, 284 | * in order to process the textarea to clean the tags. 285 | * 286 | * Once the textarea is cleaned, detaches this submit listener, i.e., it sets as default, 287 | * an then simulates the click, to submit the form. 288 | * 289 | * @method _cleanTagsOnSubmit 290 | * @param {EventFacade} event 291 | * @param {Node} submitButton 292 | * @private 293 | */ 294 | _cleanTagsOnSubmit: function(event, submitButton) { 295 | event.preventDefault(); 296 | 297 | this._cleanTagsWithNoYuiId(); 298 | this._cleanTagsWithYuiId(); 299 | 300 | submitButton.detach('click', this._cleanTagsOnSubmit); 301 | submitButton.simulate('click'); 302 | }, 303 | 304 | /** 305 | * Cleans the tags around the {mlang} tags when the form is submitted, 306 | * that do not have "id" attribute. 307 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 308 | * regular expression. 309 | * 310 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 311 | * the textareas by the class name. If there is only one, the object will be only the 312 | * reference, but, if there are more, we will have an array. So, the easiest way is to 313 | * check if what we have is an array, and if it not, create it manually, and iterate it 314 | * later. 315 | * 316 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 317 | * instead of the id string, due to problems with special characters. 318 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 319 | * 320 | * @method _cleanTagsWithNoYuiId 321 | * @private 322 | */ 323 | _cleanTagsWithNoYuiId: function() { 324 | var textareas = Y.all('.editor_atto_content'), 325 | textarea, 326 | textareaIndex, 327 | innerHTML, 328 | spanedmlangtags, 329 | spanedmlangtag, 330 | index, 331 | cleanmlangtag, 332 | regularExpression; 333 | 334 | regularExpression = new RegExp(OPENING_SPAN + '.*?' + '', 'g'); 335 | 336 | if (!textareas instanceof Array) { 337 | textarea = textareas; 338 | textareas = []; 339 | textareas[0] = textarea; 340 | } 341 | 342 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 343 | textarea = textareas._nodes[textareaIndex].id; 344 | textarea = Y.one(document.getElementById(textarea)); 345 | 346 | innerHTML = textarea.get('innerHTML'); 347 | 348 | spanedmlangtags = innerHTML.match(regularExpression); 349 | 350 | if (spanedmlangtags === null) { 351 | continue; 352 | } 353 | 354 | for (index = 0; index < spanedmlangtags.length; index++) { 355 | spanedmlangtag = spanedmlangtags[index]; 356 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 357 | 358 | cleanmlangtag = cleanmlangtag.replace('', ''); 359 | 360 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 361 | } 362 | 363 | textarea.set('innerHTML', innerHTML); 364 | } 365 | 366 | this.markUpdated(); 367 | }, 368 | 369 | /** 370 | * Cleans the tags around the {mlang} tags when the form is submitted, 371 | * that have "id" attribute, generated by YUI, when the cursor is placed on the tags. 372 | * The cleanup with "id" attribute and without it is made separately, to avoid an evil 373 | * regular expression. 374 | * 375 | * There may be more than one atto editor textarea in the page. So, we have to retrieve 376 | * the textareas by the class name. If there is only one, the object will be only the 377 | * reference, but, if there are more, we will have an array. So, the easiest way is to 378 | * check if what we have is an array, and if it not, create it manually, and iterate it 379 | * later. 380 | * 381 | * issue #15: the textareas are now retrieved passing to YUI selector the whole element, 382 | * instead of the id string, due to problems with special characters. 383 | * See discussion: https://moodle.org/mod/forum/discuss.php?d=332217 384 | * 385 | * @method anTagsWithYuiId 386 | * @private 387 | */ 388 | _cleanTagsWithYuiId: function() { 389 | var textareas = Y.all('.editor_atto_content'), 390 | textarea, 391 | textareaIndex, 392 | innerHTML, 393 | spanedmlangtag, 394 | index, 395 | cleanmlangtag, 396 | regularExpression, 397 | openingspanwithyui, 398 | spanedmlangtagsdwithyui, 399 | mlangtag; 400 | 401 | openingspanwithyui = OPENING_SPAN.replace('', 'g'); 403 | 404 | if (!textareas instanceof Array) { 405 | textarea = textareas; 406 | textareas = []; 407 | textareas[0] = textarea; 408 | } 409 | 410 | for (textareaIndex = 0; textareaIndex < textareas._nodes.length; textareaIndex++) { 411 | textarea = textareas._nodes[textareaIndex].id; 412 | textarea = Y.one(document.getElementById(textarea)); 413 | 414 | innerHTML = textarea.get('innerHTML'); 415 | 416 | spanedmlangtagsdwithyui = innerHTML.match(regularExpression); 417 | 418 | if (spanedmlangtagsdwithyui === null) { 419 | continue; 420 | } 421 | 422 | for (index = 0; index < spanedmlangtagsdwithyui.length; index++) { 423 | spanedmlangtag = spanedmlangtagsdwithyui[index]; 424 | mlangtag = spanedmlangtag.match(/\{mlang.*?\}/g)[0]; 425 | 426 | cleanmlangtag = spanedmlangtag.replace(regularExpression, mlangtag); 427 | cleanmlangtag = cleanmlangtag.replace('', ''); 428 | 429 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 430 | } 431 | 432 | textarea.set('innerHTML', innerHTML); 433 | 434 | this.markUpdated(); 435 | } 436 | }, 437 | 438 | /** 439 | * Adds the tags to the {mlang} tags when the editor is loaded. 440 | * In this case, we DON'T HAVE TO CALL TO markUpdated(). Why? Honestly, 441 | * I don't know. But, if we call it after setting the HTML, the {mlang} 442 | * tags flicker with the decoration, and returns to their original state. 443 | * 444 | * Instead of taking the HTML directly from the textarea, we have to 445 | * retrieve it, first, without the tags that can be stored 446 | * in database, due to a bug in version 2015120501 that stores the 447 | * {mlang} tags in database, with the tags. 448 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 449 | * 450 | * Every different {mlang} tag has to be replaced only once, otherwise, 451 | * nested s will be created in every repeated replacement. So, we 452 | * have to have a track of which replacements have been made. 453 | * 454 | * @method _decorateTagsOnInit 455 | * @private 456 | */ 457 | _decorateTagsOnInit: function() { 458 | var textarea = Y.all('.editor_atto_content'), 459 | innerHTML, 460 | regularExpression, 461 | mlangtags, 462 | mlangtag, 463 | index, 464 | decoratedmlangtag, 465 | replacementsmade = [], 466 | notreplacedyet; 467 | 468 | innerHTML = this._getHTMLwithCleanedTags(); 469 | 470 | regularExpression = new RegExp('{mlang.*?}', 'g'); 471 | mlangtags = innerHTML.match(regularExpression); 472 | 473 | if (mlangtags !== null) { 474 | for (index = 0; index < mlangtags.length; index++) { 475 | mlangtag = mlangtags[index]; 476 | 477 | notreplacedyet = replacementsmade.indexOf(mlangtag) === -1; 478 | 479 | if (notreplacedyet) { 480 | replacementsmade.push(mlangtag); 481 | 482 | decoratedmlangtag = OPENING_SPAN + mlangtag + ''; 483 | regularExpression = new RegExp(mlangtag, 'g'); 484 | 485 | innerHTML = innerHTML.replace(regularExpression, decoratedmlangtag); 486 | } 487 | } 488 | 489 | textarea.set('innerHTML', innerHTML); 490 | } 491 | 492 | }, 493 | 494 | /** 495 | * This function returns the HTML as it is in the textarea, but cleaning every 496 | * tag around the {mlang} tags. This is necessary for decorating tags on 497 | * init, because it could happen that in database are stored the {mlang} tags with 498 | * their tags, due to a bug in version 2015120501. 499 | * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8 500 | * 501 | * @method _getHTMLwithCleanedTags 502 | * @return {string} HTML in textarea, without any around {mlang} tags 503 | */ 504 | _getHTMLwithCleanedTags: function() { 505 | var host = this.get('host'), 506 | innerHTML = host.getCleanHTML(), 507 | regexString, 508 | regularExpression, 509 | spanedmlangtags, 510 | spanedmlangtag, 511 | cleanmlangtag, 512 | index; 513 | 514 | regexString = OPENING_SPAN + '.*?' + ''; 515 | regularExpression = new RegExp(regexString, 'g'); 516 | spanedmlangtags = innerHTML.match(regularExpression); 517 | 518 | if (spanedmlangtags !== null) { 519 | for (index = 0; index < spanedmlangtags.length; index++) { 520 | spanedmlangtag = spanedmlangtags[index]; 521 | 522 | cleanmlangtag = spanedmlangtag.replace(OPENING_SPAN, ''); 523 | cleanmlangtag = cleanmlangtag.replace('', ''); 524 | 525 | innerHTML = innerHTML.replace(spanedmlangtag, cleanmlangtag); 526 | } 527 | } 528 | 529 | return innerHTML; 530 | } 531 | 532 | }, { 533 | ATTRS: { 534 | /** 535 | * The list of installed languages. 536 | * 537 | * @attribute languages 538 | * @type array 539 | * @default {"en":"English (en)"} 540 | */ 541 | languages: DEFAULT_LANGUAGE, 542 | 543 | /** 544 | * If the current user has the capability to use the plugin. 545 | * 546 | * @attribute capability 547 | * @type boolean 548 | * @default true 549 | */ 550 | capability: DEFAULT_CAPABILITY, 551 | 552 | /** 553 | * If the {mlang} tags have to be highlighted or not. 554 | * 555 | * @property highlight 556 | * @type boolean 557 | * @default true 558 | */ 559 | highlight: DEFAULT_HIGHLIGHT, 560 | 561 | /** 562 | * The CSS for delimiters. 563 | * 564 | * @property css 565 | * @type string 566 | * @default DEFAULT_CSS 567 | */ 568 | css: DEFAULT_CSS 569 | } 570 | }); 571 | 572 | 573 | }, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]}); 574 | -------------------------------------------------------------------------------- /yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js: -------------------------------------------------------------------------------- 1 | if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } 2 | if (!__coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']) { 3 | __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\"]});","","}());"]}; 4 | } 5 | var __cov_xlcucJyua6odXhLRctX6xg = __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']; 6 | __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']}); 7 | --------------------------------------------------------------------------------