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