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 |  
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 |
--------------------------------------------------------------------------------