diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c2658d7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..5f2302e
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,403 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright 2014 Andrew Nicols
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Grunt configuration
+ */
+
+module.exports = function(grunt) {
+ var path = require('path'),
+ tasks = {},
+ cwd = process.env.PWD || process.cwd(),
+ async = require('async'),
+ DOMParser = require('xmldom').DOMParser,
+ xpath = require('xpath'),
+ semver = require('semver');
+
+ // Verify the node version is new enough.
+ var expected = semver.validRange(grunt.file.readJSON('package.json').engines.node);
+ var actual = semver.valid(process.version);
+ if (!semver.satisfies(actual, expected)) {
+ grunt.fail.fatal('Node version too old. Require ' + expected + ', version installed: ' + actual);
+ }
+
+ // Windows users can't run grunt in a subdirectory, so allow them to set
+ // the root by passing --root=path/to/dir.
+ if (grunt.option('root')) {
+ var root = grunt.option('root');
+ if (grunt.file.exists(__dirname, root)) {
+ cwd = path.join(__dirname, root);
+ grunt.log.ok('Setting root to ' + cwd);
+ } else {
+ grunt.fail.fatal('Setting root to ' + root + ' failed - path does not exist');
+ }
+ }
+
+ var inAMD = path.basename(cwd) == 'amd';
+
+ // Globbing pattern for matching all AMD JS source files.
+ var amdSrc = [inAMD ? cwd + '/src/*.js' : '**/amd/src/*.js'];
+
+ /**
+ * Function to generate the destination for the uglify task
+ * (e.g. build/file.min.js). This function will be passed to
+ * the rename property of files array when building dynamically:
+ * http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
+ *
+ * @param {String} destPath the current destination
+ * @param {String} srcPath the matched src path
+ * @return {String} The rewritten destination path.
+ */
+ var uglifyRename = function(destPath, srcPath) {
+ destPath = srcPath.replace('src', 'build');
+ destPath = destPath.replace('.js', '.min.js');
+ destPath = path.resolve(cwd, destPath);
+ return destPath;
+ };
+
+ /**
+ * Find thirdpartylibs.xml and generate an array of paths contained within
+ * them (used to generate ignore files and so on).
+ *
+ * @return {array} The list of thirdparty paths.
+ */
+ var getThirdPartyPathsFromXML = function() {
+ var thirdpartyfiles = grunt.file.expand('*/**/thirdpartylibs.xml');
+ var libs = ['node_modules/', 'vendor/'];
+
+ thirdpartyfiles.forEach(function(file) {
+ var dirname = path.dirname(file);
+
+ var doc = new DOMParser().parseFromString(grunt.file.read(file));
+ var nodes = xpath.select("/libraries/library/location/text()", doc);
+
+ nodes.forEach(function(node) {
+ var lib = path.join(dirname, node.toString());
+ if (grunt.file.isDir(lib)) {
+ // Ensure trailing slash on dirs.
+ lib = lib.replace(/\/?$/, '/');
+ }
+
+ // Look for duplicate paths before adding to array.
+ if (libs.indexOf(lib) === -1) {
+ libs.push(lib);
+ }
+ });
+ });
+ return libs;
+ };
+
+ // Project configuration.
+ grunt.initConfig({
+ eslint: {
+ // Even though warnings dont stop the build we don't display warnings by default because
+ // at this moment we've got too many core warnings.
+ options: {quiet: !grunt.option('show-lint-warnings')},
+ amd: {
+ src: amdSrc,
+ // Check AMD with some slightly stricter rules.
+ rules: {
+ 'no-unused-vars': 'error',
+ 'no-implicit-globals': 'error'
+ }
+ },
+ // Check YUI module source files.
+ yui: {
+ src: ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js'],
+ options: {
+ // Disable some rules which we can't safely define for YUI rollups.
+ rules: {
+ 'no-undef': 'off',
+ 'no-unused-vars': 'off',
+ 'no-unused-expressions': 'off'
+ }
+ }
+ }
+ },
+ uglify: {
+ amd: {
+ files: [{
+ expand: true,
+ src: amdSrc,
+ rename: uglifyRename
+ }],
+ options: {report: 'none'}
+ }
+ },
+ less: {
+ bootstrapbase: {
+ files: {
+ "theme/bootstrapbase/style/moodle.css": "theme/bootstrapbase/less/moodle.less",
+ "theme/bootstrapbase/style/editor.css": "theme/bootstrapbase/less/editor.less",
+ },
+ options: {
+ compress: false // We must not compress to keep the comments.
+ }
+ }
+ },
+ watch: {
+ options: {
+ nospawn: true // We need not to spawn so config can be changed dynamically.
+ },
+ amd: {
+ files: ['**/amd/src/**/*.js'],
+ tasks: ['amd']
+ },
+ bootstrapbase: {
+ files: ["theme/bootstrapbase/less/**/*.less"],
+ tasks: ["css"]
+ },
+ yui: {
+ files: ['**/yui/src/**/*.js'],
+ tasks: ['yui']
+ },
+ gherkinlint: {
+ files: ['**/tests/behat/*.feature'],
+ tasks: ['gherkinlint']
+ }
+ },
+ shifter: {
+ options: {
+ recursive: true,
+ paths: [cwd]
+ }
+ },
+ gherkinlint: {
+ options: {
+ files: ['**/tests/behat/*.feature'],
+ }
+ },
+ stylelint: {
+ less: {
+ options: {
+ syntax: 'less',
+ configOverrides: {
+ rules: {
+ // These rules have to be disabled in .stylelintrc for scss compat.
+ "at-rule-no-unknown": true,
+ "no-browser-hacks": [true, {"severity": "warning"}]
+ }
+ }
+ },
+ src: ['theme/**/*.less']
+ },
+ scss: {
+ options: {syntax: 'scss'},
+ src: ['*/**/*.scss']
+ },
+ css: {
+ src: ['*/**/*.css'],
+ options: {
+ configOverrides: {
+ rules: {
+ // These rules have to be disabled in .stylelintrc for scss compat.
+ "at-rule-no-unknown": true,
+ "no-browser-hacks": [true, {"severity": "warning"}]
+ }
+ }
+ }
+ }
+ }
+ });
+
+ /**
+ * Generate ignore files (utilising thirdpartylibs.xml data)
+ */
+ tasks.ignorefiles = function() {
+ // An array of paths to third party directories.
+ var thirdPartyPaths = getThirdPartyPathsFromXML();
+ // Generate .eslintignore.
+ var eslintIgnores = ['# Generated by "grunt ignorefiles"', '*/**/yui/src/*/meta/', '*/**/build/'].concat(thirdPartyPaths);
+ grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
+ // Generate .stylelintignore.
+ var stylelintIgnores = [
+ '# Generated by "grunt ignorefiles"',
+ 'theme/bootstrapbase/style/',
+ 'theme/clean/style/custom.css',
+ 'theme/more/style/custom.css'
+ ].concat(thirdPartyPaths);
+ grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
+ };
+
+ /**
+ * Shifter task. Is configured with a path to a specific file or a directory,
+ * in the case of a specific file it will work out the right module to be built.
+ *
+ * Note that this task runs the invidiaul shifter jobs async (becase it spawns
+ * so be careful to to call done().
+ */
+ tasks.shifter = function() {
+ var done = this.async(),
+ options = grunt.config('shifter.options');
+
+ // Run the shifter processes one at a time to avoid confusing output.
+ async.eachSeries(options.paths, function(src, filedone) {
+ var args = [];
+ args.push(path.normalize(__dirname + '/node_modules/shifter/bin/shifter'));
+
+ // Always ignore the node_modules directory.
+ args.push('--excludes', 'node_modules');
+
+ // Determine the most appropriate options to run with based upon the current location.
+ if (grunt.file.isMatch('**/yui/**/*.js', src)) {
+ // When passed a JS file, build our containing module (this happen with
+ // watch).
+ grunt.log.debug('Shifter passed a specific JS file');
+ src = path.dirname(path.dirname(src));
+ options.recursive = false;
+ } else if (grunt.file.isMatch('**/yui/src', src)) {
+ // When in a src directory --walk all modules.
+ grunt.log.debug('In a src directory');
+ args.push('--walk');
+ options.recursive = false;
+ } else if (grunt.file.isMatch('**/yui/src/*', src)) {
+ // When in module, only build our module.
+ grunt.log.debug('In a module directory');
+ options.recursive = false;
+ } else if (grunt.file.isMatch('**/yui/src/*/js', src)) {
+ // When in module src, only build our module.
+ grunt.log.debug('In a source directory');
+ src = path.dirname(src);
+ options.recursive = false;
+ }
+
+ if (grunt.option('watch')) {
+ grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead');
+ }
+
+ // Add the stderr option if appropriate
+ if (grunt.option('verbose')) {
+ args.push('--lint-stderr');
+ }
+
+ if (grunt.option('no-color')) {
+ args.push('--color=false');
+ }
+
+ var execShifter = function() {
+
+ grunt.log.ok("Running shifter on " + src);
+ grunt.util.spawn({
+ cmd: "node",
+ args: args,
+ opts: {cwd: src, stdio: 'inherit', env: process.env}
+ }, function(error, result, code) {
+ if (code) {
+ grunt.fail.fatal('Shifter failed with code: ' + code);
+ } else {
+ grunt.log.ok('Shifter build complete.');
+ filedone();
+ }
+ });
+ };
+
+ // Actually run shifter.
+ if (!options.recursive) {
+ execShifter();
+ } else {
+ // Check that there are yui modules otherwise shifter ends with exit code 1.
+ if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) {
+ args.push('--recursive');
+ execShifter();
+ } else {
+ grunt.log.ok('No YUI modules to build.');
+ filedone();
+ }
+ }
+ }, done);
+ };
+
+ tasks.gherkinlint = function() {
+ var done = this.async(),
+ options = grunt.config('gherkinlint.options');
+
+ var args = grunt.file.expand(options.files);
+ args.unshift(path.normalize(__dirname + '/node_modules/.bin/gherkin-lint'));
+ grunt.util.spawn({
+ cmd: 'node',
+ args: args,
+ opts: {stdio: 'inherit', env: process.env}
+ }, function(error, result, code) {
+ // Propagate the exit code.
+ done(code === 0);
+ });
+ };
+
+ tasks.startup = function() {
+ // Are we in a YUI directory?
+ if (path.basename(path.resolve(cwd, '../../')) == 'yui') {
+ grunt.task.run('yui');
+ // Are we in an AMD directory?
+ } else if (inAMD) {
+ grunt.task.run('amd');
+ } else {
+ // Run them all!.
+ grunt.task.run('css');
+ grunt.task.run('js');
+ grunt.task.run('gherkinlint');
+ }
+ };
+
+ // On watch, we dynamically modify config to build only affected files. This
+ // method is slightly complicated to deal with multiple changed files at once (copied
+ // from the grunt-contrib-watch readme).
+ var changedFiles = Object.create(null);
+ var onChange = grunt.util._.debounce(function() {
+ var files = Object.keys(changedFiles);
+ grunt.config('eslint.amd.src', files);
+ grunt.config('eslint.yui.src', files);
+ grunt.config('uglify.amd.files', [{expand: true, src: files, rename: uglifyRename}]);
+ grunt.config('shifter.options.paths', files);
+ grunt.config('stylelint.less.src', files);
+ grunt.config('gherkinlint.options.files', files);
+ changedFiles = Object.create(null);
+ }, 200);
+
+ grunt.event.on('watch', function(action, filepath) {
+ changedFiles[filepath] = action;
+ onChange();
+ });
+
+ // Register NPM tasks.
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-less');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-eslint');
+ grunt.loadNpmTasks('grunt-stylelint');
+
+ // Register JS tasks.
+ grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter);
+ grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', tasks.gherkinlint);
+ grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles);
+ grunt.registerTask('yui', ['eslint:yui', 'shifter']);
+ grunt.registerTask('amd', ['eslint:amd', 'uglify']);
+ grunt.registerTask('js', ['amd', 'yui']);
+
+ // Register CSS taks.
+ grunt.registerTask('css', ['stylelint:scss', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']);
+
+ // Register the startup task.
+ grunt.registerTask('startup', 'Run the correct tasks for the current directory', tasks.startup);
+
+ // Register the default task.
+ grunt.registerTask('default', ['startup']);
+};
diff --git a/README.md b/README.md
index a26524a..16fc65e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Atto multilanguage plugin
=========================
-![Release](https://img.shields.io/badge/release-v1.8-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3-green.svg)
+![Release](https://img.shields.io/badge/release-v1.9-blue.svg) ![Supported](https://img.shields.io/badge/supported-2.9%2C%203.0%2C%203.1%2C%203.2%2C%203.3%2C%203.4-green.svg)
This plugin will make the creation of multilingual contents on Moodle much more easier with Atto editor.
@@ -9,8 +9,12 @@ The plugin is developed to work with [Iñaki Arenaza's multilang2 filter](https:
## Current version
-The latest release is v1.8 (build 2017100200) for Moodle 2.9, 3.0, 3.1, 3.2 and 3.3 (Checkout [v2.9.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v2.9.1.8), [v3.0.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.0.1.8), [v3.1.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.1.1.8), [v3.2.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.2.1.8) and [v3.3.1.8](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.3.1.8) releases, respectively.
+The latest release is v1.9 (build 2017102700) for Moodle 2.9, 3.0, 3.1, 3.2, 3.4 and 3.4 (Checkout [v2.9.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v2.9.1.9), [v3.0.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.0.1.9), [v3.1.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.1.1.9), [v3.2.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.2.1.9) and [v3.3.1.9](https://github.com/iarenaza/moodle-atto_multilang2/releases/tag/v3.3.1.9) releases, respectively.
+## Changes from v1.8
+ - Added Grunt support files. No we can check Moodle Javascript coding guidelines and minify the plugin code without installing the plugins.
+ - Minor Javascript coding guidelines fix (signalled by the minifier, but ignored by the linter!)
+
## Changes from v1.7
- Multiple bug fixes, especially when there were more than one Atto editor in the same page. Closes issues #2, #18, #21.
- Multilang tag highlighting 's are now removed/added when switching to/from HTML view, reducing clutter when editing the HTML code.
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2a7d142
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "Moodle",
+ "private": true,
+ "description": "Moodle",
+ "devDependencies": {
+ "async": "1.5.2",
+ "eslint": "3.7.1",
+ "gherkin-lint": "1.1.3",
+ "grunt": "1.0.1",
+ "grunt-contrib-less": "1.3.0",
+ "grunt-contrib-uglify": "1.0.1",
+ "grunt-contrib-watch": "1.0.0",
+ "grunt-eslint": "19.0.0",
+ "grunt-stylelint": "0.6.0",
+ "semver": "5.3.0",
+ "shifter": "0.5.0",
+ "stylelint": "7.4.1",
+ "stylelint-checkstyle-formatter": "0.1.0",
+ "xmldom": "0.1.22",
+ "xpath": "0.0.23"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+}
diff --git a/version.php b/version.php
index 5546916..6fcf682 100644
--- a/version.php
+++ b/version.php
@@ -24,8 +24,8 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017100200; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->release = 'v3.2.1.8 (version v1.8 for Moodle 3.2) (2017100200)';
+$plugin->version = 2017102700; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->release = 'v3.2.1.9 (version v1.9 for Moodle 3.2) (2017102700)';
$plugin->requires = 2015051100; // Required Moodle version.
$plugin->component = 'atto_multilang2'; // Full name of the plugin (used for diagnostics).
$plugin->maturity = MATURITY_STABLE;
diff --git a/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js
new file mode 100644
index 0000000..3eb8e65
--- /dev/null
+++ b/yui/build/moodle-atto_multilang2-button/moodle-atto_multilang2-button-coverage.js
@@ -0,0 +1,6 @@
+if (typeof __coverage__ === 'undefined') { __coverage__ = {}; }
+if (!__coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js']) {
+ __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js'] = {"path":"build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0,0,0,0],"16":[0,0],"17":[0,0],"18":[0,0,0,0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0},"fnMap":{"1":{"name":"(anonymous_1)","line":1,"loc":{"start":{"line":1,"column":41},"end":{"line":1,"column":60}}},"2":{"name":"(anonymous_2)","line":74,"loc":{"start":{"line":74,"column":17},"end":{"line":74,"column":28}}},"3":{"name":"(anonymous_3)","line":130,"loc":{"start":{"line":130,"column":29},"end":{"line":130,"column":40}}},"4":{"name":"(anonymous_4)","line":154,"loc":{"start":{"line":154,"column":25},"end":{"line":154,"column":36}}},"5":{"name":"(anonymous_5)","line":176,"loc":{"start":{"line":176,"column":25},"end":{"line":176,"column":36}}},"6":{"name":"(anonymous_6)","line":180,"loc":{"start":{"line":180,"column":31},"end":{"line":180,"column":42}}},"7":{"name":"(anonymous_7)","line":182,"loc":{"start":{"line":182,"column":19},"end":{"line":182,"column":30}}},"8":{"name":"(anonymous_8)","line":202,"loc":{"start":{"line":202,"column":29},"end":{"line":202,"column":40}}},"9":{"name":"(anonymous_9)","line":206,"loc":{"start":{"line":206,"column":35},"end":{"line":206,"column":46}}},"10":{"name":"(anonymous_10)","line":208,"loc":{"start":{"line":208,"column":19},"end":{"line":208,"column":30}}},"11":{"name":"(anonymous_11)","line":234,"loc":{"start":{"line":234,"column":14},"end":{"line":234,"column":40}}},"12":{"name":"(anonymous_12)","line":261,"loc":{"start":{"line":261,"column":23},"end":{"line":261,"column":34}}},"13":{"name":"(anonymous_13)","line":295,"loc":{"start":{"line":295,"column":27},"end":{"line":295,"column":38}}},"14":{"name":"(anonymous_14)","line":323,"loc":{"start":{"line":323,"column":21},"end":{"line":323,"column":32}}},"15":{"name":"(anonymous_15)","line":346,"loc":{"start":{"line":346,"column":25},"end":{"line":346,"column":36}}},"16":{"name":"(anonymous_16)","line":392,"loc":{"start":{"line":392,"column":29},"end":{"line":392,"column":47}}},"17":{"name":"(anonymous_17)","line":404,"loc":{"start":{"line":404,"column":25},"end":{"line":404,"column":40}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":460,"column":61}},"2":{"start":{"line":37,"column":0},"end":{"line":60,"column":6}},"3":{"start":{"line":63,"column":0},"end":{"line":457,"column":3}},"4":{"start":{"line":75,"column":8},"end":{"line":78,"column":17}},"5":{"start":{"line":80,"column":8},"end":{"line":119,"column":9}},"6":{"start":{"line":81,"column":12},"end":{"line":81,"column":58}},"7":{"start":{"line":83,"column":12},"end":{"line":90,"column":15}},"8":{"start":{"line":92,"column":12},"end":{"line":92,"column":54}},"9":{"start":{"line":94,"column":12},"end":{"line":94,"column":55}},"10":{"start":{"line":95,"column":12},"end":{"line":118,"column":13}},"11":{"start":{"line":96,"column":16},"end":{"line":96,"column":54}},"12":{"start":{"line":100,"column":16},"end":{"line":100,"column":40}},"13":{"start":{"line":101,"column":16},"end":{"line":101,"column":54}},"14":{"start":{"line":102,"column":16},"end":{"line":104,"column":17}},"15":{"start":{"line":103,"column":20},"end":{"line":103,"column":66}},"16":{"start":{"line":108,"column":16},"end":{"line":108,"column":95}},"17":{"start":{"line":111,"column":16},"end":{"line":111,"column":85}},"18":{"start":{"line":112,"column":16},"end":{"line":112,"column":85}},"19":{"start":{"line":116,"column":16},"end":{"line":116,"column":43}},"20":{"start":{"line":117,"column":16},"end":{"line":117,"column":47}},"21":{"start":{"line":131,"column":8},"end":{"line":133,"column":21}},"22":{"start":{"line":135,"column":8},"end":{"line":135,"column":57}},"23":{"start":{"line":136,"column":8},"end":{"line":143,"column":9}},"24":{"start":{"line":137,"column":12},"end":{"line":142,"column":13}},"25":{"start":{"line":138,"column":16},"end":{"line":141,"column":19}},"26":{"start":{"line":145,"column":8},"end":{"line":145,"column":28}},"27":{"start":{"line":155,"column":8},"end":{"line":156,"column":18}},"28":{"start":{"line":158,"column":8},"end":{"line":158,"column":48}},"29":{"start":{"line":159,"column":8},"end":{"line":159,"column":32}},"30":{"start":{"line":160,"column":8},"end":{"line":160,"column":30}},"31":{"start":{"line":162,"column":8},"end":{"line":162,"column":41}},"32":{"start":{"line":177,"column":8},"end":{"line":178,"column":35}},"33":{"start":{"line":180,"column":8},"end":{"line":188,"column":13}},"34":{"start":{"line":181,"column":12},"end":{"line":181,"column":54}},"35":{"start":{"line":182,"column":12},"end":{"line":187,"column":14}},"36":{"start":{"line":183,"column":16},"end":{"line":185,"column":17}},"37":{"start":{"line":184,"column":20},"end":{"line":184,"column":126}},"38":{"start":{"line":186,"column":16},"end":{"line":186,"column":62}},"39":{"start":{"line":203,"column":8},"end":{"line":204,"column":35}},"40":{"start":{"line":206,"column":8},"end":{"line":215,"column":13}},"41":{"start":{"line":207,"column":12},"end":{"line":207,"column":62}},"42":{"start":{"line":208,"column":12},"end":{"line":214,"column":14}},"43":{"start":{"line":209,"column":16},"end":{"line":209,"column":69}},"44":{"start":{"line":210,"column":16},"end":{"line":212,"column":17}},"45":{"start":{"line":211,"column":20},"end":{"line":211,"column":58}},"46":{"start":{"line":213,"column":16},"end":{"line":213,"column":27}},"47":{"start":{"line":235,"column":8},"end":{"line":238,"column":20}},"48":{"start":{"line":240,"column":8},"end":{"line":240,"column":42}},"49":{"start":{"line":242,"column":8},"end":{"line":242,"column":45}},"50":{"start":{"line":243,"column":8},"end":{"line":243,"column":87}},"51":{"start":{"line":245,"column":8},"end":{"line":245,"column":71}},"52":{"start":{"line":246,"column":8},"end":{"line":246,"column":73}},"53":{"start":{"line":248,"column":8},"end":{"line":248,"column":54}},"54":{"start":{"line":250,"column":8},"end":{"line":250,"column":27}},"55":{"start":{"line":262,"column":8},"end":{"line":266,"column":19}},"56":{"start":{"line":268,"column":8},"end":{"line":283,"column":9}},"57":{"start":{"line":269,"column":12},"end":{"line":269,"column":46}},"58":{"start":{"line":271,"column":12},"end":{"line":277,"column":13}},"59":{"start":{"line":272,"column":16},"end":{"line":272,"column":58}},"60":{"start":{"line":273,"column":16},"end":{"line":275,"column":17}},"61":{"start":{"line":274,"column":20},"end":{"line":274,"column":87}},"62":{"start":{"line":276,"column":16},"end":{"line":276,"column":43}},"63":{"start":{"line":279,"column":15},"end":{"line":283,"column":9}},"64":{"start":{"line":280,"column":12},"end":{"line":282,"column":13}},"65":{"start":{"line":281,"column":16},"end":{"line":281,"column":65}},"66":{"start":{"line":285,"column":8},"end":{"line":285,"column":20}},"67":{"start":{"line":296,"column":8},"end":{"line":300,"column":22}},"68":{"start":{"line":303,"column":8},"end":{"line":306,"column":9}},"69":{"start":{"line":305,"column":12},"end":{"line":305,"column":19}},"70":{"start":{"line":308,"column":8},"end":{"line":308,"column":50}},"71":{"start":{"line":309,"column":8},"end":{"line":309,"column":105}},"72":{"start":{"line":310,"column":8},"end":{"line":314,"column":9}},"73":{"start":{"line":312,"column":12},"end":{"line":312,"column":63}},"74":{"start":{"line":313,"column":12},"end":{"line":313,"column":41}},"75":{"start":{"line":324,"column":8},"end":{"line":327,"column":9}},"76":{"start":{"line":325,"column":12},"end":{"line":325,"column":85}},"77":{"start":{"line":326,"column":12},"end":{"line":326,"column":31}},"78":{"start":{"line":347,"column":8},"end":{"line":354,"column":27}},"79":{"start":{"line":355,"column":8},"end":{"line":377,"column":9}},"80":{"start":{"line":356,"column":12},"end":{"line":356,"column":77}},"81":{"start":{"line":358,"column":12},"end":{"line":358,"column":62}},"82":{"start":{"line":359,"column":12},"end":{"line":359,"column":60}},"83":{"start":{"line":360,"column":12},"end":{"line":374,"column":13}},"84":{"start":{"line":361,"column":16},"end":{"line":371,"column":17}},"85":{"start":{"line":362,"column":20},"end":{"line":362,"column":48}},"86":{"start":{"line":364,"column":20},"end":{"line":364,"column":79}},"87":{"start":{"line":365,"column":20},"end":{"line":370,"column":21}},"88":{"start":{"line":366,"column":24},"end":{"line":366,"column":56}},"89":{"start":{"line":367,"column":24},"end":{"line":367,"column":85}},"90":{"start":{"line":368,"column":24},"end":{"line":368,"column":70}},"91":{"start":{"line":369,"column":24},"end":{"line":369,"column":96}},"92":{"start":{"line":373,"column":16},"end":{"line":373,"column":48}},"93":{"start":{"line":376,"column":12},"end":{"line":376,"column":31}},"94":{"start":{"line":394,"column":8},"end":{"line":396,"column":21}},"95":{"start":{"line":398,"column":8},"end":{"line":398,"column":35}},"96":{"start":{"line":399,"column":8},"end":{"line":399,"column":52}},"97":{"start":{"line":402,"column":8},"end":{"line":402,"column":56}},"98":{"start":{"line":404,"column":8},"end":{"line":414,"column":11}},"99":{"start":{"line":405,"column":12},"end":{"line":413,"column":13}},"100":{"start":{"line":407,"column":16},"end":{"line":409,"column":17}},"101":{"start":{"line":408,"column":20},"end":{"line":408,"column":72}},"102":{"start":{"line":412,"column":16},"end":{"line":412,"column":50}},"103":{"start":{"line":416,"column":8},"end":{"line":416,"column":32}}},"branchMap":{"1":{"line":80,"type":"if","locations":[{"start":{"line":80,"column":8},"end":{"line":80,"column":8}},{"start":{"line":80,"column":8},"end":{"line":80,"column":8}}]},"2":{"line":95,"type":"if","locations":[{"start":{"line":95,"column":12},"end":{"line":95,"column":12}},{"start":{"line":95,"column":12},"end":{"line":95,"column":12}}]},"3":{"line":102,"type":"if","locations":[{"start":{"line":102,"column":16},"end":{"line":102,"column":16}},{"start":{"line":102,"column":16},"end":{"line":102,"column":16}}]},"4":{"line":137,"type":"if","locations":[{"start":{"line":137,"column":12},"end":{"line":137,"column":12}},{"start":{"line":137,"column":12},"end":{"line":137,"column":12}}]},"5":{"line":183,"type":"if","locations":[{"start":{"line":183,"column":16},"end":{"line":183,"column":16}},{"start":{"line":183,"column":16},"end":{"line":183,"column":16}}]},"6":{"line":183,"type":"binary-expr","locations":[{"start":{"line":183,"column":20},"end":{"line":183,"column":46}},{"start":{"line":183,"column":51},"end":{"line":183,"column":109}}]},"7":{"line":210,"type":"if","locations":[{"start":{"line":210,"column":16},"end":{"line":210,"column":16}},{"start":{"line":210,"column":16},"end":{"line":210,"column":16}}]},"8":{"line":210,"type":"binary-expr","locations":[{"start":{"line":210,"column":20},"end":{"line":210,"column":46}},{"start":{"line":210,"column":51},"end":{"line":210,"column":113}}]},"9":{"line":243,"type":"cond-expr","locations":[{"start":{"line":243,"column":66},"end":{"line":243,"column":74}},{"start":{"line":243,"column":77},"end":{"line":243,"column":86}}]},"10":{"line":268,"type":"if","locations":[{"start":{"line":268,"column":8},"end":{"line":268,"column":8}},{"start":{"line":268,"column":8},"end":{"line":268,"column":8}}]},"11":{"line":271,"type":"if","locations":[{"start":{"line":271,"column":12},"end":{"line":271,"column":12}},{"start":{"line":271,"column":12},"end":{"line":271,"column":12}}]},"12":{"line":279,"type":"if","locations":[{"start":{"line":279,"column":15},"end":{"line":279,"column":15}},{"start":{"line":279,"column":15},"end":{"line":279,"column":15}}]},"13":{"line":280,"type":"if","locations":[{"start":{"line":280,"column":12},"end":{"line":280,"column":12}},{"start":{"line":280,"column":12},"end":{"line":280,"column":12}}]},"14":{"line":303,"type":"if","locations":[{"start":{"line":303,"column":8},"end":{"line":303,"column":8}},{"start":{"line":303,"column":8},"end":{"line":303,"column":8}}]},"15":{"line":303,"type":"binary-expr","locations":[{"start":{"line":303,"column":13},"end":{"line":303,"column":40}},{"start":{"line":303,"column":46},"end":{"line":303,"column":59}},{"start":{"line":303,"column":65},"end":{"line":303,"column":79}},{"start":{"line":304,"column":17},"end":{"line":304,"column":55}},{"start":{"line":304,"column":61},"end":{"line":304,"column":85}}]},"16":{"line":309,"type":"cond-expr","locations":[{"start":{"line":309,"column":62},"end":{"line":309,"column":99}},{"start":{"line":309,"column":102},"end":{"line":309,"column":104}}]},"17":{"line":310,"type":"if","locations":[{"start":{"line":310,"column":8},"end":{"line":310,"column":8}},{"start":{"line":310,"column":8},"end":{"line":310,"column":8}}]},"18":{"line":310,"type":"binary-expr","locations":[{"start":{"line":310,"column":13},"end":{"line":310,"column":50}},{"start":{"line":310,"column":56},"end":{"line":310,"column":79}},{"start":{"line":310,"column":85},"end":{"line":310,"column":103}},{"start":{"line":311,"column":17},"end":{"line":311,"column":42}},{"start":{"line":311,"column":48},"end":{"line":311,"column":87}}]},"19":{"line":324,"type":"if","locations":[{"start":{"line":324,"column":8},"end":{"line":324,"column":8}},{"start":{"line":324,"column":8},"end":{"line":324,"column":8}}]},"20":{"line":355,"type":"if","locations":[{"start":{"line":355,"column":8},"end":{"line":355,"column":8}},{"start":{"line":355,"column":8},"end":{"line":355,"column":8}}]},"21":{"line":360,"type":"if","locations":[{"start":{"line":360,"column":12},"end":{"line":360,"column":12}},{"start":{"line":360,"column":12},"end":{"line":360,"column":12}}]},"22":{"line":365,"type":"if","locations":[{"start":{"line":365,"column":20},"end":{"line":365,"column":20}},{"start":{"line":365,"column":20},"end":{"line":365,"column":20}}]},"23":{"line":405,"type":"if","locations":[{"start":{"line":405,"column":12},"end":{"line":405,"column":12}},{"start":{"line":405,"column":12},"end":{"line":405,"column":12}}]}},"code":["(function () { YUI.add('moodle-atto_multilang2-button', function (Y, NAME) {","","// This file is part of Moodle - http://moodle.org/","//","// Moodle is free software: you can redistribute it and/or modify","// it under the terms of the GNU General Public License as published by","// the Free Software Foundation, either version 3 of the License, or","// (at your option) any later version.","//","// Moodle is distributed in the hope that it will be useful,","// but WITHOUT ANY WARRANTY; without even the implied warranty of","// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the","// GNU General Public License for more details.","//","// You should have received a copy of the GNU General Public License","// along with Moodle. If not, see .","","/**"," * @package atto_multilang2"," * @copyright 2015 onwards Julen Pardo & Mondragon Unibertsitatea"," * @copyright 2017 onwards Iñaki Arenaza & Mondragon Unibertsitatea"," * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later"," */","","/**"," * @module moodle-atto_multilang2-button"," */","","/**"," * Atto text editor multilanguage plugin."," *"," * @namespace M.atto_multilang2"," * @class button"," * @extends M.editor_atto.EditorPlugin."," */","","var CLASSES = {"," TAG: 'filter-multilang-tag'"," },",""," LANG_WILDCARD = '%lang',"," CONTENT_WILDCARD = '%content',"," ATTR_LANGUAGES = 'languages',"," ATTR_CAPABILITY = 'capability',"," ATTR_HIGHLIGHT = 'highlight',"," ATTR_CSS = 'css',"," DEFAULT_LANGUAGE = '{\"en\":\"English (en)\"}',"," DEFAULT_CAPABILITY = true,"," DEFAULT_HIGHLIGHT = true,"," DEFAULT_CSS = 'outline: 1px dotted;' +"," 'padding: 0.1em;' +"," 'margin: 0em 0.1em;' +"," 'background-color: #ffffaa;',"," OPENING_SPAN = '',"," CLOSING_SPAN = '',"," TEMPLATES = {"," SPANNED: ' ' + OPENING_SPAN + '{mlang ' + LANG_WILDCARD + '}' + CLOSING_SPAN +"," CONTENT_WILDCARD + OPENING_SPAN + '{mlang}' + CLOSING_SPAN + ' ',"," NOT_SPANNED: '{mlang ' + LANG_WILDCARD + '}' + CONTENT_WILDCARD + '{mlang}'"," };","","","Y.namespace('M.atto_multilang2').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {",""," /**"," * If the {mlang} tags have to be highlighted or not. Received as parameter from lib.php."," *"," * @property _highlight"," * @type boolean"," * @private"," */"," _highlight: true,",""," initializer: function() {"," var hascapability = this.get(ATTR_CAPABILITY),"," toolbarItems,"," host,"," form;",""," if (hascapability) {"," toolbarItems = this._initializeToolbarItems();",""," this.addToolbarMenu({"," globalItemConfig: {"," callback: this._addTags"," },"," icon: 'icon',"," iconComponent: 'atto_multilang2',"," items: toolbarItems"," });",""," this._tagTemplate = TEMPLATES.NOT_SPANNED;",""," this._highlight = this.get(ATTR_HIGHLIGHT);"," if (this._highlight) {"," this._tagTemplate = TEMPLATES.SPANNED;",""," // Attach a submit listener to the form, so we can remove"," // the highlighting html before sending content to Moodle."," host = this.get('host');"," form = host.textarea.ancestor('form');"," if (form) {"," form.on('submit', this._cleanMlangTags, this);"," }",""," // Listen to every change of the text cursor in the text area, to see if"," // the cursor is placed within a multilang tag."," this.get('host').on('atto:selectionchanged', this._checkSelectionChange, this);",""," // Highlight the multilang tags once everything is loaded."," this.get('host').on('pluginsloaded', this._addHighlightingCss, this);"," this.get('host').on('pluginsloaded', this._highlightMlangTags, this);",""," // Hook into host.updateOriginal() and host.updateFromTextArea()"," // so we can add/remove highlighting when we switch to/from HTML view."," this._hookUpdateOriginal();"," this._hookUpdateFromTextArea();"," }"," }"," },",""," /**"," * Initializes the toolbar items, which will be the installed languages,"," * received as parameter."," *"," * @method _initializeToolbarItems"," * @private"," * @return {Array} installed language strings"," */"," _initializeToolbarItems: function() {"," var toolbarItems = [],"," languages,"," langCode;",""," languages = JSON.parse(this.get(ATTR_LANGUAGES));"," for (langCode in languages) {"," if (languages.hasOwnProperty(langCode)) {"," toolbarItems.push({"," text: languages[langCode],"," callbackArgs: langCode"," });"," }"," }",""," return toolbarItems;"," },",""," /**"," * Adds the CSS rules for the delimiters, received as parameter from lib.php."," *"," * @method _addHighlightingCss"," * @private"," */"," _addHighlightingCss: function() {"," var css = '.' + CLASSES.TAG + ' {' + this.get(ATTR_CSS) + '}',"," style;",""," style = document.createElement('style');"," style.type = 'text/css';"," style.innerHTML = css;",""," document.head.appendChild(style);"," },",""," /**"," * Hook the host.updateOriginal() method to allow us to remove the highlighting html when"," * switching to HTML view. As the HTML view plugin doesn't provide a hook or fire an event"," * to notify about the switch to HTML view, we need to hijack host.updateOriginal and look"," * for the caller. Once we've cleaned up the highlighting, we need to execute the original"," * host.updateOriginal() method."," * Inspired by https://stackoverflow.com/a/16580937"," *"," * @method _hookUpdateOriginal"," * @private"," */"," _hookUpdateOriginal: function() {"," var host = this.get('host'),"," multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _removeTags().",""," host.updateOriginal = (function() {"," var _updateOriginal = host.updateOriginal;"," return function() {"," if (multilangplugin._highlight && (this.updateOriginal.caller === host.plugins.html._showHTML)) {"," multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML()));"," }"," return _updateOriginal.apply(this, arguments);"," };"," })();"," },",""," /**"," * Hook the host.updateFromTextAreal() method to allow us to re-add the highlighting"," * html when switching from HTML view. As the HTML view plugin doesn't provide a hook"," * or fire an event to notify about the switch from HTML view, we need to hijack"," * host.updateFromTextArea and look for the caller. Once we've executed the original"," * host.updateFromTextArea() method, we re-added the highlighting."," * Inspired by https://stackoverflow.com/a/16580937"," *"," * @method _hookUpdateFromTextArea"," * @private"," */"," _hookUpdateFromTextArea: function() {"," var host = this.get('host'),"," multilangplugin = this; // Capture the plugin in the closure below, so we can invoke _highlightMlangTags().",""," host.updateFromTextArea = (function() {"," var _updateFromTextArea = host.updateFromTextArea;"," return function() {"," var ret = _updateFromTextArea.apply(this, arguments);"," if (multilangplugin._highlight && (this.updateFromTextArea.caller === host.plugins.html._showHTML)) {"," multilangplugin._highlightMlangTags();"," }"," return ret;"," };"," })();"," },",""," /**"," * Retrieves the selected text, wraps it with the multilang tags,"," * and replaces the selected text in the editor with with it."," *"," * If the 'highlight' setting is checked, the {mlang} will be wrapped between"," * the tags with the class for the CSS highlight; if not, they will not"," * be wrapped."," *"," * If there is no content selected, a \" \" will be inserted; otherwhise,"," * it's impossible to place the cursor inside the {mlang} tags."," *"," * @method _addTags"," * @param {EventFacade} event"," * @param {string} langCode the language code"," * @private"," */"," _addTags: function(event, langCode) {"," var selection,"," host = this.get('host'),"," taggedContent,"," content;",""," taggedContent = this._tagTemplate;",""," selection = this._getSelectionHTML();"," content = (host.getSelection().toString().length === 0) ? ' ' : selection;",""," taggedContent = taggedContent.replace(LANG_WILDCARD, langCode);"," taggedContent = taggedContent.replace(CONTENT_WILDCARD, content);",""," host.insertContentAtFocusPoint(taggedContent);",""," this.markUpdated();"," },",""," /**"," * Retrieves selected text with its HTML."," * Taken from: http://stackoverflow.com/questions/4176923/html-of-selected-text/4177234#4177234"," *"," * @method _getSelectionHTML"," * @private"," * @return {string} selected text's html; empty if nothing selected"," */"," _getSelectionHTML: function() {"," var html = '',"," selection,"," container,"," index,"," length;",""," if (typeof window.getSelection !== 'undefined') {"," selection = window.getSelection();",""," if (selection.rangeCount) {"," container = document.createElement('div');"," for (index = 0, length = selection.rangeCount; index < length; ++index) {"," container.appendChild(selection.getRangeAt(index).cloneContents());"," }"," html = container.innerHTML;"," }",""," } else if (typeof document.selection !== 'undefined') {"," if (document.selection.type === 'Text') {"," html = document.selection.createRange().htmlText;"," }"," }",""," return html;"," },",""," /**"," * Listens to every change of the text cursor in the text area. If the"," * cursor is placed within a highlighted multilang tag, the whole tag is selected."," *"," * @method _checkSelectionChange"," * @private"," */"," _checkSelectionChange: function() {"," var host = this.get('host'),"," node = host.getSelectionParentNode(),"," parentNodeName,"," parentClass,"," selection;",""," // If the event fires without a parent node for the selection, ignore the whole thing."," if ((typeof node === 'undefined') || (node === null) || (node === false) ||"," (typeof node.parentNode === 'undefined') || (node.parentNode === null)) {"," return;"," }",""," parentNodeName = node.parentNode.nodeName;"," parentClass = node.parentNode.hasAttribute('class') ? node.parentNode.getAttribute('class') : '';"," if ((typeof parentNodeName !== 'undefined') && (parentNodeName !== null) && (parentClass !== '') &&"," (parentNodeName === 'SPAN') && (parentClass.indexOf(CLASSES.TAG) !== -1)) {"," selection = host.getSelectionFromNode(Y.one(node));"," host.setSelection(selection);"," }"," },",""," /**"," * When submitting the form, this function is invoked to clean the highlighting html code."," *"," * @method _cleanMlangTags"," * @private"," */"," _cleanMlangTags: function() {"," if (this._highlight) {"," this.editor.setHTML(this._getHTMLwithCleanedTags(this.editor.getHTML()));"," this.markUpdated();"," }"," },",""," /**"," * Adds the tags to the {mlang} tags if highlighting is enable."," *"," * Instead of taking the HTML directly from the textarea, we have to"," * retrieve it, first, without the tags that can be stored"," * in database, due to a bug in version 2015120501 that stores the"," * {mlang} tags in database, with the tags."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," *"," * Every different {mlang} tag has to be replaced only once, otherwise,"," * nested s will be created in every repeated replacement. So, we"," * need to track which replacements have been made."," *"," * @method _highlightMlangTags"," * @private"," */"," _highlightMlangTags: function() {"," var editorHTML,"," regularExpression,"," mlangtags,"," mlangtag,"," index,"," highlightedmlangtag,"," replacementsmade = [],"," notreplacedyet;"," if (this._highlight) {"," editorHTML = this._getHTMLwithCleanedTags(this.editor.getHTML());",""," regularExpression = new RegExp('{mlang.*?}', 'g');"," mlangtags = editorHTML.match(regularExpression);"," if (mlangtags !== null) {"," for (index = 0; index < mlangtags.length; index++) {"," mlangtag = mlangtags[index];",""," notreplacedyet = replacementsmade.indexOf(mlangtag) === -1;"," if (notreplacedyet) {"," replacementsmade.push(mlangtag);"," highlightedmlangtag = OPENING_SPAN + mlangtag + CLOSING_SPAN;"," regularExpression = new RegExp(mlangtag, 'g');"," editorHTML = editorHTML.replace(regularExpression, highlightedmlangtag);"," }"," }",""," this.editor.setHTML(editorHTML);"," }",""," this.markUpdated();"," }"," },",""," /**"," * This function returns the HTML passed in as parameter, but cleaning every multilang"," * tag around the {mlang} tags. This is necessary for decorating tags on"," * init, because it could happen that in database are stored the {mlang} tags with"," * their tags, due to a bug in version 2015120501."," * More info about this bug: https://github.com/julenpardo/moodle-atto_multilang2/issues/8"," * Implementation based on code from EditorClean._clearSpans()"," *"," * @method _getHTMLwithCleanedTags"," * @param {string} content The to be cleaned."," * @return {string} HTML in editor, without any around {mlang} tags."," */"," _getHTMLwithCleanedTags: function(content) {"," // This is better to run detached from the DOM, so the browser doesn't try to update on each change."," var holder = document.createElement('div'),"," spans,"," spansarr;",""," holder.innerHTML = content;"," spans = holder.getElementsByTagName('span');",""," // Since we will be removing elements from the list, we should copy it to an array, making it static."," spansarr = Array.prototype.slice.call(spans, 0);",""," spansarr.forEach(function(span) {"," if (span.className.indexOf(CLASSES.TAG) !== -1) {"," // Move each child (if they exist) to the parent in place of this span."," while (span.firstChild) {"," span.parentNode.insertBefore(span.firstChild, span);"," }",""," // Remove the now empty span."," span.parentNode.removeChild(span);"," }"," });",""," return holder.innerHTML;"," }","","}, {"," ATTRS: {"," /**"," * The list of installed languages."," *"," * @attribute languages"," * @type array"," * @default {\"en\":\"English (en)\"}"," */"," languages: DEFAULT_LANGUAGE,",""," /**"," * If the current user has the capability to use the plugin."," *"," * @attribute capability"," * @type boolean"," * @default true"," */"," capability: DEFAULT_CAPABILITY,",""," /**"," * If the {mlang} tags have to be highlighted or not."," *"," * @property highlight"," * @type boolean"," * @default true"," */"," highlight: DEFAULT_HIGHLIGHT,",""," /**"," * The CSS for delimiters."," *"," * @property css"," * @type string"," * @default DEFAULT_CSS"," */"," css: DEFAULT_CSS"," }","});","","","}, '@VERSION@', {\"requires\": [\"moodle-editor_atto-plugin\"]});","","}());"]};
+}
+var __cov_xlcucJyua6odXhLRctX6xg = __coverage__['build/moodle-atto_multilang2-button/moodle-atto_multilang2-button.js'];
+__cov_xlcucJyua6odXhLRctX6xg.s['1']++;YUI.add('moodle-atto_multilang2-button',function(Y,NAME){__cov_xlcucJyua6odXhLRctX6xg.f['1']++;__cov_xlcucJyua6odXhLRctX6xg.s['2']++;var CLASSES={TAG:'filter-multilang-tag'},LANG_WILDCARD='%lang',CONTENT_WILDCARD='%content',ATTR_LANGUAGES='languages',ATTR_CAPABILITY='capability',ATTR_HIGHLIGHT='highlight',ATTR_CSS='css',DEFAULT_LANGUAGE='{"en":"English (en)"}',DEFAULT_CAPABILITY=true,DEFAULT_HIGHLIGHT=true,DEFAULT_CSS='outline: 1px dotted;'+'padding: 0.1em;'+'margin: 0em 0.1em;'+'background-color: #ffffaa;',OPENING_SPAN='',CLOSING_SPAN='',TEMPLATES={SPANNED:' '+OPENING_SPAN+'{mlang '+LANG_WILDCARD+'}'+CLOSING_SPAN+CONTENT_WILDCARD+OPENING_SPAN+'{mlang}'+CLOSING_SPAN+' ',NOT_SPANNED:'{mlang '+LANG_WILDCARD+'}'+CONTENT_WILDCARD+'{mlang}'};__cov_xlcucJyua6odXhLRctX6xg.s['3']++;Y.namespace('M.atto_multilang2').Button=Y.Base.create('button',Y.M.editor_atto.EditorPlugin,[],{_highlight:true,initializer:function(){__cov_xlcucJyua6odXhLRctX6xg.f['2']++;__cov_xlcucJyua6odXhLRctX6xg.s['4']++;var hascapability=this.get(ATTR_CAPABILITY),toolbarItems,host,form;__cov_xlcucJyua6odXhLRctX6xg.s['5']++;if(hascapability){__cov_xlcucJyua6odXhLRctX6xg.b['1'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['6']++;toolbarItems=this._initializeToolbarItems();__cov_xlcucJyua6odXhLRctX6xg.s['7']++;this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:'icon',iconComponent:'atto_multilang2',items:toolbarItems});__cov_xlcucJyua6odXhLRctX6xg.s['8']++;this._tagTemplate=TEMPLATES.NOT_SPANNED;__cov_xlcucJyua6odXhLRctX6xg.s['9']++;this._highlight=this.get(ATTR_HIGHLIGHT);__cov_xlcucJyua6odXhLRctX6xg.s['10']++;if(this._highlight){__cov_xlcucJyua6odXhLRctX6xg.b['2'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['11']++;this._tagTemplate=TEMPLATES.SPANNED;__cov_xlcucJyua6odXhLRctX6xg.s['12']++;host=this.get('host');__cov_xlcucJyua6odXhLRctX6xg.s['13']++;form=host.textarea.ancestor('form');__cov_xlcucJyua6odXhLRctX6xg.s['14']++;if(form){__cov_xlcucJyua6odXhLRctX6xg.b['3'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['15']++;form.on('submit',this._cleanMlangTags,this);}else{__cov_xlcucJyua6odXhLRctX6xg.b['3'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['16']++;this.get('host').on('atto:selectionchanged',this._checkSelectionChange,this);__cov_xlcucJyua6odXhLRctX6xg.s['17']++;this.get('host').on('pluginsloaded',this._addHighlightingCss,this);__cov_xlcucJyua6odXhLRctX6xg.s['18']++;this.get('host').on('pluginsloaded',this._highlightMlangTags,this);__cov_xlcucJyua6odXhLRctX6xg.s['19']++;this._hookUpdateOriginal();__cov_xlcucJyua6odXhLRctX6xg.s['20']++;this._hookUpdateFromTextArea();}else{__cov_xlcucJyua6odXhLRctX6xg.b['2'][1]++;}}else{__cov_xlcucJyua6odXhLRctX6xg.b['1'][1]++;}},_initializeToolbarItems:function(){__cov_xlcucJyua6odXhLRctX6xg.f['3']++;__cov_xlcucJyua6odXhLRctX6xg.s['21']++;var toolbarItems=[],languages,langCode;__cov_xlcucJyua6odXhLRctX6xg.s['22']++;languages=JSON.parse(this.get(ATTR_LANGUAGES));__cov_xlcucJyua6odXhLRctX6xg.s['23']++;for(langCode in languages){__cov_xlcucJyua6odXhLRctX6xg.s['24']++;if(languages.hasOwnProperty(langCode)){__cov_xlcucJyua6odXhLRctX6xg.b['4'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['25']++;toolbarItems.push({text:languages[langCode],callbackArgs:langCode});}else{__cov_xlcucJyua6odXhLRctX6xg.b['4'][1]++;}}__cov_xlcucJyua6odXhLRctX6xg.s['26']++;return toolbarItems;},_addHighlightingCss:function(){__cov_xlcucJyua6odXhLRctX6xg.f['4']++;__cov_xlcucJyua6odXhLRctX6xg.s['27']++;var css='.'+CLASSES.TAG+' {'+this.get(ATTR_CSS)+'}',style;__cov_xlcucJyua6odXhLRctX6xg.s['28']++;style=document.createElement('style');__cov_xlcucJyua6odXhLRctX6xg.s['29']++;style.type='text/css';__cov_xlcucJyua6odXhLRctX6xg.s['30']++;style.innerHTML=css;__cov_xlcucJyua6odXhLRctX6xg.s['31']++;document.head.appendChild(style);},_hookUpdateOriginal:function(){__cov_xlcucJyua6odXhLRctX6xg.f['5']++;__cov_xlcucJyua6odXhLRctX6xg.s['32']++;var host=this.get('host'),multilangplugin=this;__cov_xlcucJyua6odXhLRctX6xg.s['33']++;host.updateOriginal=function(){__cov_xlcucJyua6odXhLRctX6xg.f['6']++;__cov_xlcucJyua6odXhLRctX6xg.s['34']++;var _updateOriginal=host.updateOriginal;__cov_xlcucJyua6odXhLRctX6xg.s['35']++;return function(){__cov_xlcucJyua6odXhLRctX6xg.f['7']++;__cov_xlcucJyua6odXhLRctX6xg.s['36']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['6'][0]++,multilangplugin._highlight)&&(__cov_xlcucJyua6odXhLRctX6xg.b['6'][1]++,this.updateOriginal.caller===host.plugins.html._showHTML)){__cov_xlcucJyua6odXhLRctX6xg.b['5'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['37']++;multilangplugin.editor.setHTML(multilangplugin._getHTMLwithCleanedTags(multilangplugin.editor.getHTML()));}else{__cov_xlcucJyua6odXhLRctX6xg.b['5'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['38']++;return _updateOriginal.apply(this,arguments);};}();},_hookUpdateFromTextArea:function(){__cov_xlcucJyua6odXhLRctX6xg.f['8']++;__cov_xlcucJyua6odXhLRctX6xg.s['39']++;var host=this.get('host'),multilangplugin=this;__cov_xlcucJyua6odXhLRctX6xg.s['40']++;host.updateFromTextArea=function(){__cov_xlcucJyua6odXhLRctX6xg.f['9']++;__cov_xlcucJyua6odXhLRctX6xg.s['41']++;var _updateFromTextArea=host.updateFromTextArea;__cov_xlcucJyua6odXhLRctX6xg.s['42']++;return function(){__cov_xlcucJyua6odXhLRctX6xg.f['10']++;__cov_xlcucJyua6odXhLRctX6xg.s['43']++;var ret=_updateFromTextArea.apply(this,arguments);__cov_xlcucJyua6odXhLRctX6xg.s['44']++;if((__cov_xlcucJyua6odXhLRctX6xg.b['8'][0]++,multilangplugin._highlight)&&(__cov_xlcucJyua6odXhLRctX6xg.b['8'][1]++,this.updateFromTextArea.caller===host.plugins.html._showHTML)){__cov_xlcucJyua6odXhLRctX6xg.b['7'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['45']++;multilangplugin._highlightMlangTags();}else{__cov_xlcucJyua6odXhLRctX6xg.b['7'][1]++;}__cov_xlcucJyua6odXhLRctX6xg.s['46']++;return ret;};}();},_addTags:function(event,langCode){__cov_xlcucJyua6odXhLRctX6xg.f['11']++;__cov_xlcucJyua6odXhLRctX6xg.s['47']++;var selection,host=this.get('host'),taggedContent,content;__cov_xlcucJyua6odXhLRctX6xg.s['48']++;taggedContent=this._tagTemplate;__cov_xlcucJyua6odXhLRctX6xg.s['49']++;selection=this._getSelectionHTML();__cov_xlcucJyua6odXhLRctX6xg.s['50']++;content=host.getSelection().toString().length===0?(__cov_xlcucJyua6odXhLRctX6xg.b['9'][0]++,' '):(__cov_xlcucJyua6odXhLRctX6xg.b['9'][1]++,selection);__cov_xlcucJyua6odXhLRctX6xg.s['51']++;taggedContent=taggedContent.replace(LANG_WILDCARD,langCode);__cov_xlcucJyua6odXhLRctX6xg.s['52']++;taggedContent=taggedContent.replace(CONTENT_WILDCARD,content);__cov_xlcucJyua6odXhLRctX6xg.s['53']++;host.insertContentAtFocusPoint(taggedContent);__cov_xlcucJyua6odXhLRctX6xg.s['54']++;this.markUpdated();},_getSelectionHTML:function(){__cov_xlcucJyua6odXhLRctX6xg.f['12']++;__cov_xlcucJyua6odXhLRctX6xg.s['55']++;var html='',selection,container,index,length;__cov_xlcucJyua6odXhLRctX6xg.s['56']++;if(typeof window.getSelection!=='undefined'){__cov_xlcucJyua6odXhLRctX6xg.b['10'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['57']++;selection=window.getSelection();__cov_xlcucJyua6odXhLRctX6xg.s['58']++;if(selection.rangeCount){__cov_xlcucJyua6odXhLRctX6xg.b['11'][0]++;__cov_xlcucJyua6odXhLRctX6xg.s['59']++;container=document.createElement('div');__cov_xlcucJyua6odXhLRctX6xg.s['60']++;for(index=0,length=selection.rangeCount;index',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller===e.plugins.html._showHTML&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var r=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller===e.plugins.html._showHTML&&t._highlightMlangTags(),r}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r',d="",v={SPANNED:" "+p+"{mlang "+r+"}"+d+i+p+"{mlang}"+d+" ",NOT_SPANNED:"{mlang "+r+"}"+i+"{mlang}"};e.namespace("M.atto_multilang2").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_highlight:!0,initializer:function(){var e=this.get(o),t,n,r;e&&(t=this._initializeToolbarItems(),this.addToolbarMenu({globalItemConfig:{callback:this._addTags},icon:"icon",iconComponent:"atto_multilang2",items:t}),this._tagTemplate=v.NOT_SPANNED,this._highlight=this.get(u),this._highlight&&(this._tagTemplate=v.SPANNED,n=this.get("host"),r=n.textarea.ancestor("form"),r&&r.on("submit",this._cleanMlangTags,this),this.get("host").on("atto:selectionchanged",this._checkSelectionChange,this),this.get("host").on("pluginsloaded",this._addHighlightingCss,this),this.get("host").on("pluginsloaded",this._highlightMlangTags,this),this._hookUpdateOriginal(),this._hookUpdateFromTextArea()))},_initializeToolbarItems:function(){var e=[],t,n;t=JSON.parse(this.get(s));for(n in t)t.hasOwnProperty(n)&&e.push({text:t[n],callbackArgs:n});return e},_addHighlightingCss:function(){var e="."+n.TAG+" {"+this.get(a)+"}",t;t=document.createElement("style"),t.type="text/css",t.innerHTML=e,document.head.appendChild(t)},_hookUpdateOriginal:function(){var e=this.get("host"),t=this;e.updateOriginal=function(){var n=e.updateOriginal;return function(){return t._highlight&&this.updateOriginal.caller===e.plugins.html._showHTML&&t.editor.setHTML(t._getHTMLwithCleanedTags(t.editor.getHTML())),n.apply(this,arguments)}}()},_hookUpdateFromTextArea:function(){var e=this.get("host"),t=this;e.updateFromTextArea=function(){var n=e.updateFromTextArea;return function(){var r=n.apply(this,arguments);return t._highlight&&this.updateFromTextArea.caller===e.plugins.html._showHTML&&t._highlightMlangTags(),r}}()},_addTags:function(e,t){var n,s=this.get("host"),o,u;o=this._tagTemplate,n=this._getSelectionHTML(),u=s.getSelection().toString().length===0?" ":n,o=o.replace(r,t),o=o.replace(i,u),s.insertContentAtFocusPoint(o),this.markUpdated()},_getSelectionHTML:function(){var e="",t,n,r,i;if(typeof window.getSelection!="undefined"){t=window.getSelection();if(t.rangeCount){n=document.createElement("div");for(r=0,i=t.rangeCount;r