';
+$string['browserepositories'] = 'Browse repositories...';
+$string['createimage'] = 'Insert image';
+$string['enteralt'] = 'Enter alternative text';
+$string['enterurl'] = 'Enter URL';
+$string['height'] = 'Height';
+$string['presentation'] = 'Presentation content only';
+$string['pluginname'] = 'Image';
+$string['presentationoraltrequired'] = 'Images must be marked as presentation only, or have suitable alternative text.';
+$string['preview'] = 'Preview';
+$string['width'] = 'Width';
diff --git a/lib/editor/atto/plugins/image/lib.php b/lib/editor/atto/plugins/image/lib.php
new file mode 100644
index 00000000000..66136465a9b
--- /dev/null
+++ b/lib/editor/atto/plugins/image/lib.php
@@ -0,0 +1,58 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_image
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_image_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/insert_edit_image', 'editor_atto');
+
+ $PAGE->requires->strings_for_js(array('createimage',
+ 'enterurl',
+ 'enteralt',
+ 'presentation',
+ 'preview',
+ 'browserepositories',
+ 'accessibilityhint',
+ 'presentationoraltrequired',
+ 'width',
+ 'height'), 'atto_image');
+ $PAGE->requires->yui_module('moodle-atto_image-button',
+ 'M.atto_image.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'file')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_image_sort_order() {
+ return 13;
+}
diff --git a/lib/editor/atto/plugins/image/version.php b/lib/editor/atto/plugins/image/version.php
new file mode 100644
index 00000000000..0c628b727db
--- /dev/null
+++ b/lib/editor/atto/plugins/image/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_image
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_image'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
new file mode 100644
index 00000000000..58d3ee3b51b
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
@@ -0,0 +1,250 @@
+YUI.add('moodle-atto_image-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 .
+
+/**
+ * Atto text editor image plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_image = M.atto_image || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_image.selection = M.editor_atto.get_selection();
+ if (M.atto_image.selection !== false) {
+ var dialogue;
+ if (!M.atto_image.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_image.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_image.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createimage', 'atto_image'));
+ dialogue.render();
+ dialogue.centerDialogue();
+ M.atto_image.dialogue = dialogue;
+
+ var selectedText = M.editor_atto.get_selection_text();
+ var i = 0;
+
+ var images = [];
+ for (i = 0; i < selectedText.childNodes.length; i++) {
+ var child = selectedText.childNodes[0];
+ if (images.length === 0) {
+ if (child.nodeName.toLowerCase() === 'img') {
+ images[0] = child;
+ } else {
+ if (child.getElementsByTagName) {
+ images = child.getElementsByTagName('img');
+ }
+ }
+ }
+ }
+
+ if (images.length > 0) {
+ var image = Y.one(images[0]);
+ var width = image.getAttribute('width');
+ var height = image.getAttribute('height');
+ if (width > 0) {
+ Y.one('#atto_image_widthentry').set('value', width);
+ }
+ if (height > 0) {
+ Y.one('#atto_image_heightentry').set('value', height);
+ }
+ Y.one('#atto_image_preview').set('src', image.get('src'));
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ Y.one('#atto_image_altentry').set('value', image.get('alt'));
+ Y.one('#atto_image_urlentry').set('value', image.get('src'));
+ var role = image.get('role');
+ if (role === "presentation") {
+ Y.one('#atto_image_presentation').set('checked', 'checked');
+ }
+ }
+ dialogue.show();
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ },
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'image', M.atto_image.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_image_urlentry');
+ input.set('value', params.url);
+
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ Y.one('#atto_image_widthentry').set('value', this.width);
+ Y.one('#atto_image_heightentry').set('value', this.height);
+ Y.one('#atto_image_preview').set('src', this.src);
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ };
+ image.src = params.url;
+ }
+ },
+ url_changed : function() {
+ var input = Y.one('#atto_image_urlentry');
+
+ if (input.get('value') !== '') {
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ var input;
+
+ input = Y.one('#atto_image_widthentry');
+ if (input.get('value') === '') {
+ input.set('value', this.width);
+ }
+ input = Y.one('#atto_image_heightentry');
+ if (input.get('value') === '') {
+ input.set('value', this.height);
+ }
+ input = Y.one('#atto_image_preview');
+ input.set('src', this.src);
+ input.setStyle('display', 'inline');
+ };
+ image.src = input.get('value');
+ }
+ },
+ set_image : function(e, elementid) {
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_image_urlentry');
+
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ var alt = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_widthentry');
+ var width = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_heightentry');
+ var height = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ var presentation = input.get('checked');
+ var alrt;
+
+ e.preventDefault();
+
+ if (alt === '' && !presentation) {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'block');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', true);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', true);
+ return;
+ } else {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'none');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', false);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', false);
+ }
+
+ M.atto_image.dialogue.hide();
+
+ if (url !== '') {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ var imagehtml = '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(imagehtml);
+ } else {
+ document.execCommand('insertHTML', false, imagehtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_image'));
+
+ content.one('#atto_image_urlentry').on('blur', M.atto_image.url_changed, this);
+ content.one('#atto_image_urlentrysubmit').on('click', M.atto_image.set_image, this, elementid);
+ content.one('#openimagebrowser').on('click', M.atto_image.open_filepicker);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
new file mode 100644
index 00000000000..8532ee5fe98
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_image-button",function(e,t){M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(t){var n=function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var r;M.atto_image.dialogue?r=M.atto_image.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),r.set("bodyContent",M.atto_image.get_form_content(n)),r.set("headerContent",M.util.get_string("createimage","atto_image")),r.render(),r.centerDialogue(),M.atto_image.dialogue=r;var i=M.editor_atto.get_selection_text(),s=0,o=[];for(s=0;s0){var a=e.one(o[0]),f=a.getAttribute("width"),l=a.getAttribute("height");f>0&&e.one("#atto_image_widthentry").set("value",f),l>0&&e.one("#atto_image_heightentry").set("value",l),e.one("#atto_image_preview").set("src",a.get("src")),e.one("#atto_image_preview").setStyle("display","inline"),e.one("#atto_image_altentry").set("value",a.get("alt")),e.one("#atto_image_urlentry").set("value",a.get("src"));var c=a.get("role");c==="presentation"&&e.one("#atto_image_presentation").set("checked","checked")}r.show()}};M.editor_atto.add_toolbar_button(t.elementid,"image",t.icon,t.group,n,this)},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_image_urlentry");n.set("value",t.url);var r=new Image;r.onload=function(){e.one("#atto_image_widthentry").set("value",this.width),e.one("#atto_image_heightentry").set("value",this.height),e.one("#atto_image_preview").set("src",this.src),e.one("#atto_image_preview").setStyle("display","inline")},r.src=t.url}},url_changed:function(){var t=e.one("#atto_image_urlentry");if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#atto_image_widthentry"),t.get("value")===""&&t.set("value",this.width),t=e.one("#atto_image_heightentry"),t.get("value")===""&&t.set("value",this.height),t=e.one("#atto_image_preview"),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var r=t.currentTarget.ancestor(".atto_form").one("#atto_image_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry");var s=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_widthentry");var o=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_heightentry");var u=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation");var a=r.get("checked"),f;t.preventDefault();if(s===""&&!a){f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","block"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!0),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!0);return}f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","none"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!1),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide();if(i!==""){M.editor_atto.set_selection(M.atto_image.selection);var l='",document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(l):document.execCommand("insertHTML",!1,l),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_image"));return n.one("#atto_image_urlentry").on("blur",M.atto_image.url_changed,this),n.one("#atto_image_urlentrysubmit").on("click",M.atto_image.set_image,this,t),n.one("#openimagebrowser").on("click",M.atto_image.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
new file mode 100644
index 00000000000..58d3ee3b51b
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
@@ -0,0 +1,250 @@
+YUI.add('moodle-atto_image-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 .
+
+/**
+ * Atto text editor image plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_image = M.atto_image || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_image.selection = M.editor_atto.get_selection();
+ if (M.atto_image.selection !== false) {
+ var dialogue;
+ if (!M.atto_image.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_image.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_image.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createimage', 'atto_image'));
+ dialogue.render();
+ dialogue.centerDialogue();
+ M.atto_image.dialogue = dialogue;
+
+ var selectedText = M.editor_atto.get_selection_text();
+ var i = 0;
+
+ var images = [];
+ for (i = 0; i < selectedText.childNodes.length; i++) {
+ var child = selectedText.childNodes[0];
+ if (images.length === 0) {
+ if (child.nodeName.toLowerCase() === 'img') {
+ images[0] = child;
+ } else {
+ if (child.getElementsByTagName) {
+ images = child.getElementsByTagName('img');
+ }
+ }
+ }
+ }
+
+ if (images.length > 0) {
+ var image = Y.one(images[0]);
+ var width = image.getAttribute('width');
+ var height = image.getAttribute('height');
+ if (width > 0) {
+ Y.one('#atto_image_widthentry').set('value', width);
+ }
+ if (height > 0) {
+ Y.one('#atto_image_heightentry').set('value', height);
+ }
+ Y.one('#atto_image_preview').set('src', image.get('src'));
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ Y.one('#atto_image_altentry').set('value', image.get('alt'));
+ Y.one('#atto_image_urlentry').set('value', image.get('src'));
+ var role = image.get('role');
+ if (role === "presentation") {
+ Y.one('#atto_image_presentation').set('checked', 'checked');
+ }
+ }
+ dialogue.show();
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ },
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'image', M.atto_image.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_image_urlentry');
+ input.set('value', params.url);
+
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ Y.one('#atto_image_widthentry').set('value', this.width);
+ Y.one('#atto_image_heightentry').set('value', this.height);
+ Y.one('#atto_image_preview').set('src', this.src);
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ };
+ image.src = params.url;
+ }
+ },
+ url_changed : function() {
+ var input = Y.one('#atto_image_urlentry');
+
+ if (input.get('value') !== '') {
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ var input;
+
+ input = Y.one('#atto_image_widthentry');
+ if (input.get('value') === '') {
+ input.set('value', this.width);
+ }
+ input = Y.one('#atto_image_heightentry');
+ if (input.get('value') === '') {
+ input.set('value', this.height);
+ }
+ input = Y.one('#atto_image_preview');
+ input.set('src', this.src);
+ input.setStyle('display', 'inline');
+ };
+ image.src = input.get('value');
+ }
+ },
+ set_image : function(e, elementid) {
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_image_urlentry');
+
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ var alt = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_widthentry');
+ var width = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_heightentry');
+ var height = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ var presentation = input.get('checked');
+ var alrt;
+
+ e.preventDefault();
+
+ if (alt === '' && !presentation) {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'block');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', true);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', true);
+ return;
+ } else {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'none');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', false);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', false);
+ }
+
+ M.atto_image.dialogue.hide();
+
+ if (url !== '') {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ var imagehtml = '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(imagehtml);
+ } else {
+ document.execCommand('insertHTML', false, imagehtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_image'));
+
+ content.one('#atto_image_urlentry').on('blur', M.atto_image.url_changed, this);
+ content.one('#atto_image_urlentrysubmit').on('click', M.atto_image.set_image, this, elementid);
+ content.one('#openimagebrowser').on('click', M.atto_image.open_filepicker);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/src/button/build.json b/lib/editor/atto/plugins/image/yui/src/button/build.json
new file mode 100644
index 00000000000..15fda37bfd5
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_image-button",
+ "builds": {
+ "moodle-atto_image-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
new file mode 100644
index 00000000000..ea9fa214958
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
@@ -0,0 +1,245 @@
+// 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 .
+
+/**
+ * Atto text editor image plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_image = M.atto_image || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_image.selection = M.editor_atto.get_selection();
+ if (M.atto_image.selection !== false) {
+ var dialogue;
+ if (!M.atto_image.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_image.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_image.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createimage', 'atto_image'));
+ dialogue.render();
+ dialogue.centerDialogue();
+ M.atto_image.dialogue = dialogue;
+
+ var selectedText = M.editor_atto.get_selection_text();
+ var i = 0;
+
+ var images = [];
+ for (i = 0; i < selectedText.childNodes.length; i++) {
+ var child = selectedText.childNodes[0];
+ if (images.length === 0) {
+ if (child.nodeName.toLowerCase() === 'img') {
+ images[0] = child;
+ } else {
+ if (child.getElementsByTagName) {
+ images = child.getElementsByTagName('img');
+ }
+ }
+ }
+ }
+
+ if (images.length > 0) {
+ var image = Y.one(images[0]);
+ var width = image.getAttribute('width');
+ var height = image.getAttribute('height');
+ if (width > 0) {
+ Y.one('#atto_image_widthentry').set('value', width);
+ }
+ if (height > 0) {
+ Y.one('#atto_image_heightentry').set('value', height);
+ }
+ Y.one('#atto_image_preview').set('src', image.get('src'));
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ Y.one('#atto_image_altentry').set('value', image.get('alt'));
+ Y.one('#atto_image_urlentry').set('value', image.get('src'));
+ var role = image.get('role');
+ if (role === "presentation") {
+ Y.one('#atto_image_presentation').set('checked', 'checked');
+ }
+ }
+ dialogue.show();
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ },
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'image', M.atto_image.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_image_urlentry');
+ input.set('value', params.url);
+
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ Y.one('#atto_image_widthentry').set('value', this.width);
+ Y.one('#atto_image_heightentry').set('value', this.height);
+ Y.one('#atto_image_preview').set('src', this.src);
+ Y.one('#atto_image_preview').setStyle('display', 'inline');
+ };
+ image.src = params.url;
+ }
+ },
+ url_changed : function() {
+ var input = Y.one('#atto_image_urlentry');
+
+ if (input.get('value') !== '') {
+ // Auto set the width and height.
+ var image = new Image();
+ image.onload = function() {
+ var input;
+
+ input = Y.one('#atto_image_widthentry');
+ if (input.get('value') === '') {
+ input.set('value', this.width);
+ }
+ input = Y.one('#atto_image_heightentry');
+ if (input.get('value') === '') {
+ input.set('value', this.height);
+ }
+ input = Y.one('#atto_image_preview');
+ input.set('src', this.src);
+ input.setStyle('display', 'inline');
+ };
+ image.src = input.get('value');
+ }
+ },
+ set_image : function(e, elementid) {
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_image_urlentry');
+
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ var alt = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_widthentry');
+ var width = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_heightentry');
+ var height = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ var presentation = input.get('checked');
+ var alrt;
+
+ e.preventDefault();
+
+ if (alt === '' && !presentation) {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'block');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', true);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', true);
+ return;
+ } else {
+ alrt = e.currentTarget.ancestor('.atto_form').one('#atto_image_altwarning');
+ alrt.setStyle('display', 'none');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_altentry');
+ input.setAttribute('aria-invalid', false);
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_image_presentation');
+ input.setAttribute('aria-invalid', false);
+ }
+
+ M.atto_image.dialogue.hide();
+
+ if (url !== '') {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ var imagehtml = '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(imagehtml);
+ } else {
+ document.execCommand('insertHTML', false, imagehtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_image'));
+
+ content.one('#atto_image_urlentry').on('blur', M.atto_image.url_changed, this);
+ content.one('#atto_image_urlentrysubmit').on('click', M.atto_image.set_image, this, elementid);
+ content.one('#openimagebrowser').on('click', M.atto_image.open_filepicker);
+ return content;
+ }
+};
diff --git a/lib/editor/atto/plugins/image/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/image/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..3a97310a174
--- /dev/null
+++ b/lib/editor/atto/plugins/image/yui/src/button/meta/editor.json
@@ -0,0 +1,8 @@
+{
+ "moodle-atto_image-button": {
+ "requires": [
+ "node",
+ "escape"
+ ]
+ }
+}
diff --git a/lib/editor/atto/plugins/indent/lang/en/atto_indent.php b/lib/editor/atto/plugins/indent/lang/en/atto_indent.php
new file mode 100644
index 00000000000..9c5a3f0ac3e
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/lang/en/atto_indent.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_indent', language 'en'.
+ *
+ * @package atto_indent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Indent';
diff --git a/lib/editor/atto/plugins/indent/lib.php b/lib/editor/atto/plugins/indent/lib.php
new file mode 100644
index 00000000000..4bf3d743e2e
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/lib.php
@@ -0,0 +1,48 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_indent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_indent_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/increase_indent', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_indent-button',
+ 'M.atto_indent.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_indent_sort_order() {
+ return 5;
+}
diff --git a/lib/editor/atto/plugins/indent/version.php b/lib/editor/atto/plugins/indent/version.php
new file mode 100644
index 00000000000..e7aeaa0f877
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_indent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_indent'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
new file mode 100644
index 00000000000..f952bb08a68
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_indent-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 .
+
+/**
+ * Atto text editor indent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_indent = M.atto_indent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('indent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
new file mode 100644
index 00000000000..cd35231daca
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"indent",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
new file mode 100644
index 00000000000..f952bb08a68
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_indent-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 .
+
+/**
+ * Atto text editor indent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_indent = M.atto_indent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('indent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/build.json b/lib/editor/atto/plugins/indent/yui/src/button/build.json
new file mode 100644
index 00000000000..29b8001d1d0
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_indent-button",
+ "builds": {
+ "moodle-atto_indent-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
new file mode 100644
index 00000000000..79b73a541da
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor indent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_indent = M.atto_indent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('indent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/indent/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..812a3bc7858
--- /dev/null
+++ b/lib/editor/atto/plugins/indent/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_indent-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/italic/lang/en/atto_italic.php b/lib/editor/atto/plugins/italic/lang/en/atto_italic.php
new file mode 100644
index 00000000000..74cadb07df5
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/lang/en/atto_italic.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_italic', language 'en'.
+ *
+ * @package atto_italic
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Italic';
diff --git a/lib/editor/atto/plugins/italic/lib.php b/lib/editor/atto/plugins/italic/lib.php
new file mode 100644
index 00000000000..e8e93aafd9b
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/lib.php
@@ -0,0 +1,48 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_italic
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_italic_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/italic', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_italic-button',
+ 'M.atto_italic.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_italic_sort_order() {
+ return 1;
+}
diff --git a/lib/editor/atto/plugins/italic/version.php b/lib/editor/atto/plugins/italic/version.php
new file mode 100644
index 00000000000..d11d1c519ff
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_italic
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_italic'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
new file mode 100644
index 00000000000..1cab99f2fc0
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_italic-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 .
+
+/**
+ * Atto text editor italic plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_italic = M.atto_italic || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('italic', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
new file mode 100644
index 00000000000..545816717d3
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_italic-button",function(e,t){M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"italic",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
new file mode 100644
index 00000000000..1cab99f2fc0
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_italic-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 .
+
+/**
+ * Atto text editor italic plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_italic = M.atto_italic || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('italic', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/build.json b/lib/editor/atto/plugins/italic/yui/src/button/build.json
new file mode 100644
index 00000000000..d7df4fc2ee0
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_italic-button",
+ "builds": {
+ "moodle-atto_italic-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
new file mode 100644
index 00000000000..349ad9426da
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor italic plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_italic = M.atto_italic || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('italic', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/italic/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..4109b10ce61
--- /dev/null
+++ b/lib/editor/atto/plugins/italic/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_italic-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/link/lang/en/atto_link.php b/lib/editor/atto/plugins/link/lang/en/atto_link.php
new file mode 100644
index 00000000000..83f286877f0
--- /dev/null
+++ b/lib/editor/atto/plugins/link/lang/en/atto_link.php
@@ -0,0 +1,30 @@
+.
+
+/**
+ * Strings for component 'atto_link', language 'en'.
+ *
+ * @package atto_link
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['accessibilityhint'] = '
Accessibility recommendation
A mechanism is available to allow the purpose of each link to be identified from link text alone, except where the purpose of the link would be ambiguous to users in general.
';
+$string['browserepositories'] = 'Browse repositories...';
+$string['createlink'] = 'Create link';
+$string['enterurl'] = 'Enter a URL';
+$string['openinnewwindow'] = 'Open in new window';
+$string['pluginname'] = 'Link';
diff --git a/lib/editor/atto/plugins/link/lib.php b/lib/editor/atto/plugins/link/lib.php
new file mode 100644
index 00000000000..6461998a679
--- /dev/null
+++ b/lib/editor/atto/plugins/link/lib.php
@@ -0,0 +1,55 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_link
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_link_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/insert_edit_link', 'editor_atto');
+
+ $PAGE->requires->strings_for_js(array('createlink',
+ 'enterurl',
+ 'browserepositories',
+ 'openinnewwindow',
+ 'accessibilityhint'),
+ 'atto_link');
+
+ $PAGE->requires->yui_module('moodle-atto_link-button',
+ 'M.atto_link.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'link')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_link_sort_order() {
+ return 11;
+}
diff --git a/lib/editor/atto/plugins/link/version.php b/lib/editor/atto/plugins/link/version.php
new file mode 100644
index 00000000000..120b3bfd9f9
--- /dev/null
+++ b/lib/editor/atto/plugins/link/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_link
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_link'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
new file mode 100644
index 00000000000..e51f120a7d1
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
@@ -0,0 +1,268 @@
+YUI.add('moodle-atto_link-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 .
+
+/**
+ * Atto text editor link plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_link = M.atto_link || {
+ /**
+ * The window used to get the link details.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * The selection object returned by the browser.
+ *
+ * @property selection
+ * @type Range
+ * @default null
+ */
+ selection : null,
+
+ /**
+ * Display the chooser dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_chooser : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_link.selection = M.editor_atto.get_selection();
+ if (M.atto_link.selection !== false && (!M.atto_link.selection.collapsed)) {
+ var dialogue;
+ if (!M.atto_link.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_link.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_link.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createlink', 'atto_link'));
+
+ M.atto_link.resolve_anchors();
+
+ dialogue.show();
+ M.atto_link.dialogue = dialogue;
+ }
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ },
+
+ /**
+ * If there is selected text and it is part of an anchor link,
+ * extract the url (and target) from the link (and set them in the form).
+ *
+ * @method resolve_anchors
+ */
+ resolve_anchors : function() {
+ // Find the first anchor tag in the selection.
+ var selectednode = M.editor_atto.get_selection_parent_node(),
+ anchornodes,
+ anchornode,
+ url;
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+
+ if (anchornodes.length > 0) {
+ anchornode = anchornodes[0];
+ M.atto_link.selection = M.editor_atto.get_selection_from_node(anchornode);
+ url = anchornode.getAttribute('href');
+ target = anchornode.getAttribute('target');
+ if (url !== '') {
+ Y.one('#atto_link_urlentry').set('value', url);
+ }
+ if (target === '_blank') {
+ Y.one('#atto_link_openinnewwindow').set('checked', 'checked');
+ } else {
+ Y.one('#atto_link_openinnewwindow').set('checked', '');
+ }
+ }
+ },
+
+ /**
+ * Open the repository file picker.
+ *
+ * @method open_filepicker
+ * @param Event e
+ */
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'link', M.atto_link.filepicker_callback);
+ },
+
+ /**
+ * Called by the file picker when a link has been chosen.
+ *
+ * @method filepicker_callback
+ * @param {Object} params - contains selected url.
+ */
+ filepicker_callback : function(params) {
+ M.atto_link.dialogue.hide();
+ if (params.url !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, params.url);
+ }
+ },
+
+ /**
+ * The OK button has been pressed - make the changes to the source.
+ *
+ * @method set_link
+ * @param Event e
+ */
+ set_link : function(e, elementid) {
+ var input,
+ target,
+ selectednode,
+ anchornodes,
+ value;
+
+ e.preventDefault();
+ M.atto_link.dialogue.hide();
+
+ input = e.currentTarget.ancestor('.atto_form').one('input[type=url]');
+
+ value = input.get('value');
+ if (value !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, value);
+
+ // Now set the target.
+ selectednode = M.editor_atto.get_selection_parent_node();
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+ Y.Array.each(anchornodes, function(anchornode) {
+ target = e.currentTarget.ancestor('.atto_form').one('input[type=checkbox]');
+ if (target.get('checked')) {
+ anchornode.setAttribute('target', '_blank');
+ } else {
+ anchornode.removeAttribute('target');
+ }
+ });
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+
+ /**
+ * Look up and down for the nearest anchor tags that are least partly contained in the selection.
+ *
+ * @method find_selected_anchors
+ * @param Node node
+ * @return Node|false
+ */
+ find_selected_anchors : function(node) {
+ var tagname = node.get('tagName'), hit, hits;
+ // Direct hit.
+ if (tagname && tagname.toLowerCase() === 'a') {
+ return [node];
+ }
+ // Search down but check that each node is part of the selection.
+ hits = [];
+ node.all('a').each(function(n) {
+ if (!hit && M.editor_atto.selection_contains_node(n)) {
+ hits.push(n);
+ }
+ });
+ if (hits.length > 0) {
+ return hits;
+ }
+ // Search up.
+ hit = node.ancestor('a');
+ if (hit) {
+ return [hit];
+ }
+ return [];
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_form_content
+ * @param string elementid
+ * @return string
+ */
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_link'));
+
+ content.one('#atto_link_urlentrysubmit').on('click', M.atto_link.set_link, this, elementid);
+ content.one('#openlinkbrowser').on('click', M.atto_link.open_filepicker);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
new file mode 100644
index 00000000000..0d59b38f03f
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_link-button",function(e,t){M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){M.editor_atto.add_toolbar_button(e.elementid,"link",e.icon,e.group,this.display_chooser,this)},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_link"));return n.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),n.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
new file mode 100644
index 00000000000..e51f120a7d1
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
@@ -0,0 +1,268 @@
+YUI.add('moodle-atto_link-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 .
+
+/**
+ * Atto text editor link plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_link = M.atto_link || {
+ /**
+ * The window used to get the link details.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * The selection object returned by the browser.
+ *
+ * @property selection
+ * @type Range
+ * @default null
+ */
+ selection : null,
+
+ /**
+ * Display the chooser dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_chooser : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_link.selection = M.editor_atto.get_selection();
+ if (M.atto_link.selection !== false && (!M.atto_link.selection.collapsed)) {
+ var dialogue;
+ if (!M.atto_link.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_link.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_link.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createlink', 'atto_link'));
+
+ M.atto_link.resolve_anchors();
+
+ dialogue.show();
+ M.atto_link.dialogue = dialogue;
+ }
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ },
+
+ /**
+ * If there is selected text and it is part of an anchor link,
+ * extract the url (and target) from the link (and set them in the form).
+ *
+ * @method resolve_anchors
+ */
+ resolve_anchors : function() {
+ // Find the first anchor tag in the selection.
+ var selectednode = M.editor_atto.get_selection_parent_node(),
+ anchornodes,
+ anchornode,
+ url;
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+
+ if (anchornodes.length > 0) {
+ anchornode = anchornodes[0];
+ M.atto_link.selection = M.editor_atto.get_selection_from_node(anchornode);
+ url = anchornode.getAttribute('href');
+ target = anchornode.getAttribute('target');
+ if (url !== '') {
+ Y.one('#atto_link_urlentry').set('value', url);
+ }
+ if (target === '_blank') {
+ Y.one('#atto_link_openinnewwindow').set('checked', 'checked');
+ } else {
+ Y.one('#atto_link_openinnewwindow').set('checked', '');
+ }
+ }
+ },
+
+ /**
+ * Open the repository file picker.
+ *
+ * @method open_filepicker
+ * @param Event e
+ */
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'link', M.atto_link.filepicker_callback);
+ },
+
+ /**
+ * Called by the file picker when a link has been chosen.
+ *
+ * @method filepicker_callback
+ * @param {Object} params - contains selected url.
+ */
+ filepicker_callback : function(params) {
+ M.atto_link.dialogue.hide();
+ if (params.url !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, params.url);
+ }
+ },
+
+ /**
+ * The OK button has been pressed - make the changes to the source.
+ *
+ * @method set_link
+ * @param Event e
+ */
+ set_link : function(e, elementid) {
+ var input,
+ target,
+ selectednode,
+ anchornodes,
+ value;
+
+ e.preventDefault();
+ M.atto_link.dialogue.hide();
+
+ input = e.currentTarget.ancestor('.atto_form').one('input[type=url]');
+
+ value = input.get('value');
+ if (value !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, value);
+
+ // Now set the target.
+ selectednode = M.editor_atto.get_selection_parent_node();
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+ Y.Array.each(anchornodes, function(anchornode) {
+ target = e.currentTarget.ancestor('.atto_form').one('input[type=checkbox]');
+ if (target.get('checked')) {
+ anchornode.setAttribute('target', '_blank');
+ } else {
+ anchornode.removeAttribute('target');
+ }
+ });
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+
+ /**
+ * Look up and down for the nearest anchor tags that are least partly contained in the selection.
+ *
+ * @method find_selected_anchors
+ * @param Node node
+ * @return Node|false
+ */
+ find_selected_anchors : function(node) {
+ var tagname = node.get('tagName'), hit, hits;
+ // Direct hit.
+ if (tagname && tagname.toLowerCase() === 'a') {
+ return [node];
+ }
+ // Search down but check that each node is part of the selection.
+ hits = [];
+ node.all('a').each(function(n) {
+ if (!hit && M.editor_atto.selection_contains_node(n)) {
+ hits.push(n);
+ }
+ });
+ if (hits.length > 0) {
+ return hits;
+ }
+ // Search up.
+ hit = node.ancestor('a');
+ if (hit) {
+ return [hit];
+ }
+ return [];
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_form_content
+ * @param string elementid
+ * @return string
+ */
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_link'));
+
+ content.one('#atto_link_urlentrysubmit').on('click', M.atto_link.set_link, this, elementid);
+ content.one('#openlinkbrowser').on('click', M.atto_link.open_filepicker);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/src/button/build.json b/lib/editor/atto/plugins/link/yui/src/button/build.json
new file mode 100644
index 00000000000..0f84aad113e
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_link-button",
+ "builds": {
+ "moodle-atto_link-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/link/yui/src/button/js/button.js b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
new file mode 100644
index 00000000000..7e5023533c5
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
@@ -0,0 +1,263 @@
+// 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 .
+
+/**
+ * Atto text editor link plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_link = M.atto_link || {
+ /**
+ * The window used to get the link details.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * The selection object returned by the browser.
+ *
+ * @property selection
+ * @type Range
+ * @default null
+ */
+ selection : null,
+
+ /**
+ * Display the chooser dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_chooser : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_link.selection = M.editor_atto.get_selection();
+ if (M.atto_link.selection !== false && (!M.atto_link.selection.collapsed)) {
+ var dialogue;
+ if (!M.atto_link.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_link.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_link.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createlink', 'atto_link'));
+
+ M.atto_link.resolve_anchors();
+
+ dialogue.show();
+ M.atto_link.dialogue = dialogue;
+ }
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ },
+
+ /**
+ * If there is selected text and it is part of an anchor link,
+ * extract the url (and target) from the link (and set them in the form).
+ *
+ * @method resolve_anchors
+ */
+ resolve_anchors : function() {
+ // Find the first anchor tag in the selection.
+ var selectednode = M.editor_atto.get_selection_parent_node(),
+ anchornodes,
+ anchornode,
+ url;
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+
+ if (anchornodes.length > 0) {
+ anchornode = anchornodes[0];
+ M.atto_link.selection = M.editor_atto.get_selection_from_node(anchornode);
+ url = anchornode.getAttribute('href');
+ target = anchornode.getAttribute('target');
+ if (url !== '') {
+ Y.one('#atto_link_urlentry').set('value', url);
+ }
+ if (target === '_blank') {
+ Y.one('#atto_link_openinnewwindow').set('checked', 'checked');
+ } else {
+ Y.one('#atto_link_openinnewwindow').set('checked', '');
+ }
+ }
+ },
+
+ /**
+ * Open the repository file picker.
+ *
+ * @method open_filepicker
+ * @param Event e
+ */
+ open_filepicker : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'link', M.atto_link.filepicker_callback);
+ },
+
+ /**
+ * Called by the file picker when a link has been chosen.
+ *
+ * @method filepicker_callback
+ * @param {Object} params - contains selected url.
+ */
+ filepicker_callback : function(params) {
+ M.atto_link.dialogue.hide();
+ if (params.url !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, params.url);
+ }
+ },
+
+ /**
+ * The OK button has been pressed - make the changes to the source.
+ *
+ * @method set_link
+ * @param Event e
+ */
+ set_link : function(e, elementid) {
+ var input,
+ target,
+ selectednode,
+ anchornodes,
+ value;
+
+ e.preventDefault();
+ M.atto_link.dialogue.hide();
+
+ input = e.currentTarget.ancestor('.atto_form').one('input[type=url]');
+
+ value = input.get('value');
+ if (value !== '') {
+ M.editor_atto.set_selection(M.atto_link.selection);
+ document.execCommand('unlink', false, null);
+ document.execCommand('createLink', false, value);
+
+ // Now set the target.
+ selectednode = M.editor_atto.get_selection_parent_node();
+
+ // Note this is a document fragment and YUI doesn't like them.
+ if (!selectednode) {
+ return;
+ }
+
+ anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+ Y.Array.each(anchornodes, function(anchornode) {
+ target = e.currentTarget.ancestor('.atto_form').one('input[type=checkbox]');
+ if (target.get('checked')) {
+ anchornode.setAttribute('target', '_blank');
+ } else {
+ anchornode.removeAttribute('target');
+ }
+ });
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+
+ /**
+ * Look up and down for the nearest anchor tags that are least partly contained in the selection.
+ *
+ * @method find_selected_anchors
+ * @param Node node
+ * @return Node|false
+ */
+ find_selected_anchors : function(node) {
+ var tagname = node.get('tagName'), hit, hits;
+ // Direct hit.
+ if (tagname && tagname.toLowerCase() === 'a') {
+ return [node];
+ }
+ // Search down but check that each node is part of the selection.
+ hits = [];
+ node.all('a').each(function(n) {
+ if (!hit && M.editor_atto.selection_contains_node(n)) {
+ hits.push(n);
+ }
+ });
+ if (hits.length > 0) {
+ return hits;
+ }
+ // Search up.
+ hit = node.ancestor('a');
+ if (hit) {
+ return [hit];
+ }
+ return [];
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_form_content
+ * @param string elementid
+ * @return string
+ */
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_link'));
+
+ content.one('#atto_link_urlentrysubmit').on('click', M.atto_link.set_link, this, elementid);
+ content.one('#openlinkbrowser').on('click', M.atto_link.open_filepicker);
+ return content;
+ }
+};
diff --git a/lib/editor/atto/plugins/link/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/link/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..41593ed2074
--- /dev/null
+++ b/lib/editor/atto/plugins/link/yui/src/button/meta/editor.json
@@ -0,0 +1,8 @@
+{
+ "moodle-atto_link-button": {
+ "requires": [
+ "node",
+ "escape"
+ ]
+ }
+}
diff --git a/lib/editor/atto/plugins/media/lang/en/atto_media.php b/lib/editor/atto/plugins/media/lang/en/atto_media.php
new file mode 100644
index 00000000000..f7b8d9ef60e
--- /dev/null
+++ b/lib/editor/atto/plugins/media/lang/en/atto_media.php
@@ -0,0 +1,32 @@
+.
+
+/**
+ * Strings for component 'atto_media', language 'en'.
+ *
+ * @package atto_media
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['accessibilityhint'] = '
Accessibility recommendation
Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.
';
+$string['browserepositories'] = 'Browse repositories...';
+$string['createmedia'] = 'Insert media';
+$string['entername'] = 'Enter name';
+$string['enterurl'] = 'Enter URL';
+$string['height'] = 'Height';
+$string['pluginname'] = 'Media';
+$string['width'] = 'Width';
diff --git a/lib/editor/atto/plugins/media/lib.php b/lib/editor/atto/plugins/media/lib.php
new file mode 100644
index 00000000000..6133e197d78
--- /dev/null
+++ b/lib/editor/atto/plugins/media/lib.php
@@ -0,0 +1,52 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_media
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_media_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/insert_edit_video', 'editor_atto');
+
+ $PAGE->requires->strings_for_js(array('createmedia',
+ 'enterurl',
+ 'entername',
+ 'browserepositories',
+ 'accessibilityhint'),
+ 'atto_media');
+ $PAGE->requires->yui_module('moodle-atto_media-button',
+ 'M.atto_media.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'file')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_media_sort_order() {
+ return 14;
+}
diff --git a/lib/editor/atto/plugins/media/version.php b/lib/editor/atto/plugins/media/version.php
new file mode 100644
index 00000000000..722cd306985
--- /dev/null
+++ b/lib/editor/atto/plugins/media/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_media
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_media'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js
new file mode 100644
index 00000000000..5d67cc1f325
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js
@@ -0,0 +1,122 @@
+YUI.add('moodle-atto_media-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 .
+
+/**
+ * Atto text editor media plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_media = M.atto_media || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_media.selection = M.editor_atto.get_selection();
+ if (M.atto_media.selection !== false) {
+ var dialogue;
+ if (!M.atto_media.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_media.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_media.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createmedia', 'atto_media'));
+ dialogue.centerDialogue();
+ dialogue.show();
+ M.atto_media.dialogue = dialogue;
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ },
+ open_browser : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'media', M.atto_media.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_media_urlentry');
+ input.set('value', params.url);
+ input = Y.one('#atto_media_nameentry');
+ input.set('value', params.file);
+ }
+ },
+ set_media : function(e, elementid) {
+ e.preventDefault();
+ M.atto_media.dialogue.hide();
+
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_media_urlentry');
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_media_nameentry');
+ var name = input.get('value');
+
+ if (url !== '' && name !== '') {
+ M.editor_atto.set_selection(M.atto_media.selection);
+ var mediahtml = '' + name + '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(mediahtml);
+ } else {
+ document.execCommand('insertHTML', false, mediahtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_media'));
+
+ content.one('#atto_media_urlentrysubmit').on('click', M.atto_media.set_media, this, elementid);
+ content.one('#openmediabrowser').on('click', M.atto_media.open_browser);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js
new file mode 100644
index 00000000000..f00a5b835f8
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_media-button",function(e,t){M.atto_media=M.atto_media||{dialogue:null,selection:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_media.selection=M.editor_atto.get_selection();if(M.atto_media.selection!==!1){var n;M.atto_media.dialogue?n=M.atto_media.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_media.get_form_content(t)),n.set("headerContent",M.util.get_string("createmedia","atto_media")),n.centerDialogue(),n.show(),M.atto_media.dialogue=n}};M.editor_atto.add_toolbar_button(e.elementid,"media",e.icon,e.group,t,this)},open_browser:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"media",M.atto_media.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_media_urlentry");n.set("value",t.url),n=e.one("#atto_media_nameentry"),n.set("value",t.file)}},set_media:function(t,n){t.preventDefault(),M.atto_media.dialogue.hide();var r=t.currentTarget.ancestor(".atto_form").one("#atto_media_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_media_nameentry");var s=r.get("value");if(i!==""&&s!==""){M.editor_atto.set_selection(M.atto_media.selection);var o=''+s+"";document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(o):document.execCommand("insertHTML",!1,o),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_media"));return n.one("#atto_media_urlentrysubmit").on("click",M.atto_media.set_media,this,t),n.one("#openmediabrowser").on("click",M.atto_media.open_browser),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js
new file mode 100644
index 00000000000..5d67cc1f325
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js
@@ -0,0 +1,122 @@
+YUI.add('moodle-atto_media-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 .
+
+/**
+ * Atto text editor media plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_media = M.atto_media || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_media.selection = M.editor_atto.get_selection();
+ if (M.atto_media.selection !== false) {
+ var dialogue;
+ if (!M.atto_media.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_media.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_media.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createmedia', 'atto_media'));
+ dialogue.centerDialogue();
+ dialogue.show();
+ M.atto_media.dialogue = dialogue;
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ },
+ open_browser : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'media', M.atto_media.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_media_urlentry');
+ input.set('value', params.url);
+ input = Y.one('#atto_media_nameentry');
+ input.set('value', params.file);
+ }
+ },
+ set_media : function(e, elementid) {
+ e.preventDefault();
+ M.atto_media.dialogue.hide();
+
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_media_urlentry');
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_media_nameentry');
+ var name = input.get('value');
+
+ if (url !== '' && name !== '') {
+ M.editor_atto.set_selection(M.atto_media.selection);
+ var mediahtml = '' + name + '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(mediahtml);
+ } else {
+ document.execCommand('insertHTML', false, mediahtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_media'));
+
+ content.one('#atto_media_urlentrysubmit').on('click', M.atto_media.set_media, this, elementid);
+ content.one('#openmediabrowser').on('click', M.atto_media.open_browser);
+ return content;
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape"]});
diff --git a/lib/editor/atto/plugins/media/yui/src/button/build.json b/lib/editor/atto/plugins/media/yui/src/button/build.json
new file mode 100644
index 00000000000..b20a6f5c438
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_media-button",
+ "builds": {
+ "moodle-atto_media-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/media/yui/src/button/js/button.js b/lib/editor/atto/plugins/media/yui/src/button/js/button.js
new file mode 100644
index 00000000000..29033965ba8
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/src/button/js/button.js
@@ -0,0 +1,117 @@
+// 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 .
+
+/**
+ * Atto text editor media plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_media = M.atto_media || {
+ dialogue : null,
+ selection : null,
+ init : function(params) {
+ var display_chooser = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_media.selection = M.editor_atto.get_selection();
+ if (M.atto_media.selection !== false) {
+ var dialogue;
+ if (!M.atto_media.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_media.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_media.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createmedia', 'atto_media'));
+ dialogue.centerDialogue();
+ dialogue.show();
+ M.atto_media.dialogue = dialogue;
+ }
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ },
+ open_browser : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ e.preventDefault();
+
+ M.editor_atto.show_filepicker(elementid, 'media', M.atto_media.filepicker_callback);
+ },
+ filepicker_callback : function(params) {
+ if (params.url !== '') {
+ var input = Y.one('#atto_media_urlentry');
+ input.set('value', params.url);
+ input = Y.one('#atto_media_nameentry');
+ input.set('value', params.file);
+ }
+ },
+ set_media : function(e, elementid) {
+ e.preventDefault();
+ M.atto_media.dialogue.hide();
+
+ var input = e.currentTarget.ancestor('.atto_form').one('#atto_media_urlentry');
+ var url = input.get('value');
+ input = e.currentTarget.ancestor('.atto_form').one('#atto_media_nameentry');
+ var name = input.get('value');
+
+ if (url !== '' && name !== '') {
+ M.editor_atto.set_selection(M.atto_media.selection);
+ var mediahtml = '' + name + '';
+
+ if (document.selection && document.selection.createRange().pasteHTML) {
+ document.selection.createRange().pasteHTML(mediahtml);
+ } else {
+ document.execCommand('insertHTML', false, mediahtml);
+ }
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+ },
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_media'));
+
+ content.one('#atto_media_urlentrysubmit').on('click', M.atto_media.set_media, this, elementid);
+ content.one('#openmediabrowser').on('click', M.atto_media.open_browser);
+ return content;
+ }
+};
diff --git a/lib/editor/atto/plugins/media/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/media/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..3632bf78635
--- /dev/null
+++ b/lib/editor/atto/plugins/media/yui/src/button/meta/editor.json
@@ -0,0 +1,8 @@
+{
+ "moodle-atto_media-button": {
+ "requires": [
+ "node",
+ "escape"
+ ]
+ }
+}
diff --git a/lib/editor/atto/plugins/orderedlist/lang/en/atto_orderedlist.php b/lib/editor/atto/plugins/orderedlist/lang/en/atto_orderedlist.php
new file mode 100644
index 00000000000..d4d8cc2395a
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/lang/en/atto_orderedlist.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_orderedlist', language 'en'.
+ *
+ * @package atto_orderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Ordered list';
diff --git a/lib/editor/atto/plugins/orderedlist/lib.php b/lib/editor/atto/plugins/orderedlist/lib.php
new file mode 100644
index 00000000000..f9e45bcd3fc
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/lib.php
@@ -0,0 +1,48 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_orderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_orderedlist_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/numbered_list', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_orderedlist-button',
+ 'M.atto_orderedlist.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return array groupname followed by the absolute position within the toolbar
+ */
+function atto_orderedlist_sort_order() {
+ return 6;
+}
diff --git a/lib/editor/atto/plugins/orderedlist/version.php b/lib/editor/atto/plugins/orderedlist/version.php
new file mode 100644
index 00000000000..bcd6bf33195
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_orderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_orderedlist'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
new file mode 100644
index 00000000000..4b19e1e757f
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_orderedlist-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 .
+
+/**
+ * Atto text editor orderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_orderedlist = M.atto_orderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertOrderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
new file mode 100644
index 00000000000..c1863bd3a6e
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_orderedlist-button",function(e,t){M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
new file mode 100644
index 00000000000..4b19e1e757f
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_orderedlist-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 .
+
+/**
+ * Atto text editor orderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_orderedlist = M.atto_orderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertOrderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/build.json b/lib/editor/atto/plugins/orderedlist/yui/src/button/build.json
new file mode 100644
index 00000000000..2326ff6dcb1
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_orderedlist-button",
+ "builds": {
+ "moodle-atto_orderedlist-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
new file mode 100644
index 00000000000..eb3f83423a0
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor orderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_orderedlist = M.atto_orderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertOrderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/orderedlist/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..08ed48a0e44
--- /dev/null
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_orderedlist-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php b/lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php
new file mode 100644
index 00000000000..6fb8976ac1b
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_outdent', language 'en'.
+ *
+ * @package atto_outdent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Outdent';
diff --git a/lib/editor/atto/plugins/outdent/lib.php b/lib/editor/atto/plugins/outdent/lib.php
new file mode 100644
index 00000000000..66d832ac902
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/lib.php
@@ -0,0 +1,46 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_outdent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_outdent_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/decrease_indent', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_outdent-button',
+ 'M.atto_outdent.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_outdent_sort_order() {
+ return 4;
+}
diff --git a/lib/editor/atto/plugins/outdent/version.php b/lib/editor/atto/plugins/outdent/version.php
new file mode 100644
index 00000000000..40db5bdd047
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_outdent
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_outdent'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
new file mode 100644
index 00000000000..4224011908d
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_outdent-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 .
+
+/**
+ * Atto text editor outdent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_outdent = M.atto_outdent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
new file mode 100644
index 00000000000..f86a8cda522
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_outdent-button",function(e,t){M.atto_outdent=M.atto_outdent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("outdent",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"outdent",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
new file mode 100644
index 00000000000..4224011908d
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_outdent-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 .
+
+/**
+ * Atto text editor outdent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_outdent = M.atto_outdent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/build.json b/lib/editor/atto/plugins/outdent/yui/src/button/build.json
new file mode 100644
index 00000000000..177e1c7f1d4
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_outdent-button",
+ "builds": {
+ "moodle-atto_outdent-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js b/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
new file mode 100644
index 00000000000..d3d67afddf2
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor outdent plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_outdent = M.atto_outdent || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/outdent/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..5c46b6c399e
--- /dev/null
+++ b/lib/editor/atto/plugins/outdent/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_outdent-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/strike/lang/en/atto_strike.php b/lib/editor/atto/plugins/strike/lang/en/atto_strike.php
new file mode 100644
index 00000000000..1e4ca1a207a
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/lang/en/atto_strike.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_strike', language 'en'.
+ *
+ * @package atto_strike
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Strike through';
diff --git a/lib/editor/atto/plugins/strike/lib.php b/lib/editor/atto/plugins/strike/lib.php
new file mode 100644
index 00000000000..7e68784a00c
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/lib.php
@@ -0,0 +1,48 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_strike
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_strike_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/strikethrough', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_strike-button',
+ 'M.atto_strike.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int absolute position within the toolbar
+ */
+function atto_strike_sort_order() {
+ return 3;
+}
diff --git a/lib/editor/atto/plugins/strike/version.php b/lib/editor/atto/plugins/strike/version.php
new file mode 100644
index 00000000000..25442063cb2
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_strike
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_strike'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
new file mode 100644
index 00000000000..d2a09428ff4
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_strike-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 .
+
+/**
+ * Atto text editor strike plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_strike = M.atto_strike || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('strikeThrough', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
new file mode 100644
index 00000000000..067c1db0312
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_strike-button",function(e,t){M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"strike",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
new file mode 100644
index 00000000000..d2a09428ff4
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_strike-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 .
+
+/**
+ * Atto text editor strike plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_strike = M.atto_strike || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('strikeThrough', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/build.json b/lib/editor/atto/plugins/strike/yui/src/button/build.json
new file mode 100644
index 00000000000..b24dfb52af7
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_strike-button",
+ "builds": {
+ "moodle-atto_strike-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
new file mode 100644
index 00000000000..8e8505e2c57
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor strike plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_strike = M.atto_strike || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('strikeThrough', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/strike/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..71772d22ca9
--- /dev/null
+++ b/lib/editor/atto/plugins/strike/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_strike-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/table/lang/en/atto_table.php b/lib/editor/atto/plugins/table/lang/en/atto_table.php
new file mode 100644
index 00000000000..388e14e3690
--- /dev/null
+++ b/lib/editor/atto/plugins/table/lang/en/atto_table.php
@@ -0,0 +1,43 @@
+.
+
+/**
+ * Strings for component 'atto_table', language 'en'.
+ *
+ * @package atto_table
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['accessibilityhint'] = '
Accessible tables recommendation
An accessible HTML table should have :
a caption describing the content of the table
row and/or column headers
no empty table headers
no merged table cells
.
';
+$string['createtable'] = 'Create table';
+$string['pluginname'] = 'Table';
+$string['numberofcolumns'] = 'Number of columns';
+$string['numberofrows'] = 'Number of rows';
+$string['caption'] = 'Caption';
+$string['headers'] = 'Define headers on';
+$string['columns'] = 'Columns';
+$string['rows'] = 'Rows';
+$string['both'] = 'Both';
+$string['edittable'] = 'Edit table';
+$string['addrowafter'] = 'Add row after';
+$string['addcolumnafter'] = 'Add column after';
+$string['moverowup'] = 'Move row up';
+$string['moverowdown'] = 'Move row down';
+$string['movecolumnleft'] = 'Move column left';
+$string['movecolumnright'] = 'Move column right';
+$string['deleterow'] = 'Delete row';
+$string['deletecolumn'] = 'Delete column';
diff --git a/lib/editor/atto/plugins/table/lib.php b/lib/editor/atto/plugins/table/lib.php
new file mode 100644
index 00000000000..b3a64895d08
--- /dev/null
+++ b/lib/editor/atto/plugins/table/lib.php
@@ -0,0 +1,68 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_table
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_table_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/table', 'editor_atto');
+
+ $PAGE->requires->strings_for_js(array('createtable',
+ 'accessibilityhint',
+ 'headers',
+ 'caption',
+ 'columns',
+ 'rows',
+ 'numberofcolumns',
+ 'numberofrows',
+ 'both',
+ 'edittable',
+ 'addcolumnafter',
+ 'addrowafter',
+ 'movecolumnright',
+ 'movecolumnleft',
+ 'moverowdown',
+ 'moverowup',
+ 'deleterow',
+ 'deletecolumn'),
+ 'atto_table');
+
+ $PAGE->requires->yui_module('moodle-atto_table-button',
+ 'M.atto_table.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_table_sort_order() {
+ return 11;
+}
diff --git a/lib/editor/atto/plugins/table/styles.css b/lib/editor/atto/plugins/table/styles.css
new file mode 100644
index 00000000000..763ee9c746f
--- /dev/null
+++ b/lib/editor/atto/plugins/table/styles.css
@@ -0,0 +1,7 @@
+div.editor_atto_content td,
+div.editor_atto_content th {
+ border: 1px dashed #BBB;
+ position: relative;
+ min-width: 30px;
+ height: 13px;
+}
diff --git a/lib/editor/atto/plugins/table/version.php b/lib/editor/atto/plugins/table/version.php
new file mode 100644
index 00000000000..222d7a6ac76
--- /dev/null
+++ b/lib/editor/atto/plugins/table/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_table
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_table'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
new file mode 100644
index 00000000000..d627f07167e
--- /dev/null
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
@@ -0,0 +1,624 @@
+YUI.add('moodle-atto_table-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 .
+
+/**
+ * Atto text editor table plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_table = M.atto_table || {
+
+ /**
+ * The window used to get the table details.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * The selection object returned by the browser.
+ *
+ * @property selection
+ * @type Range
+ * @default null
+ */
+ selection : null,
+
+ /**
+ * Yui image for table editing controls.
+ *
+ * @property menunode
+ * @type Y.Node
+ * @default null
+ */
+ menunode : null,
+
+ /**
+ * Popup menu for table controls
+ *
+ * @property controlmenu
+ * @type M.editor_atto.controlmenu
+ * @default null
+ */
+ controlmenu : null,
+
+ /**
+ * Last clicked cell that opened the context menu.
+ *
+ * @property lasttarget
+ * @type Y.Node
+ * @default null
+ */
+ lasttarget : null,
+
+ /**
+ * Display the chooser dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_chooser : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_table.selection = M.editor_atto.get_selection();
+ if (M.atto_table.selection !== false && (!M.atto_table.selection.collapsed)) {
+ var dialogue;
+ if (!M.atto_table.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_table.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_table.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createtable', 'atto_table'));
+
+ dialogue.show();
+ M.atto_table.dialogue = dialogue;
+ }
+
+ },
+
+ /**
+ * Show the context menu
+ *
+ * @method show_menu
+ * @param Event e
+ * @param string elementid
+ */
+ show_menu : function(e, elementid) {
+ var addhandlers = false;
+
+ e.preventDefault();
+
+ if (this.controlmenu === null) {
+ addhandlers = true;
+ // Add event handlers for table control menus.
+ var bodycontent = '
",document.execCommand("insertHTML",!1,u),M.editor_atto.text_updated(n)},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_table"));return n.one("#atto_table_submit").on("click",M.atto_table.set_table,this,t),n}}},"@VERSION@",{requires:["node","escape","event","event-valuechange"]});
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
new file mode 100644
index 00000000000..d627f07167e
--- /dev/null
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
@@ -0,0 +1,624 @@
+YUI.add('moodle-atto_table-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 .
+
+/**
+ * Atto text editor table plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_table = M.atto_table || {
+
+ /**
+ * The window used to get the table details.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * The selection object returned by the browser.
+ *
+ * @property selection
+ * @type Range
+ * @default null
+ */
+ selection : null,
+
+ /**
+ * Yui image for table editing controls.
+ *
+ * @property menunode
+ * @type Y.Node
+ * @default null
+ */
+ menunode : null,
+
+ /**
+ * Popup menu for table controls
+ *
+ * @property controlmenu
+ * @type M.editor_atto.controlmenu
+ * @default null
+ */
+ controlmenu : null,
+
+ /**
+ * Last clicked cell that opened the context menu.
+ *
+ * @property lasttarget
+ * @type Y.Node
+ * @default null
+ */
+ lasttarget : null,
+
+ /**
+ * Display the chooser dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_chooser : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ M.atto_table.selection = M.editor_atto.get_selection();
+ if (M.atto_table.selection !== false && (!M.atto_table.selection.collapsed)) {
+ var dialogue;
+ if (!M.atto_table.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true
+ });
+ } else {
+ dialogue = M.atto_table.dialogue;
+ }
+
+ dialogue.render();
+ dialogue.set('bodyContent', M.atto_table.get_form_content(elementid));
+ dialogue.set('headerContent', M.util.get_string('createtable', 'atto_table'));
+
+ dialogue.show();
+ M.atto_table.dialogue = dialogue;
+ }
+
+ },
+
+ /**
+ * Show the context menu
+ *
+ * @method show_menu
+ * @param Event e
+ * @param string elementid
+ */
+ show_menu : function(e, elementid) {
+ var addhandlers = false;
+
+ e.preventDefault();
+
+ if (this.controlmenu === null) {
+ addhandlers = true;
+ // Add event handlers for table control menus.
+ var bodycontent = '
';
+
+ document.execCommand('insertHTML', false, tablehtml);
+
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_form_content
+ * @param string elementid
+ * @return string
+ */
+ get_form_content : function(elementid) {
+ var content = Y.Node.create('' +
+ '' + M.util.get_string('accessibilityhint', 'atto_table'));
+
+ content.one('#atto_table_submit').on('click', M.atto_table.set_table, this, elementid);
+ return content;
+ }
+};
diff --git a/lib/editor/atto/plugins/table/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/table/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..b36d2785778
--- /dev/null
+++ b/lib/editor/atto/plugins/table/yui/src/button/meta/editor.json
@@ -0,0 +1,10 @@
+{
+ "moodle-atto_table-button": {
+ "requires": [
+ "node",
+ "escape",
+ "event",
+ "event-valuechange"
+ ]
+ }
+}
diff --git a/lib/editor/atto/plugins/title/lang/en/atto_title.php b/lib/editor/atto/plugins/title/lang/en/atto_title.php
new file mode 100644
index 00000000000..4435b2bb021
--- /dev/null
+++ b/lib/editor/atto/plugins/title/lang/en/atto_title.php
@@ -0,0 +1,32 @@
+.
+
+/**
+ * Strings for component 'atto_title', language 'en'.
+ *
+ * @package atto_title
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['blockquote'] = 'Quoted';
+$string['h3'] = 'Heading 1';
+$string['h4'] = 'Heading 2';
+$string['h5'] = 'Heading 3';
+$string['p'] = 'Plain';
+$string['pluginname'] = 'Paragraph styles';
+$string['pre'] = 'Pre-formatted';
+$string['title'] = 'Paragraph styles';
diff --git a/lib/editor/atto/plugins/title/lib.php b/lib/editor/atto/plugins/title/lib.php
new file mode 100644
index 00000000000..2a5369c3c0d
--- /dev/null
+++ b/lib/editor/atto/plugins/title/lib.php
@@ -0,0 +1,54 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_title
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_title_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/styleprops', 'editor_atto');
+
+ $PAGE->requires->strings_for_js(array('h3',
+ 'h4',
+ 'h5',
+ 'blockquote',
+ 'pre',
+ 'p'), 'atto_title');
+ $PAGE->requires->yui_module('moodle-atto_title-button',
+ 'M.atto_title.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'title')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_title_sort_order() {
+ return 9;
+}
diff --git a/lib/editor/atto/plugins/title/version.php b/lib/editor/atto/plugins/title/version.php
new file mode 100644
index 00000000000..2cad843b64e
--- /dev/null
+++ b/lib/editor/atto/plugins/title/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_title
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_title'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
new file mode 100644
index 00000000000..a66644fe626
--- /dev/null
+++ b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
@@ -0,0 +1,85 @@
+YUI.add('moodle-atto_title-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 .
+
+/**
+ * Atto text editor title plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_title = M.atto_title || {
+ init : function(params) {
+ var click_h3 = function(e, elementid) {
+ M.atto_title.change_title(e, elementid, '
")},u=M.util.get_string("h3","atto_title"),a=M.util.get_string("h4","atto_title"),f=M.util.get_string("h5","atto_title"),l=M.util.get_string("pre","atto_title"),c=M.util.get_string("blockquote","atto_title"),h=M.util.get_string("p","atto_title");M.editor_atto.add_toolbar_menu(e.elementid,"title",e.icon,e.group,[{text:u,handler:t},{text:a,handler:n},{text:f,handler:r},{text:l,handler:i},{text:c,handler:s},{text:h,handler:o}])},change_title:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("formatBlock",!1,n),M.editor_atto.text_updated(t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
new file mode 100644
index 00000000000..a66644fe626
--- /dev/null
+++ b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
@@ -0,0 +1,85 @@
+YUI.add('moodle-atto_title-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 .
+
+/**
+ * Atto text editor title plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_title = M.atto_title || {
+ init : function(params) {
+ var click_h3 = function(e, elementid) {
+ M.atto_title.change_title(e, elementid, '
');
+ };
+
+ var h3 = M.util.get_string('h3', 'atto_title');
+ var h4 = M.util.get_string('h4', 'atto_title');
+ var h5 = M.util.get_string('h5', 'atto_title');
+ var pre = M.util.get_string('pre', 'atto_title');
+ var blockquote = M.util.get_string('blockquote', 'atto_title');
+ var p = M.util.get_string('p', 'atto_title');
+
+ M.editor_atto.add_toolbar_menu(params.elementid,
+ 'title',
+ params.icon,
+ params.group,
+ [
+ {'text' : h3, 'handler' : click_h3},
+ {'text' : h4, 'handler' : click_h4},
+ {'text' : h5, 'handler' : click_h5},
+ {'text' : pre, 'handler' : click_pre},
+ {'text' : blockquote, 'handler' : click_blockquote},
+ {'text' : p, 'handler' : click_p}
+ ]);
+ },
+
+ /**
+ * Handle a choice from the menu (insert the node in the text editor matching elementid).
+ * @param event e - The event that triggered this.
+ * @param string elementid - The id of the editor
+ * @param string node - The html to insert
+ */
+ change_title : function(e, elementid, node) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('formatBlock', false, node);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/title/yui/src/button/build.json b/lib/editor/atto/plugins/title/yui/src/button/build.json
new file mode 100644
index 00000000000..8ee3ce0e4b8
--- /dev/null
+++ b/lib/editor/atto/plugins/title/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_title-button",
+ "builds": {
+ "moodle-atto_title-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/title/yui/src/button/js/button.js b/lib/editor/atto/plugins/title/yui/src/button/js/button.js
new file mode 100644
index 00000000000..1f5de116cb2
--- /dev/null
+++ b/lib/editor/atto/plugins/title/yui/src/button/js/button.js
@@ -0,0 +1,80 @@
+// 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 .
+
+/**
+ * Atto text editor title plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_title = M.atto_title || {
+ init : function(params) {
+ var click_h3 = function(e, elementid) {
+ M.atto_title.change_title(e, elementid, '
');
+ };
+
+ var h3 = M.util.get_string('h3', 'atto_title');
+ var h4 = M.util.get_string('h4', 'atto_title');
+ var h5 = M.util.get_string('h5', 'atto_title');
+ var pre = M.util.get_string('pre', 'atto_title');
+ var blockquote = M.util.get_string('blockquote', 'atto_title');
+ var p = M.util.get_string('p', 'atto_title');
+
+ M.editor_atto.add_toolbar_menu(params.elementid,
+ 'title',
+ params.icon,
+ params.group,
+ [
+ {'text' : h3, 'handler' : click_h3},
+ {'text' : h4, 'handler' : click_h4},
+ {'text' : h5, 'handler' : click_h5},
+ {'text' : pre, 'handler' : click_pre},
+ {'text' : blockquote, 'handler' : click_blockquote},
+ {'text' : p, 'handler' : click_p}
+ ]);
+ },
+
+ /**
+ * Handle a choice from the menu (insert the node in the text editor matching elementid).
+ * @param event e - The event that triggered this.
+ * @param string elementid - The id of the editor
+ * @param string node - The html to insert
+ */
+ change_title : function(e, elementid, node) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('formatBlock', false, node);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ }
+};
diff --git a/lib/editor/atto/plugins/title/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/title/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..c073041342e
--- /dev/null
+++ b/lib/editor/atto/plugins/title/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_title-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/underline/lang/en/atto_underline.php b/lib/editor/atto/plugins/underline/lang/en/atto_underline.php
new file mode 100644
index 00000000000..74c9e3f4703
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/lang/en/atto_underline.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_underline', language 'en'.
+ *
+ * @package atto_underline
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Underline';
diff --git a/lib/editor/atto/plugins/underline/lib.php b/lib/editor/atto/plugins/underline/lib.php
new file mode 100644
index 00000000000..cc66d3a74b3
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/lib.php
@@ -0,0 +1,48 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_underline
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_underline_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/underline', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_underline-button',
+ 'M.atto_underline.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_underline_sort_order() {
+ return 2;
+}
diff --git a/lib/editor/atto/plugins/underline/version.php b/lib/editor/atto/plugins/underline/version.php
new file mode 100644
index 00000000000..efc7eda869a
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_underline
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_underline'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
new file mode 100644
index 00000000000..1566a0f2813
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_underline-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 .
+
+/**
+ * Atto text editor underline plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_underline = M.atto_underline || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('underline', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
new file mode 100644
index 00000000000..5880711bb21
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_underline-button",function(e,t){M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"underline",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
new file mode 100644
index 00000000000..1566a0f2813
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_underline-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 .
+
+/**
+ * Atto text editor underline plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_underline = M.atto_underline || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('underline', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/build.json b/lib/editor/atto/plugins/underline/yui/src/button/build.json
new file mode 100644
index 00000000000..761727315e1
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_underline-button",
+ "builds": {
+ "moodle-atto_underline-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
new file mode 100644
index 00000000000..1ea68b08940
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor underline plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_underline = M.atto_underline || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('underline', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/underline/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..e96b89bcddb
--- /dev/null
+++ b/lib/editor/atto/plugins/underline/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_underline-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/unlink/lang/en/atto_unlink.php b/lib/editor/atto/plugins/unlink/lang/en/atto_unlink.php
new file mode 100644
index 00000000000..bbf03b7a181
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/lang/en/atto_unlink.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_unlink', language 'en'.
+ *
+ * @package atto_unlink
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Unlink';
diff --git a/lib/editor/atto/plugins/unlink/lib.php b/lib/editor/atto/plugins/unlink/lib.php
new file mode 100644
index 00000000000..8c9f059f3ba
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/lib.php
@@ -0,0 +1,46 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_unlink
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_unlink_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/remove_link', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_unlink-button',
+ 'M.atto_unlink.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'link')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_unlink_sort_order() {
+ return 12;
+}
diff --git a/lib/editor/atto/plugins/unlink/version.php b/lib/editor/atto/plugins/unlink/version.php
new file mode 100644
index 00000000000..5e5b14395f2
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_unlink
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_unlink'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
new file mode 100644
index 00000000000..b3d87f52cf5
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_unlink-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 .
+
+/**
+ * Atto text editor unlink plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unlink = M.atto_unlink || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('unlink', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
new file mode 100644
index 00000000000..ebfb57b9cfd
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_unlink-button",function(e,t){M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"unlink",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
new file mode 100644
index 00000000000..b3d87f52cf5
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_unlink-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 .
+
+/**
+ * Atto text editor unlink plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unlink = M.atto_unlink || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('unlink', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/build.json b/lib/editor/atto/plugins/unlink/yui/src/button/build.json
new file mode 100644
index 00000000000..57ceaa51efb
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_unlink-button",
+ "builds": {
+ "moodle-atto_unlink-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
new file mode 100644
index 00000000000..e8cc4fd819a
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor unlink plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unlink = M.atto_unlink || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('unlink', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/unlink/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..448646904ac
--- /dev/null
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_unlink-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/plugins/unorderedlist/lang/en/atto_unorderedlist.php b/lib/editor/atto/plugins/unorderedlist/lang/en/atto_unorderedlist.php
new file mode 100644
index 00000000000..d8cac218f2e
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/lang/en/atto_unorderedlist.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * Strings for component 'atto_unorderedlist', language 'en'.
+ *
+ * @package atto_unorderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Unordered list';
diff --git a/lib/editor/atto/plugins/unorderedlist/lib.php b/lib/editor/atto/plugins/unorderedlist/lib.php
new file mode 100644
index 00000000000..64f07e65e7b
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/lib.php
@@ -0,0 +1,46 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_unorderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_unorderedlist_init_editor($elementid) {
+ global $PAGE, $OUTPUT;
+
+ $icon = array('e/bullet_list', 'editor_atto');
+
+ $PAGE->requires->yui_module('moodle-atto_unorderedlist-button',
+ 'M.atto_unorderedlist.init',
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
+
+}
+
+/**
+ * Return the order this plugin should be displayed in the toolbar
+ * @return int the absolute position within the toolbar
+ */
+function atto_unorderedlist_sort_order() {
+ return 7;
+}
diff --git a/lib/editor/atto/plugins/unorderedlist/version.php b/lib/editor/atto/plugins/unorderedlist/version.php
new file mode 100644
index 00000000000..bbc36d26dbf
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_unorderedlist
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_unorderedlist'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
new file mode 100644
index 00000000000..6b9309a051a
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_unorderedlist-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 .
+
+/**
+ * Atto text editor unorderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unorderedlist = M.atto_unorderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertUnorderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
new file mode 100644
index 00000000000..1498365a7c8
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_unorderedlist-button",function(e,t){M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
new file mode 100644
index 00000000000..6b9309a051a
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
@@ -0,0 +1,42 @@
+YUI.add('moodle-atto_unorderedlist-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 .
+
+/**
+ * Atto text editor unorderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unorderedlist = M.atto_unorderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertUnorderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/build.json b/lib/editor/atto/plugins/unorderedlist/yui/src/button/build.json
new file mode 100644
index 00000000000..eced221fea8
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_unorderedlist-button",
+ "builds": {
+ "moodle-atto_unorderedlist-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
new file mode 100644
index 00000000000..9a3895d1247
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
@@ -0,0 +1,37 @@
+// 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 .
+
+/**
+ * Atto text editor unorderedlist plugin.
+ *
+ * @package editor-atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_unorderedlist = M.atto_unorderedlist || {
+ init : function(params) {
+ var click = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('insertUnorderedList', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
+ }
+};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/meta/editor.json b/lib/editor/atto/plugins/unorderedlist/yui/src/button/meta/editor.json
new file mode 100644
index 00000000000..58307eb3208
--- /dev/null
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/meta/editor.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_unorderedlist-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/styles.css b/lib/editor/atto/styles.css
new file mode 100644
index 00000000000..e71092dff9a
--- /dev/null
+++ b/lib/editor/atto/styles.css
@@ -0,0 +1,180 @@
+.editor_atto_content_wrap {
+ background-color: white;
+}
+.editor_atto_content {
+ padding: 4px;
+}
+
+.editor_atto_content_wrap,
+.editor_atto + textarea {
+ width: 100%;
+ padding: 0;
+ border: 1px solid #BBB;
+ border-top: none;
+}
+
+.editor_atto + textarea {
+ border-radius: 0;
+ resize: vertical;
+ margin-top: -1px;
+}
+
+div.editor_atto_toolbar {
+ display: block;
+ background: #F2F2F2;
+ min-height: 35px;
+ border: 1px solid #BBB;
+ width: 100%;
+ padding: 0 0 9px 0;
+}
+
+div.editor_atto_toolbar button {
+ padding: 3px 9px;
+ background: none;
+ border: 0px;
+ margin: 0;
+ border-radius: 0;
+ cursor: pointer;
+ }
+
+div.editor_atto_toolbar button + button {
+ border-left: 1px solid #CCC;
+}
+
+div.editor_atto_toolbar button[disabled] {
+ opacity: .45;
+ background: none;
+ cursor: default;
+}
+
+.editor_atto_toolbar button:hover {
+ background-image: radial-gradient(ellipse at center, #ffffff 60%,#dfdfdf 100%);
+ background-color: #ebebeb;
+}
+
+.editor_atto_toolbar button:active {
+ background-image: radial-gradient(ellipse at center, #ffffff 40%,#dfdfdf 100%);
+ background-color: #dfdfdf;
+}
+
+div.editor_atto_toolbar button img {
+ padding: 1px;
+}
+
+
+div.editor_atto_toolbar div.atto_group {
+ display: inline-block;
+ border: 1px solid #CCC;
+ border-bottom: 1px solid #B3B3B3;
+ border-radius: 4px;
+ margin: 9px 0px 0px 9px;
+ background: #FFF;
+}
+
+.editor_atto_content img {
+ resize: both; overflow: auto;
+}
+
+.atto_menuentry img {
+ width: 16px;
+ height: 16px;
+}
+
+.atto_menu {
+ min-width: 12em;
+ background: white;
+ padding: 1px;
+}
+
+.atto_menu a {
+ color: black;
+}
+.atto_menuentry {
+ border-bottom: 1px solid #eee;
+ padding: 2px;
+ clear: left;
+}
+
+.atto_menuentry h1,
+.atto_menuentry h2,
+.atto_menuentry p {
+ margin: 4px;
+}
+
+
+.atto_form label.sameline {
+ display: inline-block;
+ min-width: 4em;
+}
+.atto_form textarea.fullwidth,
+.atto_form input.fullwidth {
+ width: 100%;
+}
+.atto_form {
+ padding-left: 30px;
+ padding-right: 30px;
+}
+.atto_form label {
+ display: block;
+ margin: 5px;
+}
+
+/* RTL Rules */
+body.dir-rtl div.editor_atto_toolbar button + button {
+ border-left: 0;
+ border-right: 1px solid #CCC;
+}
+
+body.dir-rtl div.editor_atto_toolbar img.icon {
+ padding: 0;
+}
+
+body.dir-rtl div.editor_atto_toolbar div.atto_group {
+ margin: 9px 9px 0px 0px;
+}
+
+.atto_control {
+ position: absolute;
+ right: -6px;
+ bottom: -6px;
+ display: none;
+ cursor: pointer;
+}
+.atto_control img {
+ background-color: white;
+}
+div.editor_atto_content:focus .atto_control,
+div.editor_atto_content:hover .atto_control {
+ display: block;
+}
+
+.yui3-controlmenu-hidden {
+ display: none;
+}
+.editor_atto_controlmenu li {
+ list-style-type: none;
+ padding: 0px;
+ margin; 0px;
+}
+.editor_atto_controlmenu ul {
+ padding: 0px;
+ margin: 0px;
+}
+.editor_atto_controlmenu .moodle-dialogue-hd,
+.editor_atto_controlmenu .moodle-dialogue-ft {
+ display: none;
+}
+
+.editor_atto_controlmenu ul li a:active,
+.editor_atto_controlmenu ul li a:hover {
+ background-color: #0088cc;
+ color: white;
+}
+.editor_atto_controlmenu ul li a {
+ color: #333;
+ padding: 2px;
+ padding-left: 6px;
+ padding-right: 6px;
+ border-radius: 2px;
+ display: block;
+}
diff --git a/lib/editor/atto/version.php b/lib/editor/atto/version.php
new file mode 100644
index 00000000000..c2112cb401c
--- /dev/null
+++ b/lib/editor/atto/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package editor_atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'editor_atto'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
new file mode 100644
index 00000000000..d5293faf3f5
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -0,0 +1,702 @@
+YUI.add('moodle-editor_atto-editor', 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 .
+
+/**
+ * Atto editor.
+ *
+ * @package editor_atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Classes constants.
+ */
+CSS = {
+ CONTENT: 'editor_atto_content',
+ CONTENTWRAPPER: 'editor_atto_content_wrap',
+ TOOLBAR: 'editor_atto_toolbar',
+ WRAPPER: 'editor_atto'
+};
+
+/**
+ * Atto editor main class.
+ * Common functions required by editor plugins.
+ *
+ * @package editor_atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.editor_atto = M.editor_atto || {
+
+ /**
+ * List of attached button handlers to prevent duplicates.
+ */
+ buttonhandlers : {},
+
+ /**
+ * List of attached handlers to add inline editing controls to content.
+ */
+ textupdatedhandlers : {},
+
+ /**
+ * List of YUI overlays for custom menus.
+ */
+ menus : {},
+
+ /**
+ * List of attached menu handlers to prevent duplicates.
+ */
+ menuhandlers : {},
+
+ /**
+ * List of file picker options for specific editor instances.
+ */
+ filepickeroptions : {},
+
+ /**
+ * List of buttons and menus that have been added to the toolbar.
+ */
+ widgets : {},
+
+ /**
+ * Toggle a menu.
+ * @param event e
+ */
+ showhide_menu_handler : function(e) {
+ e.preventDefault();
+ var disabled = this.getAttribute('disabled');
+ var overlayid = this.getAttribute('data-menu');
+ var overlay = M.editor_atto.menus[overlayid];
+ var menu = overlay.get('bodyContent');
+ if (overlay.get('visible') || disabled) {
+ overlay.hide();
+ menu.detach('clickoutside');
+ } else {
+ menu.on('clickoutside', function(ev) {
+ if ((ev.target.ancestor() !== this) && (ev.target !== this)) {
+ if (overlay.get('visible')) {
+ menu.detach('clickoutside');
+ overlay.hide();
+ }
+ }
+ }, this);
+ overlay.show();
+ overlay.bodyNode.one('a').focus();
+ }
+ },
+
+ /**
+ * Handle clicks on editor buttons.
+ * @param event e
+ */
+ buttonclicked_handler : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ var plugin = this.getAttribute('data-plugin');
+ var handler = this.getAttribute('data-handler');
+ var overlay = M.editor_atto.menus[plugin + '_' + elementid];
+
+ if (overlay) {
+ overlay.hide();
+ }
+
+ if (M.editor_atto.is_enabled(elementid, plugin)) {
+ // Pass it on.
+ handler = M.editor_atto.buttonhandlers[handler];
+ return handler(e, elementid);
+ }
+ },
+
+ /**
+ * Determine if the specified toolbar button/menu is enabled.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the plugin that created the button/menu.
+ */
+ is_enabled : function(elementid, plugin) {
+ var element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ return !element.hasAttribute('disabled');
+ },
+ /**
+ * Disable all buttons and menus in the toolbar.
+ * @param string elementid, the element id of this editor.
+ */
+ disable_all_widgets : function(elementid) {
+ var plugin, element;
+ for (plugin in M.editor_atto.widgets) {
+ element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.setAttribute('disabled', 'true');
+ }
+ }
+ },
+
+ /**
+ * Enable a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ */
+ enable_widget : function(elementid, plugin) {
+ var element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.removeAttribute('disabled');
+ }
+ },
+
+ /**
+ * Enable all buttons and menus in the toolbar.
+ * @param string elementid, the element id of this editor.
+ */
+ enable_all_widgets : function(elementid) {
+ var plugin, element;
+ for (plugin in M.editor_atto.widgets) {
+ element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.removeAttribute('disabled');
+ }
+ }
+ },
+
+ /**
+ * Add a content update handler to be called whenever the content is updated.
+ * This is used to add inline editing controls to the content that are cleaned on submission.
+ *
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @handler function callback - The function to do the cleaning.
+ * @param object context - the context to set for the callback.
+ * @handler function handler - A function to call when the button is clicked.
+ */
+ add_text_updated_handler : function(elementid, callback) {
+ if (!(elementid in M.editor_atto.textupdatedhandlers)) {
+ M.editor_atto.textupdatedhandlers[elementid] = [];
+ }
+ M.editor_atto.textupdatedhandlers[elementid].push(callback);
+ },
+
+ /**
+ * Add a button to the toolbar belonging to the editor for element with id "elementid".
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @param string plugin - the plugin defining the button
+ * @param string icon - the html used for the content of the button
+ * @param string groupname - the group the button should be appended to.
+ * @param array entries - List of menu entries with the string (entry.text) and the handlers (entry.handler).
+ */
+ add_toolbar_menu : function(elementid, plugin, icon, groupname, entries) {
+ var toolbar = Y.one('#' + elementid + '_toolbar'),
+ group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
+ currentfocus,
+ button,
+ imgurl,
+ expimgurl;
+
+ if (!group) {
+ group = Y.Node.create('');
+ toolbar.append(group);
+ }
+ imgurl = M.util.image_url(icon[0], icon[1]);
+ expimgurl = M.util.image_url('t/expanded', 'moodle');
+ button = Y.Node.create('');
+
+ group.append(button);
+
+ currentfocus = toolbar.getAttribute('aria-activedescendant');
+ if (!currentfocus) {
+ button.setAttribute('tabindex', '0');
+ toolbar.setAttribute('aria-activedescendant', button.generateID());
+ }
+
+ // Save the name of the plugin.
+ M.editor_atto.widgets[plugin] = plugin;
+
+ var menu = Y.Node.create('');
+ var i = 0, entry = {};
+
+ for (i = 0; i < entries.length; i++) {
+ entry = entries[i];
+
+ menu.append(Y.Node.create('
'));
+ if (!M.editor_atto.buttonhandlers[plugin + '_action_' + i]) {
+ Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_action_' + i);
+ Y.one('body').delegate('key', M.editor_atto.buttonclicked_handler, 'space,enter', '.atto_' + plugin + '_action_' + i);
+ M.editor_atto.buttonhandlers[plugin + '_action_' + i] = entry.handler;
+ }
+ }
+
+ if (!M.editor_atto.buttonhandlers[plugin]) {
+ Y.one('body').delegate('click', M.editor_atto.showhide_menu_handler, '.atto_' + plugin + '_button');
+ M.editor_atto.buttonhandlers[plugin] = true;
+ }
+
+ var overlay = new M.core.dialogue({
+ bodyContent : menu,
+ visible : false,
+ width: '14em',
+ zindex: 100,
+ lightbox: false,
+ closeButton: false,
+ centered : false,
+ align: {node: button, points: [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]}
+ });
+
+ M.editor_atto.menus[plugin + '_' + elementid] = overlay;
+ overlay.render();
+ overlay.hide();
+ overlay.headerNode.hide();
+ },
+
+ /**
+ * Add a button to the toolbar belonging to the editor for element with id "elementid".
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @param string plugin - the plugin defining the button.
+ * @param string icon - the html used for the content of the button.
+ * @param string groupname - the group the button should be appended to.
+ * @handler function handler- A function to call when the button is clicked.
+ */
+ add_toolbar_button : function(elementid, plugin, icon, groupname, handler) {
+ var toolbar = Y.one('#' + elementid + '_toolbar'),
+ group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
+ button,
+ currentfocus,
+ imgurl;
+
+ if (!group) {
+ group = Y.Node.create('');
+ toolbar.append(group);
+ }
+ imgurl = M.util.image_url(icon[0], icon[1]);
+ button = Y.Node.create('');
+
+ group.append(button);
+
+ currentfocus = toolbar.getAttribute('aria-activedescendant');
+ if (!currentfocus) {
+ button.setAttribute('tabindex', '0');
+ toolbar.setAttribute('aria-activedescendant', button.generateID());
+ }
+
+ // We only need to attach this once.
+ if (!M.editor_atto.buttonhandlers[plugin]) {
+ Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_button');
+ M.editor_atto.buttonhandlers[plugin] = handler;
+ }
+
+ // Save the name of the plugin.
+ M.editor_atto.widgets[plugin] = plugin;
+
+ },
+
+ /**
+ * Work out if the cursor is in the editable area for this editor instance.
+ * @param string elementid of this editor
+ * @return bool
+ */
+ is_active : function(elementid) {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ var node = null;
+ if (selection.parentElement) {
+ node = Y.one(selection.parentElement());
+ } else {
+ node = Y.one(selection.startContainer);
+ }
+
+ return node && node.ancestor('#' + elementid + 'editable') !== null;
+ },
+
+ /**
+ * Focus on the editable area for this editor.
+ * @param string elementid of this editor
+ */
+ focus : function(elementid) {
+ Y.one('#' + elementid + 'editable').focus();
+ },
+
+ /**
+ * Initialise the editor
+ * @param object params for this editor instance.
+ */
+ init : function(params) {
+ var textarea = Y.one('#' +params.elementid);
+ var wrapper = Y.Node.create('');
+ var atto = Y.Node.create('');
+
+ var cssfont = '';
+ var toolbar = Y.Node.create('');
+
+ // Editable content wrapper.
+ var content = Y.Node.create('');
+ content.appendChild(atto);
+
+ // Add everything to the wrapper.
+ wrapper.appendChild(toolbar);
+ wrapper.appendChild(content);
+
+ // Bleh - why are we sent a url and not the css to apply directly?
+ var css = Y.io(params.content_css, { sync: true });
+ var pos = css.responseText.indexOf('font:');
+ if (pos) {
+ cssfont = css.responseText.substring(pos + 'font:'.length, css.responseText.length - 1);
+ atto.setStyle('font', cssfont);
+ }
+ atto.setStyle('minHeight', (1.2 * (textarea.getAttribute('rows'))) + 'em');
+
+ // Copy text to editable div.
+ atto.append(textarea.get('value'));
+
+ // Add the toolbar and editable zone to the page.
+ textarea.get('parentNode').insert(wrapper, textarea);
+ atto.setStyle('color', textarea.getStyle('color'));
+ atto.setStyle('lineHeight', textarea.getStyle('lineHeight'));
+ atto.setStyle('fontSize', textarea.getStyle('fontSize'));
+ // Hide the old textarea.
+ textarea.hide();
+
+ // Copy the current value back to the textarea when focus leaves us.
+ atto.on('blur', function() {
+ this.text_updated(params.elementid);
+ }, this);
+
+ // Listen for Arrow left and Arrow right keys.
+ Y.one(Y.config.doc.body).delegate('key',
+ this.keyboard_navigation,
+ 'down:37,39',
+ '#' + params.elementid + '_toolbar',
+ this,
+ params.elementid);
+
+ // Save the file picker options for later.
+ M.editor_atto.filepickeroptions[params.elementid] = params.filepickeroptions;
+ },
+
+ /**
+ * The text in the contenteditable region has been updated,
+ * clean and copy the buffer to the text area.
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ text_updated : function(elementid) {
+ var textarea = Y.one('#' + elementid),
+ cleancontent = this.get_clean_html(elementid);
+ textarea.set('value', cleancontent);
+ // Trigger handlers for this action.
+ var i = 0;
+ if (elementid in M.editor_atto.textupdatedhandlers) {
+ for (i = 0; i < M.editor_atto.textupdatedhandlers[elementid].length; i++) {
+ var callback = M.editor_atto.textupdatedhandlers[elementid][i];
+ callback(elementid);
+ }
+ }
+ },
+
+ /**
+ * Remove all YUI ids from the generated HTML.
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @return string HTML stripped of YUI ids
+ */
+ get_clean_html : function(elementid) {
+ var atto = Y.one('#' + elementid + 'editable').cloneNode(true);
+
+ Y.each(atto.all('[id]'), function(node) {
+ var id = node.get('id');
+ if (id.indexOf('yui') === 0) {
+ node.removeAttribute('id');
+ }
+ });
+
+ Y.each(atto.all('.atto_control'), function(node) {
+ node.remove(true);
+ });
+
+ return atto.getHTML();
+ },
+
+ /**
+ * Implement arrow key navigation for the buttons in the toolbar.
+ * @param Event e - the keyboard event.
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ keyboard_navigation : function(e, elementid) {
+ var buttons,
+ current,
+ currentid,
+ currentindex;
+
+ e.preventDefault();
+
+ buttons = Y.all('#' + elementid + '_toolbar button');
+ currentid = Y.one('#' + elementid + '_toolbar').getAttribute('aria-activedescendant');
+ if (!currentid) {
+ return;
+ }
+ current = Y.one('#' + currentid);
+ current.setAttribute('tabindex', '-1');
+
+ currentindex = buttons.indexOf(current);
+
+ if (e.keyCode === 37) {
+ // Left
+ currentindex--;
+ if (currentindex < 0) {
+ currentindex = buttons.size()-1;
+ }
+ } else {
+ // Right
+ currentindex++;
+ if (currentindex >= buttons.size()) {
+ currentindex = 0;
+ }
+ }
+
+ current = buttons.item(currentindex);
+ current.setAttribute('tabindex', '0');
+ current.focus();
+ Y.one('#' + elementid + '_toolbar').setAttribute('aria-activedescendant', current.generateID());
+ },
+
+ /**
+ * Show the filepicker.
+ * @param string elementid for this editor instance.
+ * @param string type The media type for the file picker
+ * @param function callback
+ */
+ show_filepicker : function(elementid, type, callback) {
+ Y.use('core_filepicker', function (Y) {
+ var options = M.editor_atto.filepickeroptions[elementid][type];
+
+ options.formcallback = callback;
+ options.editor_target = Y.one(elementid);
+
+ M.core_filepicker.show(Y, options);
+ });
+ },
+
+ /**
+ * Create a cross browser selection object that represents a yui node.
+ * @param Node yui node for the selection
+ * @return range (browser dependent)
+ */
+ get_selection_from_node: function(node) {
+ var range;
+
+ if (window.getSelection) {
+ range = document.createRange();
+
+ range.setStartBefore(node.getDOMNode());
+ range.setEndAfter(node.getDOMNode());
+ return [range];
+ } else if (document.selection) {
+ range = document.body.createTextRange();
+ range.moveToElementText(node.getDOMNode());
+ return range;
+ }
+ return false;
+ },
+
+ /**
+ * Get the selection object that can be passed back to set_selection.
+ * @return range (browser dependent)
+ */
+ get_selection : function() {
+ if (window.getSelection) {
+ var sel = window.getSelection();
+ var ranges = [], i = 0;
+ for (i = 0; i < sel.rangeCount; i++) {
+ ranges.push(sel.getRangeAt(i));
+ }
+ return ranges;
+ } else if (document.selection) {
+ // IE < 9
+ if (document.selection.createRange) {
+ return document.selection.createRange();
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Check that a YUI node it at least partly contained by the selection.
+ * @param Range selection
+ * @param Y.Node node
+ * @return boolean
+ */
+ selection_contains_node : function(node) {
+ var range, sel;
+ if (window.getSelection) {
+ sel = window.getSelection();
+
+ if (sel.containsNode) {
+ return sel.containsNode(node.getDOMNode(), true);
+ }
+ }
+ sel = document.selection.createRange();
+ range = sel.duplicate();
+ range.moveToElementText(node.getDOMNode());
+ return sel.inRange(range);
+ },
+
+ /**
+ * Get the dom node representing the common anscestor of the selection nodes.
+ * @return DOMNode
+ */
+ get_selection_parent_node : function() {
+ var selection = M.editor_atto.get_selection();
+ if (selection.length > 0) {
+ return selection[0].commonAncestorContainer;
+ }
+ },
+
+ /**
+ * Get the list of child nodes of the selection.
+ * @return DOMNode[]
+ */
+ get_selection_text : function() {
+ var selection = M.editor_atto.get_selection();
+ if (selection.length > 0 && selection[0].cloneContents) {
+ return selection[0].cloneContents();
+ }
+ },
+
+ /**
+ * Set the current selection. Used to restore a selection.
+ */
+ set_selection : function(selection) {
+ var sel, i;
+
+ if (window.getSelection) {
+ sel = window.getSelection();
+ sel.removeAllRanges();
+ for (i = 0; i < selection.length; i++) {
+ sel.addRange(selection[i]);
+ }
+ } else if (document.selection) {
+ // IE < 9
+ if (selection.select) {
+ selection.select();
+ }
+ }
+ }
+
+};
+var CONTROLMENU_NAME = "Controlmenu",
+ CONTROLMENU;
+
+/**
+ * CONTROLMENU
+ * This is a drop down list of buttons triggered (and aligned to) a button.
+ *
+ * @namespace M.editor_atto.controlmenu
+ * @class controlmenu
+ * @constructor
+ * @extends M.core.dialogue
+ */
+CONTROLMENU = function(config) {
+ config.draggable = false;
+ config.centered = false;
+ config.width = 'auto';
+ config.lightbox = false;
+ //config.visible = false;
+ config.footerContent = '';
+ CONTROLMENU.superclass.constructor.apply(this, [config]);
+};
+
+Y.extend(CONTROLMENU, M.core.dialogue, {
+
+ /**
+ * Initialise the menu.
+ *
+ * @method initializer
+ * @return void
+ */
+ initializer : function(config) {
+ var body, headertext, bb;
+ CONTROLMENU.superclass.initializer.call(this, config);
+
+ bb = this.get('boundingBox');
+ bb.addClass('editor_atto_controlmenu');
+
+ // Close the menu when clicked outside (excluding the button that opened the menu).
+ body = this.bodyNode;
+
+ headertext = Y.Node.create('');
+ headertext.addClass('accesshide');
+ headertext.setHTML(this.get('headerText'));
+ body.prepend(headertext);
+
+ body.on('clickoutside', function(e) {
+ if (this.get('visible')) {
+ // Note: we need to compare ids because for some reason - sometimes button is an Object, not a Y.Node.
+ if (!e.target.ancestor('.atto_control')) {
+ e.preventDefault();
+ this.hide();
+ }
+ }
+ }, this);
+ }
+
+}, {
+ NAME : CONTROLMENU_NAME,
+ ATTRS : {
+ /**
+ * The header for the drop down (only accessible to screen readers).
+ *
+ * @attribute headerText
+ * @type String
+ * @default ''
+ */
+ headerText : {
+ value : ''
+ }
+
+ }
+});
+
+M.editor_atto = M.editor_atto || {};
+M.editor_atto.controlmenu = CONTROLMENU;
+
+
+}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event", "moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
new file mode 100644
index 00000000000..58b9869b39d
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -0,0 +1,2 @@
+YUI.add("moodle-editor_atto-editor",function(e,t){CSS={CONTENT:"editor_atto_content",CONTENTWRAPPER:"editor_atto_content_wrap",TOOLBAR:"editor_atto_toolbar",WRAPPER:"editor_atto"},M.editor_atto=M.editor_atto||{buttonhandlers:{},textupdatedhandlers:{},menus:{},menuhandlers:{},filepickeroptions:{},widgets:{},showhide_menu_handler:function(e){e.preventDefault();var t=this.getAttribute("disabled"),n=this.getAttribute("data-menu"),r=M.editor_atto.menus[n],i=r.get("bodyContent");r.get("visible")||t?(r.hide(),i.detach("clickoutside")):(i.on("clickoutside",function(e){e.target.ancestor()!==this&&e.target!==this&&r.get("visible")&&(i.detach("clickoutside"),r.hide())},this),r.show(),r.bodyNode.one("a").focus())},buttonclicked_handler:function(e){var t=this.getAttribute("data-editor"),n=this.getAttribute("data-plugin"),r=this.getAttribute("data-handler"),i=M.editor_atto.menus[n+"_"+t];i&&i.hide();if(M.editor_atto.is_enabled(t,n))return r=M.editor_atto.buttonhandlers[r],r(e,t)},is_enabled:function(t,n){var r=e.one("#"+t+"_toolbar .atto_"+n+"_button");return!r.hasAttribute("disabled")},disable_all_widgets:function(t){var n,r;for(n in M.editor_atto.widgets)r=e.one("#"+t+"_toolbar .atto_"+n+"_button"),r&&r.setAttribute("disabled","true")},enable_widget:function(t,n){var r=e.one("#"+t+"_toolbar .atto_"+n+"_button");r&&r.removeAttribute("disabled")},enable_all_widgets:function(t){var n,r;for(n in M.editor_atto.widgets)r=e.one("#"+t+"_toolbar .atto_"+n+"_button"),r&&r.removeAttribute("disabled")},add_text_updated_handler:function(e,t){e in M.editor_atto.textupdatedhandlers||(M.editor_atto.textupdatedhandlers[e]=[]),M.editor_atto.textupdatedhandlers[e].push(t)},add_toolbar_menu:function(t,n,r,i,s){var o=e.one("#"+t+"_toolbar"),u=e.one("#"+t+"_toolbar .atto_group."+i+"_group"),a,f,l,c;u||(u=e.Node.create(''),o.append(u)),l=M.util.image_url(r[0],r[1]),c=M.util.image_url("t/expanded","moodle"),f=e.Node.create('"),u.append(f),a=o.getAttribute("aria-activedescendant"),a||(f.setAttribute("tabindex","0"),o.setAttribute("aria-activedescendant",f.generateID())),M.editor_atto.widgets[n]=n;var h=e.Node.create(''),p=0,d={};for(p=0;p'+d.text+""+"")),M.editor_atto.buttonhandlers[n+"_action_"+p]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+n+"_action_"+p),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"space,enter",".atto_"+n+"_action_"+p),M.editor_atto.buttonhandlers[n+"_action_"+p]=d.handler);M.editor_atto.buttonhandlers[n]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+n+"_button"),M.editor_atto.buttonhandlers[n]=!0);var v=new M.core.dialogue({bodyContent:h,visible:!1,width:"14em",zindex:100,lightbox:!1,closeButton:!1,centered:!1,align:{node:f,points:[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]}});M.editor_atto.menus[n+"_"+t]=v,v.render(),v.hide(),v.headerNode.hide()},add_toolbar_button:function(t,n,r,i,s){var o=e.one("#"+t+"_toolbar"),u=e.one("#"+t+"_toolbar .atto_group."+i+"_group"),a,f,l;u||(u=e.Node.create(''),o.append(u)),l=M.util.image_url(r[0],r[1]),a=e.Node.create('"),u.append(a),f=o.getAttribute("aria-activedescendant"),f||(a.setAttribute("tabindex","0"),o.setAttribute("aria-activedescendant",a.generateID())),M.editor_atto.buttonhandlers[n]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+n+"_button"),M.editor_atto.buttonhandlers[n]=s),M.editor_atto.widgets[n]=n},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&(n=n.pop());var r=null;return n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer),r&&r.ancestor("#"+t+"editable")!==null},focus:function(t){e.one("#"+t+"editable").focus()},init:function(t){var n=e.one("#"+t.elementid),r=e.Node.create(''),i=e.Node.create(''),s="",o=e.Node.create(''),u=e.Node.create('');u.appendChild(i),r.appendChild(o),r.appendChild(u);var a=e.io(t.content_css,{sync:!0}),f=a.responseText.indexOf("font:");f&&(s=a.responseText.substring(f+"font:".length,a.responseText.length-1),i.setStyle("font",s)),i.setStyle("minHeight",1.2*n.getAttribute("rows")+"em"),i.append(n.get("value")),n.get("parentNode").insert(r,n),i.setStyle("color",n.getStyle("color")),i.setStyle("lineHeight",n.getStyle("lineHeight")),i.setStyle("fontSize",n.getStyle("fontSize")),n.hide(),i.on("blur",function(){this.text_updated(t.elementid)},this),e.one(e.config.doc.body).delegate("key",this.keyboard_navigation,"down:37,39","#"+t.elementid+"_toolbar",this,t.elementid),M.editor_atto.filepickeroptions[t.elementid]=t.filepickeroptions},text_updated:function(t){var n=e.one("#"+t
+),r=this.get_clean_html(t);n.set("value",r);var i=0;if(t in M.editor_atto.textupdatedhandlers)for(i=0;i=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),e.one("#"+n+"_toolbar").setAttribute("aria-activedescendant",i.generateID())},show_filepicker:function(t,n,r){e.use("core_filepicker",function(e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,i.editor_target=e.one(t),M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0)return e[0].commonAncestorContainer},get_selection_text:function(){var e=M.editor_atto.get_selection();if(e.length>0&&e[0].cloneContents)return e[0].cloneContents()},set_selection:function(e){var t,n;if(window.getSelection){t=window.getSelection(),t.removeAllRanges();for(n=0;n"),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i),n.on("clickoutside",function(e){this.get("visible")&&(e.target.ancestor(".atto_control")||(e.preventDefault(),this.hide()))},this)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r},"@VERSION@",{requires:["node","io","overlay","escape","event","moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
new file mode 100644
index 00000000000..d5293faf3f5
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -0,0 +1,702 @@
+YUI.add('moodle-editor_atto-editor', 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 .
+
+/**
+ * Atto editor.
+ *
+ * @package editor_atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Classes constants.
+ */
+CSS = {
+ CONTENT: 'editor_atto_content',
+ CONTENTWRAPPER: 'editor_atto_content_wrap',
+ TOOLBAR: 'editor_atto_toolbar',
+ WRAPPER: 'editor_atto'
+};
+
+/**
+ * Atto editor main class.
+ * Common functions required by editor plugins.
+ *
+ * @package editor_atto
+ * @copyright 2013 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.editor_atto = M.editor_atto || {
+
+ /**
+ * List of attached button handlers to prevent duplicates.
+ */
+ buttonhandlers : {},
+
+ /**
+ * List of attached handlers to add inline editing controls to content.
+ */
+ textupdatedhandlers : {},
+
+ /**
+ * List of YUI overlays for custom menus.
+ */
+ menus : {},
+
+ /**
+ * List of attached menu handlers to prevent duplicates.
+ */
+ menuhandlers : {},
+
+ /**
+ * List of file picker options for specific editor instances.
+ */
+ filepickeroptions : {},
+
+ /**
+ * List of buttons and menus that have been added to the toolbar.
+ */
+ widgets : {},
+
+ /**
+ * Toggle a menu.
+ * @param event e
+ */
+ showhide_menu_handler : function(e) {
+ e.preventDefault();
+ var disabled = this.getAttribute('disabled');
+ var overlayid = this.getAttribute('data-menu');
+ var overlay = M.editor_atto.menus[overlayid];
+ var menu = overlay.get('bodyContent');
+ if (overlay.get('visible') || disabled) {
+ overlay.hide();
+ menu.detach('clickoutside');
+ } else {
+ menu.on('clickoutside', function(ev) {
+ if ((ev.target.ancestor() !== this) && (ev.target !== this)) {
+ if (overlay.get('visible')) {
+ menu.detach('clickoutside');
+ overlay.hide();
+ }
+ }
+ }, this);
+ overlay.show();
+ overlay.bodyNode.one('a').focus();
+ }
+ },
+
+ /**
+ * Handle clicks on editor buttons.
+ * @param event e
+ */
+ buttonclicked_handler : function(e) {
+ var elementid = this.getAttribute('data-editor');
+ var plugin = this.getAttribute('data-plugin');
+ var handler = this.getAttribute('data-handler');
+ var overlay = M.editor_atto.menus[plugin + '_' + elementid];
+
+ if (overlay) {
+ overlay.hide();
+ }
+
+ if (M.editor_atto.is_enabled(elementid, plugin)) {
+ // Pass it on.
+ handler = M.editor_atto.buttonhandlers[handler];
+ return handler(e, elementid);
+ }
+ },
+
+ /**
+ * Determine if the specified toolbar button/menu is enabled.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the plugin that created the button/menu.
+ */
+ is_enabled : function(elementid, plugin) {
+ var element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ return !element.hasAttribute('disabled');
+ },
+ /**
+ * Disable all buttons and menus in the toolbar.
+ * @param string elementid, the element id of this editor.
+ */
+ disable_all_widgets : function(elementid) {
+ var plugin, element;
+ for (plugin in M.editor_atto.widgets) {
+ element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.setAttribute('disabled', 'true');
+ }
+ }
+ },
+
+ /**
+ * Enable a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ */
+ enable_widget : function(elementid, plugin) {
+ var element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.removeAttribute('disabled');
+ }
+ },
+
+ /**
+ * Enable all buttons and menus in the toolbar.
+ * @param string elementid, the element id of this editor.
+ */
+ enable_all_widgets : function(elementid) {
+ var plugin, element;
+ for (plugin in M.editor_atto.widgets) {
+ element = Y.one('#' + elementid + '_toolbar .atto_' + plugin + '_button');
+
+ if (element) {
+ element.removeAttribute('disabled');
+ }
+ }
+ },
+
+ /**
+ * Add a content update handler to be called whenever the content is updated.
+ * This is used to add inline editing controls to the content that are cleaned on submission.
+ *
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @handler function callback - The function to do the cleaning.
+ * @param object context - the context to set for the callback.
+ * @handler function handler - A function to call when the button is clicked.
+ */
+ add_text_updated_handler : function(elementid, callback) {
+ if (!(elementid in M.editor_atto.textupdatedhandlers)) {
+ M.editor_atto.textupdatedhandlers[elementid] = [];
+ }
+ M.editor_atto.textupdatedhandlers[elementid].push(callback);
+ },
+
+ /**
+ * Add a button to the toolbar belonging to the editor for element with id "elementid".
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @param string plugin - the plugin defining the button
+ * @param string icon - the html used for the content of the button
+ * @param string groupname - the group the button should be appended to.
+ * @param array entries - List of menu entries with the string (entry.text) and the handlers (entry.handler).
+ */
+ add_toolbar_menu : function(elementid, plugin, icon, groupname, entries) {
+ var toolbar = Y.one('#' + elementid + '_toolbar'),
+ group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
+ currentfocus,
+ button,
+ imgurl,
+ expimgurl;
+
+ if (!group) {
+ group = Y.Node.create('');
+ toolbar.append(group);
+ }
+ imgurl = M.util.image_url(icon[0], icon[1]);
+ expimgurl = M.util.image_url('t/expanded', 'moodle');
+ button = Y.Node.create('');
+
+ group.append(button);
+
+ currentfocus = toolbar.getAttribute('aria-activedescendant');
+ if (!currentfocus) {
+ button.setAttribute('tabindex', '0');
+ toolbar.setAttribute('aria-activedescendant', button.generateID());
+ }
+
+ // Save the name of the plugin.
+ M.editor_atto.widgets[plugin] = plugin;
+
+ var menu = Y.Node.create('');
+ var i = 0, entry = {};
+
+ for (i = 0; i < entries.length; i++) {
+ entry = entries[i];
+
+ menu.append(Y.Node.create('
'));
+ if (!M.editor_atto.buttonhandlers[plugin + '_action_' + i]) {
+ Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_action_' + i);
+ Y.one('body').delegate('key', M.editor_atto.buttonclicked_handler, 'space,enter', '.atto_' + plugin + '_action_' + i);
+ M.editor_atto.buttonhandlers[plugin + '_action_' + i] = entry.handler;
+ }
+ }
+
+ if (!M.editor_atto.buttonhandlers[plugin]) {
+ Y.one('body').delegate('click', M.editor_atto.showhide_menu_handler, '.atto_' + plugin + '_button');
+ M.editor_atto.buttonhandlers[plugin] = true;
+ }
+
+ var overlay = new M.core.dialogue({
+ bodyContent : menu,
+ visible : false,
+ width: '14em',
+ zindex: 100,
+ lightbox: false,
+ closeButton: false,
+ centered : false,
+ align: {node: button, points: [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]}
+ });
+
+ M.editor_atto.menus[plugin + '_' + elementid] = overlay;
+ overlay.render();
+ overlay.hide();
+ overlay.headerNode.hide();
+ },
+
+ /**
+ * Add a button to the toolbar belonging to the editor for element with id "elementid".
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @param string plugin - the plugin defining the button.
+ * @param string icon - the html used for the content of the button.
+ * @param string groupname - the group the button should be appended to.
+ * @handler function handler- A function to call when the button is clicked.
+ */
+ add_toolbar_button : function(elementid, plugin, icon, groupname, handler) {
+ var toolbar = Y.one('#' + elementid + '_toolbar'),
+ group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
+ button,
+ currentfocus,
+ imgurl;
+
+ if (!group) {
+ group = Y.Node.create('');
+ toolbar.append(group);
+ }
+ imgurl = M.util.image_url(icon[0], icon[1]);
+ button = Y.Node.create('');
+
+ group.append(button);
+
+ currentfocus = toolbar.getAttribute('aria-activedescendant');
+ if (!currentfocus) {
+ button.setAttribute('tabindex', '0');
+ toolbar.setAttribute('aria-activedescendant', button.generateID());
+ }
+
+ // We only need to attach this once.
+ if (!M.editor_atto.buttonhandlers[plugin]) {
+ Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_button');
+ M.editor_atto.buttonhandlers[plugin] = handler;
+ }
+
+ // Save the name of the plugin.
+ M.editor_atto.widgets[plugin] = plugin;
+
+ },
+
+ /**
+ * Work out if the cursor is in the editable area for this editor instance.
+ * @param string elementid of this editor
+ * @return bool
+ */
+ is_active : function(elementid) {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ var node = null;
+ if (selection.parentElement) {
+ node = Y.one(selection.parentElement());
+ } else {
+ node = Y.one(selection.startContainer);
+ }
+
+ return node && node.ancestor('#' + elementid + 'editable') !== null;
+ },
+
+ /**
+ * Focus on the editable area for this editor.
+ * @param string elementid of this editor
+ */
+ focus : function(elementid) {
+ Y.one('#' + elementid + 'editable').focus();
+ },
+
+ /**
+ * Initialise the editor
+ * @param object params for this editor instance.
+ */
+ init : function(params) {
+ var textarea = Y.one('#' +params.elementid);
+ var wrapper = Y.Node.create('');
+ var atto = Y.Node.create('');
+
+ var cssfont = '';
+ var toolbar = Y.Node.create('');
+
+ // Editable content wrapper.
+ var content = Y.Node.create('');
+ content.appendChild(atto);
+
+ // Add everything to the wrapper.
+ wrapper.appendChild(toolbar);
+ wrapper.appendChild(content);
+
+ // Bleh - why are we sent a url and not the css to apply directly?
+ var css = Y.io(params.content_css, { sync: true });
+ var pos = css.responseText.indexOf('font:');
+ if (pos) {
+ cssfont = css.responseText.substring(pos + 'font:'.length, css.responseText.length - 1);
+ atto.setStyle('font', cssfont);
+ }
+ atto.setStyle('minHeight', (1.2 * (textarea.getAttribute('rows'))) + 'em');
+
+ // Copy text to editable div.
+ atto.append(textarea.get('value'));
+
+ // Add the toolbar and editable zone to the page.
+ textarea.get('parentNode').insert(wrapper, textarea);
+ atto.setStyle('color', textarea.getStyle('color'));
+ atto.setStyle('lineHeight', textarea.getStyle('lineHeight'));
+ atto.setStyle('fontSize', textarea.getStyle('fontSize'));
+ // Hide the old textarea.
+ textarea.hide();
+
+ // Copy the current value back to the textarea when focus leaves us.
+ atto.on('blur', function() {
+ this.text_updated(params.elementid);
+ }, this);
+
+ // Listen for Arrow left and Arrow right keys.
+ Y.one(Y.config.doc.body).delegate('key',
+ this.keyboard_navigation,
+ 'down:37,39',
+ '#' + params.elementid + '_toolbar',
+ this,
+ params.elementid);
+
+ // Save the file picker options for later.
+ M.editor_atto.filepickeroptions[params.elementid] = params.filepickeroptions;
+ },
+
+ /**
+ * The text in the contenteditable region has been updated,
+ * clean and copy the buffer to the text area.
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ text_updated : function(elementid) {
+ var textarea = Y.one('#' + elementid),
+ cleancontent = this.get_clean_html(elementid);
+ textarea.set('value', cleancontent);
+ // Trigger handlers for this action.
+ var i = 0;
+ if (elementid in M.editor_atto.textupdatedhandlers) {
+ for (i = 0; i < M.editor_atto.textupdatedhandlers[elementid].length; i++) {
+ var callback = M.editor_atto.textupdatedhandlers[elementid][i];
+ callback(elementid);
+ }
+ }
+ },
+
+ /**
+ * Remove all YUI ids from the generated HTML.
+ * @param string elementid - the id of the textarea we created this editor from.
+ * @return string HTML stripped of YUI ids
+ */
+ get_clean_html : function(elementid) {
+ var atto = Y.one('#' + elementid + 'editable').cloneNode(true);
+
+ Y.each(atto.all('[id]'), function(node) {
+ var id = node.get('id');
+ if (id.indexOf('yui') === 0) {
+ node.removeAttribute('id');
+ }
+ });
+
+ Y.each(atto.all('.atto_control'), function(node) {
+ node.remove(true);
+ });
+
+ return atto.getHTML();
+ },
+
+ /**
+ * Implement arrow key navigation for the buttons in the toolbar.
+ * @param Event e - the keyboard event.
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ keyboard_navigation : function(e, elementid) {
+ var buttons,
+ current,
+ currentid,
+ currentindex;
+
+ e.preventDefault();
+
+ buttons = Y.all('#' + elementid + '_toolbar button');
+ currentid = Y.one('#' + elementid + '_toolbar').getAttribute('aria-activedescendant');
+ if (!currentid) {
+ return;
+ }
+ current = Y.one('#' + currentid);
+ current.setAttribute('tabindex', '-1');
+
+ currentindex = buttons.indexOf(current);
+
+ if (e.keyCode === 37) {
+ // Left
+ currentindex--;
+ if (currentindex < 0) {
+ currentindex = buttons.size()-1;
+ }
+ } else {
+ // Right
+ currentindex++;
+ if (currentindex >= buttons.size()) {
+ currentindex = 0;
+ }
+ }
+
+ current = buttons.item(currentindex);
+ current.setAttribute('tabindex', '0');
+ current.focus();
+ Y.one('#' + elementid + '_toolbar').setAttribute('aria-activedescendant', current.generateID());
+ },
+
+ /**
+ * Show the filepicker.
+ * @param string elementid for this editor instance.
+ * @param string type The media type for the file picker
+ * @param function callback
+ */
+ show_filepicker : function(elementid, type, callback) {
+ Y.use('core_filepicker', function (Y) {
+ var options = M.editor_atto.filepickeroptions[elementid][type];
+
+ options.formcallback = callback;
+ options.editor_target = Y.one(elementid);
+
+ M.core_filepicker.show(Y, options);
+ });
+ },
+
+ /**
+ * Create a cross browser selection object that represents a yui node.
+ * @param Node yui node for the selection
+ * @return range (browser dependent)
+ */
+ get_selection_from_node: function(node) {
+ var range;
+
+ if (window.getSelection) {
+ range = document.createRange();
+
+ range.setStartBefore(node.getDOMNode());
+ range.setEndAfter(node.getDOMNode());
+ return [range];
+ } else if (document.selection) {
+ range = document.body.createTextRange();
+ range.moveToElementText(node.getDOMNode());
+ return range;
+ }
+ return false;
+ },
+
+ /**
+ * Get the selection object that can be passed back to set_selection.
+ * @return range (browser dependent)
+ */
+ get_selection : function() {
+ if (window.getSelection) {
+ var sel = window.getSelection();
+ var ranges = [], i = 0;
+ for (i = 0; i < sel.rangeCount; i++) {
+ ranges.push(sel.getRangeAt(i));
+ }
+ return ranges;
+ } else if (document.selection) {
+ // IE < 9
+ if (document.selection.createRange) {
+ return document.selection.createRange();
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Check that a YUI node it at least partly contained by the selection.
+ * @param Range selection
+ * @param Y.Node node
+ * @return boolean
+ */
+ selection_contains_node : function(node) {
+ var range, sel;
+ if (window.getSelection) {
+ sel = window.getSelection();
+
+ if (sel.containsNode) {
+ return sel.containsNode(node.getDOMNode(), true);
+ }
+ }
+ sel = document.selection.createRange();
+ range = sel.duplicate();
+ range.moveToElementText(node.getDOMNode());
+ return sel.inRange(range);
+ },
+
+ /**
+ * Get the dom node representing the common anscestor of the selection nodes.
+ * @return DOMNode
+ */
+ get_selection_parent_node : function() {
+ var selection = M.editor_atto.get_selection();
+ if (selection.length > 0) {
+ return selection[0].commonAncestorContainer;
+ }
+ },
+
+ /**
+ * Get the list of child nodes of the selection.
+ * @return DOMNode[]
+ */
+ get_selection_text : function() {
+ var selection = M.editor_atto.get_selection();
+ if (selection.length > 0 && selection[0].cloneContents) {
+ return selection[0].cloneContents();
+ }
+ },
+
+ /**
+ * Set the current selection. Used to restore a selection.
+ */
+ set_selection : function(selection) {
+ var sel, i;
+
+ if (window.getSelection) {
+ sel = window.getSelection();
+ sel.removeAllRanges();
+ for (i = 0; i < selection.length; i++) {
+ sel.addRange(selection[i]);
+ }
+ } else if (document.selection) {
+ // IE < 9
+ if (selection.select) {
+ selection.select();
+ }
+ }
+ }
+
+};
diff --git a/lib/editor/atto/yui/src/editor/meta/editor.json b/lib/editor/atto/yui/src/editor/meta/editor.json
new file mode 100644
index 00000000000..e50017fd4d7
--- /dev/null
+++ b/lib/editor/atto/yui/src/editor/meta/editor.json
@@ -0,0 +1,12 @@
+{
+ "moodle-editor_atto-editor": {
+ "requires": [
+ "node",
+ "io",
+ "overlay",
+ "escape",
+ "event",
+ "moodle-core-notification"
+ ]
+ }
+}
diff --git a/lib/editorlib.php b/lib/editorlib.php
index fbafa1a5a47..118d6757d90 100644
--- a/lib/editorlib.php
+++ b/lib/editorlib.php
@@ -95,7 +95,7 @@ function editors_get_enabled() {
global $CFG;
if (empty($CFG->texteditors)) {
- $CFG->texteditors = 'tinymce,textarea';
+ $CFG->texteditors = 'atto,tinymce,textarea';
}
$active = array();
foreach(explode(',', $CFG->texteditors) as $e) {
@@ -153,7 +153,7 @@ function editors_head_setup() {
global $CFG;
if (empty($CFG->texteditors)) {
- $CFG->texteditors = 'tinymce,textarea';
+ $CFG->texteditors = 'atto,tinymce,textarea';
}
$active = explode(',', $CFG->texteditors);
From 3df7cfc59a221de6c61d86fad0a6975fc7e6b426 Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Tue, 28 Jan 2014 13:58:47 +0800
Subject: [PATCH 02/63] MDL-43842 Atto: Move pix-icons into core pix folder.
---
lib/editor/atto/plugins/bold/lib.php | 2 +-
lib/editor/atto/plugins/clear/lib.php | 2 +-
lib/editor/atto/plugins/html/lib.php | 2 +-
lib/editor/atto/plugins/image/lib.php | 2 +-
lib/editor/atto/plugins/indent/lib.php | 2 +-
lib/editor/atto/plugins/italic/lib.php | 2 +-
lib/editor/atto/plugins/link/lib.php | 2 +-
lib/editor/atto/plugins/media/lib.php | 2 +-
lib/editor/atto/plugins/orderedlist/lib.php | 2 +-
lib/editor/atto/plugins/outdent/lib.php | 2 +-
lib/editor/atto/plugins/strike/lib.php | 2 +-
lib/editor/atto/plugins/table/lib.php | 2 +-
lib/editor/atto/plugins/title/lib.php | 2 +-
lib/editor/atto/plugins/underline/lib.php | 2 +-
lib/editor/atto/plugins/unlink/lib.php | 2 +-
lib/editor/atto/plugins/unorderedlist/lib.php | 2 +-
{lib/editor/atto/pix => pix}/e/abbr.png | Bin
{lib/editor/atto/pix => pix}/e/abbr.svg | 0
{lib/editor/atto/pix => pix}/e/absolute.png | Bin
{lib/editor/atto/pix => pix}/e/absolute.svg | 0
{lib/editor/atto/pix => pix}/e/acronym.png | Bin
{lib/editor/atto/pix => pix}/e/acronym.svg | 0
{lib/editor/atto/pix => pix}/e/advance_hr.png | Bin
{lib/editor/atto/pix => pix}/e/advance_hr.svg | 0
{lib/editor/atto/pix => pix}/e/align_center.png | Bin
{lib/editor/atto/pix => pix}/e/align_center.svg | 0
{lib/editor/atto/pix => pix}/e/align_left.png | Bin
{lib/editor/atto/pix => pix}/e/align_left.svg | 0
{lib/editor/atto/pix => pix}/e/align_right.png | Bin
{lib/editor/atto/pix => pix}/e/align_right.svg | 0
{lib/editor/atto/pix => pix}/e/anchor.png | Bin
{lib/editor/atto/pix => pix}/e/anchor.svg | 0
{lib/editor/atto/pix => pix}/e/backward.png | Bin
{lib/editor/atto/pix => pix}/e/backward.svg | 0
{lib/editor/atto/pix => pix}/e/bold.png | Bin
{lib/editor/atto/pix => pix}/e/bold.svg | 0
{lib/editor/atto/pix => pix}/e/bullet_list.png | Bin
{lib/editor/atto/pix => pix}/e/bullet_list.svg | 0
{lib/editor/atto/pix => pix}/e/cell_props.png | Bin
{lib/editor/atto/pix => pix}/e/cell_props.svg | 0
{lib/editor/atto/pix => pix}/e/cite.png | Bin
{lib/editor/atto/pix => pix}/e/cite.svg | 0
.../atto/pix => pix}/e/cleanup_messy_code.png | Bin
.../atto/pix => pix}/e/cleanup_messy_code.svg | 0
{lib/editor/atto/pix => pix}/e/clear_formatting.png | Bin
{lib/editor/atto/pix => pix}/e/clear_formatting.svg | 0
{lib/editor/atto/pix => pix}/e/copy.png | Bin
{lib/editor/atto/pix => pix}/e/copy.svg | 0
{lib/editor/atto/pix => pix}/e/cut.png | Bin
{lib/editor/atto/pix => pix}/e/cut.svg | 0
{lib/editor/atto/pix => pix}/e/decrease_indent.png | Bin
{lib/editor/atto/pix => pix}/e/decrease_indent.svg | 0
{lib/editor/atto/pix => pix}/e/delete.png | Bin
{lib/editor/atto/pix => pix}/e/delete.svg | 0
{lib/editor/atto/pix => pix}/e/delete_col.png | Bin
{lib/editor/atto/pix => pix}/e/delete_col.svg | 0
{lib/editor/atto/pix => pix}/e/delete_row.png | Bin
{lib/editor/atto/pix => pix}/e/delete_row.svg | 0
{lib/editor/atto/pix => pix}/e/delete_table.png | Bin
{lib/editor/atto/pix => pix}/e/delete_table.svg | 0
.../atto/pix => pix}/e/document_properties.png | Bin
.../atto/pix => pix}/e/document_properties.svg | 0
{lib/editor/atto/pix => pix}/e/dragmath.png | Bin
{lib/editor/atto/pix => pix}/e/dragmath.svg | 0
{lib/editor/atto/pix => pix}/e/emoticons.png | Bin
{lib/editor/atto/pix => pix}/e/emoticons.svg | 0
{lib/editor/atto/pix => pix}/e/find_replace.png | Bin
{lib/editor/atto/pix => pix}/e/find_replace.svg | 0
{lib/editor/atto/pix => pix}/e/forward.png | Bin
{lib/editor/atto/pix => pix}/e/forward.svg | 0
{lib/editor/atto/pix => pix}/e/fullpage.png | Bin
{lib/editor/atto/pix => pix}/e/fullpage.svg | 0
{lib/editor/atto/pix => pix}/e/fullscreen.png | Bin
{lib/editor/atto/pix => pix}/e/fullscreen.svg | 0
{lib/editor/atto/pix => pix}/e/help.png | Bin
{lib/editor/atto/pix => pix}/e/help.svg | 0
{lib/editor/atto/pix => pix}/e/increase_indent.png | Bin
{lib/editor/atto/pix => pix}/e/increase_indent.svg | 0
{lib/editor/atto/pix => pix}/e/insert.png | Bin
{lib/editor/atto/pix => pix}/e/insert.svg | 0
{lib/editor/atto/pix => pix}/e/insert_col_after.png | Bin
{lib/editor/atto/pix => pix}/e/insert_col_after.svg | 0
.../editor/atto/pix => pix}/e/insert_col_before.png | Bin
.../editor/atto/pix => pix}/e/insert_col_before.svg | 0
{lib/editor/atto/pix => pix}/e/insert_date.png | Bin
{lib/editor/atto/pix => pix}/e/insert_date.svg | 0
.../editor/atto/pix => pix}/e/insert_edit_image.png | Bin
.../editor/atto/pix => pix}/e/insert_edit_image.svg | 0
{lib/editor/atto/pix => pix}/e/insert_edit_link.png | Bin
{lib/editor/atto/pix => pix}/e/insert_edit_link.svg | 0
.../editor/atto/pix => pix}/e/insert_edit_video.png | Bin
.../editor/atto/pix => pix}/e/insert_edit_video.svg | 0
{lib/editor/atto/pix => pix}/e/insert_file.png | Bin
{lib/editor/atto/pix => pix}/e/insert_file.svg | 0
.../atto/pix => pix}/e/insert_horizontal_ruler.png | Bin
.../atto/pix => pix}/e/insert_horizontal_ruler.svg | 0
.../atto/pix => pix}/e/insert_nonbreaking_space.png | Bin
.../atto/pix => pix}/e/insert_nonbreaking_space.svg | 0
{lib/editor/atto/pix => pix}/e/insert_row_after.png | Bin
{lib/editor/atto/pix => pix}/e/insert_row_after.svg | 0
.../editor/atto/pix => pix}/e/insert_row_before.png | Bin
.../editor/atto/pix => pix}/e/insert_row_before.svg | 0
{lib/editor/atto/pix => pix}/e/insert_time.png | Bin
{lib/editor/atto/pix => pix}/e/insert_time.svg | 0
{lib/editor/atto/pix => pix}/e/italic.png | Bin
{lib/editor/atto/pix => pix}/e/italic.svg | 0
{lib/editor/atto/pix => pix}/e/justify.png | Bin
{lib/editor/atto/pix => pix}/e/justify.svg | 0
{lib/editor/atto/pix => pix}/e/layers.png | Bin
{lib/editor/atto/pix => pix}/e/layers.svg | 0
{lib/editor/atto/pix => pix}/e/layers_over.png | Bin
{lib/editor/atto/pix => pix}/e/layers_over.svg | 0
{lib/editor/atto/pix => pix}/e/layers_under.png | Bin
{lib/editor/atto/pix => pix}/e/layers_under.svg | 0
{lib/editor/atto/pix => pix}/e/left_to_right.png | Bin
{lib/editor/atto/pix => pix}/e/left_to_right.svg | 0
{lib/editor/atto/pix => pix}/e/manage_files.png | Bin
{lib/editor/atto/pix => pix}/e/manage_files.svg | 0
{lib/editor/atto/pix => pix}/e/merge_cells.png | Bin
{lib/editor/atto/pix => pix}/e/merge_cells.svg | 0
{lib/editor/atto/pix => pix}/e/new_document.png | Bin
{lib/editor/atto/pix => pix}/e/new_document.svg | 0
{lib/editor/atto/pix => pix}/e/numbered_list.png | Bin
{lib/editor/atto/pix => pix}/e/numbered_list.svg | 0
{lib/editor/atto/pix => pix}/e/page_break.png | Bin
{lib/editor/atto/pix => pix}/e/page_break.svg | 0
{lib/editor/atto/pix => pix}/e/paste.png | Bin
{lib/editor/atto/pix => pix}/e/paste.svg | 0
{lib/editor/atto/pix => pix}/e/paste_text.png | Bin
{lib/editor/atto/pix => pix}/e/paste_text.svg | 0
{lib/editor/atto/pix => pix}/e/paste_word.png | Bin
{lib/editor/atto/pix => pix}/e/paste_word.svg | 0
{lib/editor/atto/pix => pix}/e/prevent_autolink.png | Bin
{lib/editor/atto/pix => pix}/e/prevent_autolink.svg | 0
{lib/editor/atto/pix => pix}/e/preview.png | Bin
{lib/editor/atto/pix => pix}/e/preview.svg | 0
{lib/editor/atto/pix => pix}/e/print.png | Bin
{lib/editor/atto/pix => pix}/e/print.svg | 0
{lib/editor/atto/pix => pix}/e/question.png | Bin
{lib/editor/atto/pix => pix}/e/question.svg | 0
{lib/editor/atto/pix => pix}/e/redo.png | Bin
{lib/editor/atto/pix => pix}/e/redo.svg | 0
{lib/editor/atto/pix => pix}/e/remove_link.png | Bin
{lib/editor/atto/pix => pix}/e/remove_link.svg | 0
{lib/editor/atto/pix => pix}/e/resize.png | Bin
{lib/editor/atto/pix => pix}/e/resize.svg | 0
{lib/editor/atto/pix => pix}/e/restore_draft.png | Bin
{lib/editor/atto/pix => pix}/e/restore_draft.svg | 0
.../atto/pix => pix}/e/restore_last_draft.png | Bin
.../atto/pix => pix}/e/restore_last_draft.svg | 0
{lib/editor/atto/pix => pix}/e/right_to_left.png | Bin
{lib/editor/atto/pix => pix}/e/right_to_left.svg | 0
{lib/editor/atto/pix => pix}/e/row_props.png | Bin
{lib/editor/atto/pix => pix}/e/row_props.svg | 0
{lib/editor/atto/pix => pix}/e/save.png | Bin
{lib/editor/atto/pix => pix}/e/save.svg | 0
{lib/editor/atto/pix => pix}/e/search.png | Bin
{lib/editor/atto/pix => pix}/e/search.svg | 0
{lib/editor/atto/pix => pix}/e/select_all.png | Bin
{lib/editor/atto/pix => pix}/e/select_all.svg | 0
.../pix => pix}/e/show_invisible_characters.png | Bin
.../pix => pix}/e/show_invisible_characters.svg | 0
{lib/editor/atto/pix => pix}/e/source_code.png | Bin
{lib/editor/atto/pix => pix}/e/source_code.svg | 0
.../editor/atto/pix => pix}/e/special_character.png | Bin
.../editor/atto/pix => pix}/e/special_character.svg | 0
{lib/editor/atto/pix => pix}/e/spellcheck.png | Bin
{lib/editor/atto/pix => pix}/e/spellcheck.svg | 0
{lib/editor/atto/pix => pix}/e/split_cells.png | Bin
{lib/editor/atto/pix => pix}/e/split_cells.svg | 0
{lib/editor/atto/pix => pix}/e/strikethrough.png | Bin
{lib/editor/atto/pix => pix}/e/strikethrough.svg | 0
{lib/editor/atto/pix => pix}/e/styleprops.png | Bin
{lib/editor/atto/pix => pix}/e/styleprops.svg | 0
{lib/editor/atto/pix => pix}/e/subscript.png | Bin
{lib/editor/atto/pix => pix}/e/subscript.svg | 0
{lib/editor/atto/pix => pix}/e/superscript.png | Bin
{lib/editor/atto/pix => pix}/e/superscript.svg | 0
{lib/editor/atto/pix => pix}/e/table.png | Bin
{lib/editor/atto/pix => pix}/e/table.svg | 0
{lib/editor/atto/pix => pix}/e/table_props.png | Bin
{lib/editor/atto/pix => pix}/e/table_props.svg | 0
{lib/editor/atto/pix => pix}/e/template.png | Bin
{lib/editor/atto/pix => pix}/e/template.svg | 0
{lib/editor/atto/pix => pix}/e/text_color.png | Bin
{lib/editor/atto/pix => pix}/e/text_color.svg | 0
{lib/editor/atto/pix => pix}/e/text_highlight.png | Bin
{lib/editor/atto/pix => pix}/e/text_highlight.svg | 0
{lib/editor/atto/pix => pix}/e/tick.png | Bin
{lib/editor/atto/pix => pix}/e/tick.svg | 0
.../editor/atto/pix => pix}/e/toggle_blockquote.png | Bin
.../editor/atto/pix => pix}/e/toggle_blockquote.svg | 0
{lib/editor/atto/pix => pix}/e/underline.png | Bin
{lib/editor/atto/pix => pix}/e/underline.svg | 0
{lib/editor/atto/pix => pix}/e/undo.png | Bin
{lib/editor/atto/pix => pix}/e/undo.svg | 0
{lib/editor/atto/pix => pix}/e/visual_aid.png | Bin
{lib/editor/atto/pix => pix}/e/visual_aid.svg | 0
{lib/editor/atto/pix => pix}/e/visual_blocks.png | Bin
{lib/editor/atto/pix => pix}/e/visual_blocks.svg | 0
200 files changed, 16 insertions(+), 16 deletions(-)
rename {lib/editor/atto/pix => pix}/e/abbr.png (100%)
rename {lib/editor/atto/pix => pix}/e/abbr.svg (100%)
rename {lib/editor/atto/pix => pix}/e/absolute.png (100%)
rename {lib/editor/atto/pix => pix}/e/absolute.svg (100%)
rename {lib/editor/atto/pix => pix}/e/acronym.png (100%)
rename {lib/editor/atto/pix => pix}/e/acronym.svg (100%)
rename {lib/editor/atto/pix => pix}/e/advance_hr.png (100%)
rename {lib/editor/atto/pix => pix}/e/advance_hr.svg (100%)
rename {lib/editor/atto/pix => pix}/e/align_center.png (100%)
rename {lib/editor/atto/pix => pix}/e/align_center.svg (100%)
rename {lib/editor/atto/pix => pix}/e/align_left.png (100%)
rename {lib/editor/atto/pix => pix}/e/align_left.svg (100%)
rename {lib/editor/atto/pix => pix}/e/align_right.png (100%)
rename {lib/editor/atto/pix => pix}/e/align_right.svg (100%)
rename {lib/editor/atto/pix => pix}/e/anchor.png (100%)
rename {lib/editor/atto/pix => pix}/e/anchor.svg (100%)
rename {lib/editor/atto/pix => pix}/e/backward.png (100%)
rename {lib/editor/atto/pix => pix}/e/backward.svg (100%)
rename {lib/editor/atto/pix => pix}/e/bold.png (100%)
rename {lib/editor/atto/pix => pix}/e/bold.svg (100%)
rename {lib/editor/atto/pix => pix}/e/bullet_list.png (100%)
rename {lib/editor/atto/pix => pix}/e/bullet_list.svg (100%)
rename {lib/editor/atto/pix => pix}/e/cell_props.png (100%)
rename {lib/editor/atto/pix => pix}/e/cell_props.svg (100%)
rename {lib/editor/atto/pix => pix}/e/cite.png (100%)
rename {lib/editor/atto/pix => pix}/e/cite.svg (100%)
rename {lib/editor/atto/pix => pix}/e/cleanup_messy_code.png (100%)
rename {lib/editor/atto/pix => pix}/e/cleanup_messy_code.svg (100%)
rename {lib/editor/atto/pix => pix}/e/clear_formatting.png (100%)
rename {lib/editor/atto/pix => pix}/e/clear_formatting.svg (100%)
rename {lib/editor/atto/pix => pix}/e/copy.png (100%)
rename {lib/editor/atto/pix => pix}/e/copy.svg (100%)
rename {lib/editor/atto/pix => pix}/e/cut.png (100%)
rename {lib/editor/atto/pix => pix}/e/cut.svg (100%)
rename {lib/editor/atto/pix => pix}/e/decrease_indent.png (100%)
rename {lib/editor/atto/pix => pix}/e/decrease_indent.svg (100%)
rename {lib/editor/atto/pix => pix}/e/delete.png (100%)
rename {lib/editor/atto/pix => pix}/e/delete.svg (100%)
rename {lib/editor/atto/pix => pix}/e/delete_col.png (100%)
rename {lib/editor/atto/pix => pix}/e/delete_col.svg (100%)
rename {lib/editor/atto/pix => pix}/e/delete_row.png (100%)
rename {lib/editor/atto/pix => pix}/e/delete_row.svg (100%)
rename {lib/editor/atto/pix => pix}/e/delete_table.png (100%)
rename {lib/editor/atto/pix => pix}/e/delete_table.svg (100%)
rename {lib/editor/atto/pix => pix}/e/document_properties.png (100%)
rename {lib/editor/atto/pix => pix}/e/document_properties.svg (100%)
rename {lib/editor/atto/pix => pix}/e/dragmath.png (100%)
rename {lib/editor/atto/pix => pix}/e/dragmath.svg (100%)
rename {lib/editor/atto/pix => pix}/e/emoticons.png (100%)
rename {lib/editor/atto/pix => pix}/e/emoticons.svg (100%)
rename {lib/editor/atto/pix => pix}/e/find_replace.png (100%)
rename {lib/editor/atto/pix => pix}/e/find_replace.svg (100%)
rename {lib/editor/atto/pix => pix}/e/forward.png (100%)
rename {lib/editor/atto/pix => pix}/e/forward.svg (100%)
rename {lib/editor/atto/pix => pix}/e/fullpage.png (100%)
rename {lib/editor/atto/pix => pix}/e/fullpage.svg (100%)
rename {lib/editor/atto/pix => pix}/e/fullscreen.png (100%)
rename {lib/editor/atto/pix => pix}/e/fullscreen.svg (100%)
rename {lib/editor/atto/pix => pix}/e/help.png (100%)
rename {lib/editor/atto/pix => pix}/e/help.svg (100%)
rename {lib/editor/atto/pix => pix}/e/increase_indent.png (100%)
rename {lib/editor/atto/pix => pix}/e/increase_indent.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_col_after.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_col_after.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_col_before.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_col_before.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_date.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_date.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_image.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_image.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_link.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_link.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_video.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_edit_video.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_file.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_file.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_horizontal_ruler.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_horizontal_ruler.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_nonbreaking_space.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_nonbreaking_space.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_row_after.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_row_after.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_row_before.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_row_before.svg (100%)
rename {lib/editor/atto/pix => pix}/e/insert_time.png (100%)
rename {lib/editor/atto/pix => pix}/e/insert_time.svg (100%)
rename {lib/editor/atto/pix => pix}/e/italic.png (100%)
rename {lib/editor/atto/pix => pix}/e/italic.svg (100%)
rename {lib/editor/atto/pix => pix}/e/justify.png (100%)
rename {lib/editor/atto/pix => pix}/e/justify.svg (100%)
rename {lib/editor/atto/pix => pix}/e/layers.png (100%)
rename {lib/editor/atto/pix => pix}/e/layers.svg (100%)
rename {lib/editor/atto/pix => pix}/e/layers_over.png (100%)
rename {lib/editor/atto/pix => pix}/e/layers_over.svg (100%)
rename {lib/editor/atto/pix => pix}/e/layers_under.png (100%)
rename {lib/editor/atto/pix => pix}/e/layers_under.svg (100%)
rename {lib/editor/atto/pix => pix}/e/left_to_right.png (100%)
rename {lib/editor/atto/pix => pix}/e/left_to_right.svg (100%)
rename {lib/editor/atto/pix => pix}/e/manage_files.png (100%)
rename {lib/editor/atto/pix => pix}/e/manage_files.svg (100%)
rename {lib/editor/atto/pix => pix}/e/merge_cells.png (100%)
rename {lib/editor/atto/pix => pix}/e/merge_cells.svg (100%)
rename {lib/editor/atto/pix => pix}/e/new_document.png (100%)
rename {lib/editor/atto/pix => pix}/e/new_document.svg (100%)
rename {lib/editor/atto/pix => pix}/e/numbered_list.png (100%)
rename {lib/editor/atto/pix => pix}/e/numbered_list.svg (100%)
rename {lib/editor/atto/pix => pix}/e/page_break.png (100%)
rename {lib/editor/atto/pix => pix}/e/page_break.svg (100%)
rename {lib/editor/atto/pix => pix}/e/paste.png (100%)
rename {lib/editor/atto/pix => pix}/e/paste.svg (100%)
rename {lib/editor/atto/pix => pix}/e/paste_text.png (100%)
rename {lib/editor/atto/pix => pix}/e/paste_text.svg (100%)
rename {lib/editor/atto/pix => pix}/e/paste_word.png (100%)
rename {lib/editor/atto/pix => pix}/e/paste_word.svg (100%)
rename {lib/editor/atto/pix => pix}/e/prevent_autolink.png (100%)
rename {lib/editor/atto/pix => pix}/e/prevent_autolink.svg (100%)
rename {lib/editor/atto/pix => pix}/e/preview.png (100%)
rename {lib/editor/atto/pix => pix}/e/preview.svg (100%)
rename {lib/editor/atto/pix => pix}/e/print.png (100%)
rename {lib/editor/atto/pix => pix}/e/print.svg (100%)
rename {lib/editor/atto/pix => pix}/e/question.png (100%)
rename {lib/editor/atto/pix => pix}/e/question.svg (100%)
rename {lib/editor/atto/pix => pix}/e/redo.png (100%)
rename {lib/editor/atto/pix => pix}/e/redo.svg (100%)
rename {lib/editor/atto/pix => pix}/e/remove_link.png (100%)
rename {lib/editor/atto/pix => pix}/e/remove_link.svg (100%)
rename {lib/editor/atto/pix => pix}/e/resize.png (100%)
rename {lib/editor/atto/pix => pix}/e/resize.svg (100%)
rename {lib/editor/atto/pix => pix}/e/restore_draft.png (100%)
rename {lib/editor/atto/pix => pix}/e/restore_draft.svg (100%)
rename {lib/editor/atto/pix => pix}/e/restore_last_draft.png (100%)
rename {lib/editor/atto/pix => pix}/e/restore_last_draft.svg (100%)
rename {lib/editor/atto/pix => pix}/e/right_to_left.png (100%)
rename {lib/editor/atto/pix => pix}/e/right_to_left.svg (100%)
rename {lib/editor/atto/pix => pix}/e/row_props.png (100%)
rename {lib/editor/atto/pix => pix}/e/row_props.svg (100%)
rename {lib/editor/atto/pix => pix}/e/save.png (100%)
rename {lib/editor/atto/pix => pix}/e/save.svg (100%)
rename {lib/editor/atto/pix => pix}/e/search.png (100%)
rename {lib/editor/atto/pix => pix}/e/search.svg (100%)
rename {lib/editor/atto/pix => pix}/e/select_all.png (100%)
rename {lib/editor/atto/pix => pix}/e/select_all.svg (100%)
rename {lib/editor/atto/pix => pix}/e/show_invisible_characters.png (100%)
rename {lib/editor/atto/pix => pix}/e/show_invisible_characters.svg (100%)
rename {lib/editor/atto/pix => pix}/e/source_code.png (100%)
rename {lib/editor/atto/pix => pix}/e/source_code.svg (100%)
rename {lib/editor/atto/pix => pix}/e/special_character.png (100%)
rename {lib/editor/atto/pix => pix}/e/special_character.svg (100%)
rename {lib/editor/atto/pix => pix}/e/spellcheck.png (100%)
rename {lib/editor/atto/pix => pix}/e/spellcheck.svg (100%)
rename {lib/editor/atto/pix => pix}/e/split_cells.png (100%)
rename {lib/editor/atto/pix => pix}/e/split_cells.svg (100%)
rename {lib/editor/atto/pix => pix}/e/strikethrough.png (100%)
rename {lib/editor/atto/pix => pix}/e/strikethrough.svg (100%)
rename {lib/editor/atto/pix => pix}/e/styleprops.png (100%)
rename {lib/editor/atto/pix => pix}/e/styleprops.svg (100%)
rename {lib/editor/atto/pix => pix}/e/subscript.png (100%)
rename {lib/editor/atto/pix => pix}/e/subscript.svg (100%)
rename {lib/editor/atto/pix => pix}/e/superscript.png (100%)
rename {lib/editor/atto/pix => pix}/e/superscript.svg (100%)
rename {lib/editor/atto/pix => pix}/e/table.png (100%)
rename {lib/editor/atto/pix => pix}/e/table.svg (100%)
rename {lib/editor/atto/pix => pix}/e/table_props.png (100%)
rename {lib/editor/atto/pix => pix}/e/table_props.svg (100%)
rename {lib/editor/atto/pix => pix}/e/template.png (100%)
rename {lib/editor/atto/pix => pix}/e/template.svg (100%)
rename {lib/editor/atto/pix => pix}/e/text_color.png (100%)
rename {lib/editor/atto/pix => pix}/e/text_color.svg (100%)
rename {lib/editor/atto/pix => pix}/e/text_highlight.png (100%)
rename {lib/editor/atto/pix => pix}/e/text_highlight.svg (100%)
rename {lib/editor/atto/pix => pix}/e/tick.png (100%)
rename {lib/editor/atto/pix => pix}/e/tick.svg (100%)
rename {lib/editor/atto/pix => pix}/e/toggle_blockquote.png (100%)
rename {lib/editor/atto/pix => pix}/e/toggle_blockquote.svg (100%)
rename {lib/editor/atto/pix => pix}/e/underline.png (100%)
rename {lib/editor/atto/pix => pix}/e/underline.svg (100%)
rename {lib/editor/atto/pix => pix}/e/undo.png (100%)
rename {lib/editor/atto/pix => pix}/e/undo.svg (100%)
rename {lib/editor/atto/pix => pix}/e/visual_aid.png (100%)
rename {lib/editor/atto/pix => pix}/e/visual_aid.svg (100%)
rename {lib/editor/atto/pix => pix}/e/visual_blocks.png (100%)
rename {lib/editor/atto/pix => pix}/e/visual_blocks.svg (100%)
diff --git a/lib/editor/atto/plugins/bold/lib.php b/lib/editor/atto/plugins/bold/lib.php
index 41b3a2d60d0..99acc45bd6d 100644
--- a/lib/editor/atto/plugins/bold/lib.php
+++ b/lib/editor/atto/plugins/bold/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_bold_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/bold', 'editor_atto');
+ $icon = array('e/bold', 'core');
$PAGE->requires->yui_module('moodle-atto_bold-button',
'M.atto_bold.init',
diff --git a/lib/editor/atto/plugins/clear/lib.php b/lib/editor/atto/plugins/clear/lib.php
index 831ba031b4f..7a5196967e6 100644
--- a/lib/editor/atto/plugins/clear/lib.php
+++ b/lib/editor/atto/plugins/clear/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_clear_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/clear_formatting', 'editor_atto');
+ $icon = array('e/clear_formatting', 'core');
$PAGE->requires->yui_module('moodle-atto_clear-button',
'M.atto_clear.init',
diff --git a/lib/editor/atto/plugins/html/lib.php b/lib/editor/atto/plugins/html/lib.php
index cae6aff569b..5c60123c950 100644
--- a/lib/editor/atto/plugins/html/lib.php
+++ b/lib/editor/atto/plugins/html/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_html_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/source_code', 'editor_atto');
+ $icon = array('e/source_code', 'core');
$PAGE->requires->yui_module('moodle-atto_html-button',
'M.atto_html.init',
diff --git a/lib/editor/atto/plugins/image/lib.php b/lib/editor/atto/plugins/image/lib.php
index 66136465a9b..908cbfbbbbb 100644
--- a/lib/editor/atto/plugins/image/lib.php
+++ b/lib/editor/atto/plugins/image/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_image_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/insert_edit_image', 'editor_atto');
+ $icon = array('e/insert_edit_image', 'core');
$PAGE->requires->strings_for_js(array('createimage',
'enterurl',
diff --git a/lib/editor/atto/plugins/indent/lib.php b/lib/editor/atto/plugins/indent/lib.php
index 4bf3d743e2e..308c1620977 100644
--- a/lib/editor/atto/plugins/indent/lib.php
+++ b/lib/editor/atto/plugins/indent/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_indent_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/increase_indent', 'editor_atto');
+ $icon = array('e/increase_indent', 'core');
$PAGE->requires->yui_module('moodle-atto_indent-button',
'M.atto_indent.init',
diff --git a/lib/editor/atto/plugins/italic/lib.php b/lib/editor/atto/plugins/italic/lib.php
index e8e93aafd9b..8b01b4faec1 100644
--- a/lib/editor/atto/plugins/italic/lib.php
+++ b/lib/editor/atto/plugins/italic/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_italic_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/italic', 'editor_atto');
+ $icon = array('e/italic', 'core');
$PAGE->requires->yui_module('moodle-atto_italic-button',
'M.atto_italic.init',
diff --git a/lib/editor/atto/plugins/link/lib.php b/lib/editor/atto/plugins/link/lib.php
index 6461998a679..d5eee36d4d5 100644
--- a/lib/editor/atto/plugins/link/lib.php
+++ b/lib/editor/atto/plugins/link/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_link_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/insert_edit_link', 'editor_atto');
+ $icon = array('e/insert_edit_link', 'core');
$PAGE->requires->strings_for_js(array('createlink',
'enterurl',
diff --git a/lib/editor/atto/plugins/media/lib.php b/lib/editor/atto/plugins/media/lib.php
index 6133e197d78..c2841bdb268 100644
--- a/lib/editor/atto/plugins/media/lib.php
+++ b/lib/editor/atto/plugins/media/lib.php
@@ -29,7 +29,7 @@
function atto_media_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/insert_edit_video', 'editor_atto');
+ $icon = array('e/insert_edit_video', 'core');
$PAGE->requires->strings_for_js(array('createmedia',
'enterurl',
diff --git a/lib/editor/atto/plugins/orderedlist/lib.php b/lib/editor/atto/plugins/orderedlist/lib.php
index f9e45bcd3fc..57f658d9518 100644
--- a/lib/editor/atto/plugins/orderedlist/lib.php
+++ b/lib/editor/atto/plugins/orderedlist/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_orderedlist_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/numbered_list', 'editor_atto');
+ $icon = array('e/numbered_list', 'core');
$PAGE->requires->yui_module('moodle-atto_orderedlist-button',
'M.atto_orderedlist.init',
diff --git a/lib/editor/atto/plugins/outdent/lib.php b/lib/editor/atto/plugins/outdent/lib.php
index 66d832ac902..2e4a9337da7 100644
--- a/lib/editor/atto/plugins/outdent/lib.php
+++ b/lib/editor/atto/plugins/outdent/lib.php
@@ -29,7 +29,7 @@
function atto_outdent_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/decrease_indent', 'editor_atto');
+ $icon = array('e/decrease_indent', 'core');
$PAGE->requires->yui_module('moodle-atto_outdent-button',
'M.atto_outdent.init',
diff --git a/lib/editor/atto/plugins/strike/lib.php b/lib/editor/atto/plugins/strike/lib.php
index 7e68784a00c..a0d4eb03e55 100644
--- a/lib/editor/atto/plugins/strike/lib.php
+++ b/lib/editor/atto/plugins/strike/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_strike_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/strikethrough', 'editor_atto');
+ $icon = array('e/strikethrough', 'core');
$PAGE->requires->yui_module('moodle-atto_strike-button',
'M.atto_strike.init',
diff --git a/lib/editor/atto/plugins/table/lib.php b/lib/editor/atto/plugins/table/lib.php
index b3a64895d08..452afb6810a 100644
--- a/lib/editor/atto/plugins/table/lib.php
+++ b/lib/editor/atto/plugins/table/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_table_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/table', 'editor_atto');
+ $icon = array('e/table', 'core');
$PAGE->requires->strings_for_js(array('createtable',
'accessibilityhint',
diff --git a/lib/editor/atto/plugins/title/lib.php b/lib/editor/atto/plugins/title/lib.php
index 2a5369c3c0d..4085b934475 100644
--- a/lib/editor/atto/plugins/title/lib.php
+++ b/lib/editor/atto/plugins/title/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_title_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/styleprops', 'editor_atto');
+ $icon = array('e/styleprops', 'core');
$PAGE->requires->strings_for_js(array('h3',
'h4',
diff --git a/lib/editor/atto/plugins/underline/lib.php b/lib/editor/atto/plugins/underline/lib.php
index cc66d3a74b3..d22efe50661 100644
--- a/lib/editor/atto/plugins/underline/lib.php
+++ b/lib/editor/atto/plugins/underline/lib.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
function atto_underline_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/underline', 'editor_atto');
+ $icon = array('e/underline', 'core');
$PAGE->requires->yui_module('moodle-atto_underline-button',
'M.atto_underline.init',
diff --git a/lib/editor/atto/plugins/unlink/lib.php b/lib/editor/atto/plugins/unlink/lib.php
index 8c9f059f3ba..7c8b38040ee 100644
--- a/lib/editor/atto/plugins/unlink/lib.php
+++ b/lib/editor/atto/plugins/unlink/lib.php
@@ -29,7 +29,7 @@
function atto_unlink_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/remove_link', 'editor_atto');
+ $icon = array('e/remove_link', 'core');
$PAGE->requires->yui_module('moodle-atto_unlink-button',
'M.atto_unlink.init',
diff --git a/lib/editor/atto/plugins/unorderedlist/lib.php b/lib/editor/atto/plugins/unorderedlist/lib.php
index 64f07e65e7b..7055d43e75a 100644
--- a/lib/editor/atto/plugins/unorderedlist/lib.php
+++ b/lib/editor/atto/plugins/unorderedlist/lib.php
@@ -29,7 +29,7 @@
function atto_unorderedlist_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = array('e/bullet_list', 'editor_atto');
+ $icon = array('e/bullet_list', 'core');
$PAGE->requires->yui_module('moodle-atto_unorderedlist-button',
'M.atto_unorderedlist.init',
diff --git a/lib/editor/atto/pix/e/abbr.png b/pix/e/abbr.png
similarity index 100%
rename from lib/editor/atto/pix/e/abbr.png
rename to pix/e/abbr.png
diff --git a/lib/editor/atto/pix/e/abbr.svg b/pix/e/abbr.svg
similarity index 100%
rename from lib/editor/atto/pix/e/abbr.svg
rename to pix/e/abbr.svg
diff --git a/lib/editor/atto/pix/e/absolute.png b/pix/e/absolute.png
similarity index 100%
rename from lib/editor/atto/pix/e/absolute.png
rename to pix/e/absolute.png
diff --git a/lib/editor/atto/pix/e/absolute.svg b/pix/e/absolute.svg
similarity index 100%
rename from lib/editor/atto/pix/e/absolute.svg
rename to pix/e/absolute.svg
diff --git a/lib/editor/atto/pix/e/acronym.png b/pix/e/acronym.png
similarity index 100%
rename from lib/editor/atto/pix/e/acronym.png
rename to pix/e/acronym.png
diff --git a/lib/editor/atto/pix/e/acronym.svg b/pix/e/acronym.svg
similarity index 100%
rename from lib/editor/atto/pix/e/acronym.svg
rename to pix/e/acronym.svg
diff --git a/lib/editor/atto/pix/e/advance_hr.png b/pix/e/advance_hr.png
similarity index 100%
rename from lib/editor/atto/pix/e/advance_hr.png
rename to pix/e/advance_hr.png
diff --git a/lib/editor/atto/pix/e/advance_hr.svg b/pix/e/advance_hr.svg
similarity index 100%
rename from lib/editor/atto/pix/e/advance_hr.svg
rename to pix/e/advance_hr.svg
diff --git a/lib/editor/atto/pix/e/align_center.png b/pix/e/align_center.png
similarity index 100%
rename from lib/editor/atto/pix/e/align_center.png
rename to pix/e/align_center.png
diff --git a/lib/editor/atto/pix/e/align_center.svg b/pix/e/align_center.svg
similarity index 100%
rename from lib/editor/atto/pix/e/align_center.svg
rename to pix/e/align_center.svg
diff --git a/lib/editor/atto/pix/e/align_left.png b/pix/e/align_left.png
similarity index 100%
rename from lib/editor/atto/pix/e/align_left.png
rename to pix/e/align_left.png
diff --git a/lib/editor/atto/pix/e/align_left.svg b/pix/e/align_left.svg
similarity index 100%
rename from lib/editor/atto/pix/e/align_left.svg
rename to pix/e/align_left.svg
diff --git a/lib/editor/atto/pix/e/align_right.png b/pix/e/align_right.png
similarity index 100%
rename from lib/editor/atto/pix/e/align_right.png
rename to pix/e/align_right.png
diff --git a/lib/editor/atto/pix/e/align_right.svg b/pix/e/align_right.svg
similarity index 100%
rename from lib/editor/atto/pix/e/align_right.svg
rename to pix/e/align_right.svg
diff --git a/lib/editor/atto/pix/e/anchor.png b/pix/e/anchor.png
similarity index 100%
rename from lib/editor/atto/pix/e/anchor.png
rename to pix/e/anchor.png
diff --git a/lib/editor/atto/pix/e/anchor.svg b/pix/e/anchor.svg
similarity index 100%
rename from lib/editor/atto/pix/e/anchor.svg
rename to pix/e/anchor.svg
diff --git a/lib/editor/atto/pix/e/backward.png b/pix/e/backward.png
similarity index 100%
rename from lib/editor/atto/pix/e/backward.png
rename to pix/e/backward.png
diff --git a/lib/editor/atto/pix/e/backward.svg b/pix/e/backward.svg
similarity index 100%
rename from lib/editor/atto/pix/e/backward.svg
rename to pix/e/backward.svg
diff --git a/lib/editor/atto/pix/e/bold.png b/pix/e/bold.png
similarity index 100%
rename from lib/editor/atto/pix/e/bold.png
rename to pix/e/bold.png
diff --git a/lib/editor/atto/pix/e/bold.svg b/pix/e/bold.svg
similarity index 100%
rename from lib/editor/atto/pix/e/bold.svg
rename to pix/e/bold.svg
diff --git a/lib/editor/atto/pix/e/bullet_list.png b/pix/e/bullet_list.png
similarity index 100%
rename from lib/editor/atto/pix/e/bullet_list.png
rename to pix/e/bullet_list.png
diff --git a/lib/editor/atto/pix/e/bullet_list.svg b/pix/e/bullet_list.svg
similarity index 100%
rename from lib/editor/atto/pix/e/bullet_list.svg
rename to pix/e/bullet_list.svg
diff --git a/lib/editor/atto/pix/e/cell_props.png b/pix/e/cell_props.png
similarity index 100%
rename from lib/editor/atto/pix/e/cell_props.png
rename to pix/e/cell_props.png
diff --git a/lib/editor/atto/pix/e/cell_props.svg b/pix/e/cell_props.svg
similarity index 100%
rename from lib/editor/atto/pix/e/cell_props.svg
rename to pix/e/cell_props.svg
diff --git a/lib/editor/atto/pix/e/cite.png b/pix/e/cite.png
similarity index 100%
rename from lib/editor/atto/pix/e/cite.png
rename to pix/e/cite.png
diff --git a/lib/editor/atto/pix/e/cite.svg b/pix/e/cite.svg
similarity index 100%
rename from lib/editor/atto/pix/e/cite.svg
rename to pix/e/cite.svg
diff --git a/lib/editor/atto/pix/e/cleanup_messy_code.png b/pix/e/cleanup_messy_code.png
similarity index 100%
rename from lib/editor/atto/pix/e/cleanup_messy_code.png
rename to pix/e/cleanup_messy_code.png
diff --git a/lib/editor/atto/pix/e/cleanup_messy_code.svg b/pix/e/cleanup_messy_code.svg
similarity index 100%
rename from lib/editor/atto/pix/e/cleanup_messy_code.svg
rename to pix/e/cleanup_messy_code.svg
diff --git a/lib/editor/atto/pix/e/clear_formatting.png b/pix/e/clear_formatting.png
similarity index 100%
rename from lib/editor/atto/pix/e/clear_formatting.png
rename to pix/e/clear_formatting.png
diff --git a/lib/editor/atto/pix/e/clear_formatting.svg b/pix/e/clear_formatting.svg
similarity index 100%
rename from lib/editor/atto/pix/e/clear_formatting.svg
rename to pix/e/clear_formatting.svg
diff --git a/lib/editor/atto/pix/e/copy.png b/pix/e/copy.png
similarity index 100%
rename from lib/editor/atto/pix/e/copy.png
rename to pix/e/copy.png
diff --git a/lib/editor/atto/pix/e/copy.svg b/pix/e/copy.svg
similarity index 100%
rename from lib/editor/atto/pix/e/copy.svg
rename to pix/e/copy.svg
diff --git a/lib/editor/atto/pix/e/cut.png b/pix/e/cut.png
similarity index 100%
rename from lib/editor/atto/pix/e/cut.png
rename to pix/e/cut.png
diff --git a/lib/editor/atto/pix/e/cut.svg b/pix/e/cut.svg
similarity index 100%
rename from lib/editor/atto/pix/e/cut.svg
rename to pix/e/cut.svg
diff --git a/lib/editor/atto/pix/e/decrease_indent.png b/pix/e/decrease_indent.png
similarity index 100%
rename from lib/editor/atto/pix/e/decrease_indent.png
rename to pix/e/decrease_indent.png
diff --git a/lib/editor/atto/pix/e/decrease_indent.svg b/pix/e/decrease_indent.svg
similarity index 100%
rename from lib/editor/atto/pix/e/decrease_indent.svg
rename to pix/e/decrease_indent.svg
diff --git a/lib/editor/atto/pix/e/delete.png b/pix/e/delete.png
similarity index 100%
rename from lib/editor/atto/pix/e/delete.png
rename to pix/e/delete.png
diff --git a/lib/editor/atto/pix/e/delete.svg b/pix/e/delete.svg
similarity index 100%
rename from lib/editor/atto/pix/e/delete.svg
rename to pix/e/delete.svg
diff --git a/lib/editor/atto/pix/e/delete_col.png b/pix/e/delete_col.png
similarity index 100%
rename from lib/editor/atto/pix/e/delete_col.png
rename to pix/e/delete_col.png
diff --git a/lib/editor/atto/pix/e/delete_col.svg b/pix/e/delete_col.svg
similarity index 100%
rename from lib/editor/atto/pix/e/delete_col.svg
rename to pix/e/delete_col.svg
diff --git a/lib/editor/atto/pix/e/delete_row.png b/pix/e/delete_row.png
similarity index 100%
rename from lib/editor/atto/pix/e/delete_row.png
rename to pix/e/delete_row.png
diff --git a/lib/editor/atto/pix/e/delete_row.svg b/pix/e/delete_row.svg
similarity index 100%
rename from lib/editor/atto/pix/e/delete_row.svg
rename to pix/e/delete_row.svg
diff --git a/lib/editor/atto/pix/e/delete_table.png b/pix/e/delete_table.png
similarity index 100%
rename from lib/editor/atto/pix/e/delete_table.png
rename to pix/e/delete_table.png
diff --git a/lib/editor/atto/pix/e/delete_table.svg b/pix/e/delete_table.svg
similarity index 100%
rename from lib/editor/atto/pix/e/delete_table.svg
rename to pix/e/delete_table.svg
diff --git a/lib/editor/atto/pix/e/document_properties.png b/pix/e/document_properties.png
similarity index 100%
rename from lib/editor/atto/pix/e/document_properties.png
rename to pix/e/document_properties.png
diff --git a/lib/editor/atto/pix/e/document_properties.svg b/pix/e/document_properties.svg
similarity index 100%
rename from lib/editor/atto/pix/e/document_properties.svg
rename to pix/e/document_properties.svg
diff --git a/lib/editor/atto/pix/e/dragmath.png b/pix/e/dragmath.png
similarity index 100%
rename from lib/editor/atto/pix/e/dragmath.png
rename to pix/e/dragmath.png
diff --git a/lib/editor/atto/pix/e/dragmath.svg b/pix/e/dragmath.svg
similarity index 100%
rename from lib/editor/atto/pix/e/dragmath.svg
rename to pix/e/dragmath.svg
diff --git a/lib/editor/atto/pix/e/emoticons.png b/pix/e/emoticons.png
similarity index 100%
rename from lib/editor/atto/pix/e/emoticons.png
rename to pix/e/emoticons.png
diff --git a/lib/editor/atto/pix/e/emoticons.svg b/pix/e/emoticons.svg
similarity index 100%
rename from lib/editor/atto/pix/e/emoticons.svg
rename to pix/e/emoticons.svg
diff --git a/lib/editor/atto/pix/e/find_replace.png b/pix/e/find_replace.png
similarity index 100%
rename from lib/editor/atto/pix/e/find_replace.png
rename to pix/e/find_replace.png
diff --git a/lib/editor/atto/pix/e/find_replace.svg b/pix/e/find_replace.svg
similarity index 100%
rename from lib/editor/atto/pix/e/find_replace.svg
rename to pix/e/find_replace.svg
diff --git a/lib/editor/atto/pix/e/forward.png b/pix/e/forward.png
similarity index 100%
rename from lib/editor/atto/pix/e/forward.png
rename to pix/e/forward.png
diff --git a/lib/editor/atto/pix/e/forward.svg b/pix/e/forward.svg
similarity index 100%
rename from lib/editor/atto/pix/e/forward.svg
rename to pix/e/forward.svg
diff --git a/lib/editor/atto/pix/e/fullpage.png b/pix/e/fullpage.png
similarity index 100%
rename from lib/editor/atto/pix/e/fullpage.png
rename to pix/e/fullpage.png
diff --git a/lib/editor/atto/pix/e/fullpage.svg b/pix/e/fullpage.svg
similarity index 100%
rename from lib/editor/atto/pix/e/fullpage.svg
rename to pix/e/fullpage.svg
diff --git a/lib/editor/atto/pix/e/fullscreen.png b/pix/e/fullscreen.png
similarity index 100%
rename from lib/editor/atto/pix/e/fullscreen.png
rename to pix/e/fullscreen.png
diff --git a/lib/editor/atto/pix/e/fullscreen.svg b/pix/e/fullscreen.svg
similarity index 100%
rename from lib/editor/atto/pix/e/fullscreen.svg
rename to pix/e/fullscreen.svg
diff --git a/lib/editor/atto/pix/e/help.png b/pix/e/help.png
similarity index 100%
rename from lib/editor/atto/pix/e/help.png
rename to pix/e/help.png
diff --git a/lib/editor/atto/pix/e/help.svg b/pix/e/help.svg
similarity index 100%
rename from lib/editor/atto/pix/e/help.svg
rename to pix/e/help.svg
diff --git a/lib/editor/atto/pix/e/increase_indent.png b/pix/e/increase_indent.png
similarity index 100%
rename from lib/editor/atto/pix/e/increase_indent.png
rename to pix/e/increase_indent.png
diff --git a/lib/editor/atto/pix/e/increase_indent.svg b/pix/e/increase_indent.svg
similarity index 100%
rename from lib/editor/atto/pix/e/increase_indent.svg
rename to pix/e/increase_indent.svg
diff --git a/lib/editor/atto/pix/e/insert.png b/pix/e/insert.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert.png
rename to pix/e/insert.png
diff --git a/lib/editor/atto/pix/e/insert.svg b/pix/e/insert.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert.svg
rename to pix/e/insert.svg
diff --git a/lib/editor/atto/pix/e/insert_col_after.png b/pix/e/insert_col_after.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_col_after.png
rename to pix/e/insert_col_after.png
diff --git a/lib/editor/atto/pix/e/insert_col_after.svg b/pix/e/insert_col_after.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_col_after.svg
rename to pix/e/insert_col_after.svg
diff --git a/lib/editor/atto/pix/e/insert_col_before.png b/pix/e/insert_col_before.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_col_before.png
rename to pix/e/insert_col_before.png
diff --git a/lib/editor/atto/pix/e/insert_col_before.svg b/pix/e/insert_col_before.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_col_before.svg
rename to pix/e/insert_col_before.svg
diff --git a/lib/editor/atto/pix/e/insert_date.png b/pix/e/insert_date.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_date.png
rename to pix/e/insert_date.png
diff --git a/lib/editor/atto/pix/e/insert_date.svg b/pix/e/insert_date.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_date.svg
rename to pix/e/insert_date.svg
diff --git a/lib/editor/atto/pix/e/insert_edit_image.png b/pix/e/insert_edit_image.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_image.png
rename to pix/e/insert_edit_image.png
diff --git a/lib/editor/atto/pix/e/insert_edit_image.svg b/pix/e/insert_edit_image.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_image.svg
rename to pix/e/insert_edit_image.svg
diff --git a/lib/editor/atto/pix/e/insert_edit_link.png b/pix/e/insert_edit_link.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_link.png
rename to pix/e/insert_edit_link.png
diff --git a/lib/editor/atto/pix/e/insert_edit_link.svg b/pix/e/insert_edit_link.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_link.svg
rename to pix/e/insert_edit_link.svg
diff --git a/lib/editor/atto/pix/e/insert_edit_video.png b/pix/e/insert_edit_video.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_video.png
rename to pix/e/insert_edit_video.png
diff --git a/lib/editor/atto/pix/e/insert_edit_video.svg b/pix/e/insert_edit_video.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_edit_video.svg
rename to pix/e/insert_edit_video.svg
diff --git a/lib/editor/atto/pix/e/insert_file.png b/pix/e/insert_file.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_file.png
rename to pix/e/insert_file.png
diff --git a/lib/editor/atto/pix/e/insert_file.svg b/pix/e/insert_file.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_file.svg
rename to pix/e/insert_file.svg
diff --git a/lib/editor/atto/pix/e/insert_horizontal_ruler.png b/pix/e/insert_horizontal_ruler.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_horizontal_ruler.png
rename to pix/e/insert_horizontal_ruler.png
diff --git a/lib/editor/atto/pix/e/insert_horizontal_ruler.svg b/pix/e/insert_horizontal_ruler.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_horizontal_ruler.svg
rename to pix/e/insert_horizontal_ruler.svg
diff --git a/lib/editor/atto/pix/e/insert_nonbreaking_space.png b/pix/e/insert_nonbreaking_space.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_nonbreaking_space.png
rename to pix/e/insert_nonbreaking_space.png
diff --git a/lib/editor/atto/pix/e/insert_nonbreaking_space.svg b/pix/e/insert_nonbreaking_space.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_nonbreaking_space.svg
rename to pix/e/insert_nonbreaking_space.svg
diff --git a/lib/editor/atto/pix/e/insert_row_after.png b/pix/e/insert_row_after.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_row_after.png
rename to pix/e/insert_row_after.png
diff --git a/lib/editor/atto/pix/e/insert_row_after.svg b/pix/e/insert_row_after.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_row_after.svg
rename to pix/e/insert_row_after.svg
diff --git a/lib/editor/atto/pix/e/insert_row_before.png b/pix/e/insert_row_before.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_row_before.png
rename to pix/e/insert_row_before.png
diff --git a/lib/editor/atto/pix/e/insert_row_before.svg b/pix/e/insert_row_before.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_row_before.svg
rename to pix/e/insert_row_before.svg
diff --git a/lib/editor/atto/pix/e/insert_time.png b/pix/e/insert_time.png
similarity index 100%
rename from lib/editor/atto/pix/e/insert_time.png
rename to pix/e/insert_time.png
diff --git a/lib/editor/atto/pix/e/insert_time.svg b/pix/e/insert_time.svg
similarity index 100%
rename from lib/editor/atto/pix/e/insert_time.svg
rename to pix/e/insert_time.svg
diff --git a/lib/editor/atto/pix/e/italic.png b/pix/e/italic.png
similarity index 100%
rename from lib/editor/atto/pix/e/italic.png
rename to pix/e/italic.png
diff --git a/lib/editor/atto/pix/e/italic.svg b/pix/e/italic.svg
similarity index 100%
rename from lib/editor/atto/pix/e/italic.svg
rename to pix/e/italic.svg
diff --git a/lib/editor/atto/pix/e/justify.png b/pix/e/justify.png
similarity index 100%
rename from lib/editor/atto/pix/e/justify.png
rename to pix/e/justify.png
diff --git a/lib/editor/atto/pix/e/justify.svg b/pix/e/justify.svg
similarity index 100%
rename from lib/editor/atto/pix/e/justify.svg
rename to pix/e/justify.svg
diff --git a/lib/editor/atto/pix/e/layers.png b/pix/e/layers.png
similarity index 100%
rename from lib/editor/atto/pix/e/layers.png
rename to pix/e/layers.png
diff --git a/lib/editor/atto/pix/e/layers.svg b/pix/e/layers.svg
similarity index 100%
rename from lib/editor/atto/pix/e/layers.svg
rename to pix/e/layers.svg
diff --git a/lib/editor/atto/pix/e/layers_over.png b/pix/e/layers_over.png
similarity index 100%
rename from lib/editor/atto/pix/e/layers_over.png
rename to pix/e/layers_over.png
diff --git a/lib/editor/atto/pix/e/layers_over.svg b/pix/e/layers_over.svg
similarity index 100%
rename from lib/editor/atto/pix/e/layers_over.svg
rename to pix/e/layers_over.svg
diff --git a/lib/editor/atto/pix/e/layers_under.png b/pix/e/layers_under.png
similarity index 100%
rename from lib/editor/atto/pix/e/layers_under.png
rename to pix/e/layers_under.png
diff --git a/lib/editor/atto/pix/e/layers_under.svg b/pix/e/layers_under.svg
similarity index 100%
rename from lib/editor/atto/pix/e/layers_under.svg
rename to pix/e/layers_under.svg
diff --git a/lib/editor/atto/pix/e/left_to_right.png b/pix/e/left_to_right.png
similarity index 100%
rename from lib/editor/atto/pix/e/left_to_right.png
rename to pix/e/left_to_right.png
diff --git a/lib/editor/atto/pix/e/left_to_right.svg b/pix/e/left_to_right.svg
similarity index 100%
rename from lib/editor/atto/pix/e/left_to_right.svg
rename to pix/e/left_to_right.svg
diff --git a/lib/editor/atto/pix/e/manage_files.png b/pix/e/manage_files.png
similarity index 100%
rename from lib/editor/atto/pix/e/manage_files.png
rename to pix/e/manage_files.png
diff --git a/lib/editor/atto/pix/e/manage_files.svg b/pix/e/manage_files.svg
similarity index 100%
rename from lib/editor/atto/pix/e/manage_files.svg
rename to pix/e/manage_files.svg
diff --git a/lib/editor/atto/pix/e/merge_cells.png b/pix/e/merge_cells.png
similarity index 100%
rename from lib/editor/atto/pix/e/merge_cells.png
rename to pix/e/merge_cells.png
diff --git a/lib/editor/atto/pix/e/merge_cells.svg b/pix/e/merge_cells.svg
similarity index 100%
rename from lib/editor/atto/pix/e/merge_cells.svg
rename to pix/e/merge_cells.svg
diff --git a/lib/editor/atto/pix/e/new_document.png b/pix/e/new_document.png
similarity index 100%
rename from lib/editor/atto/pix/e/new_document.png
rename to pix/e/new_document.png
diff --git a/lib/editor/atto/pix/e/new_document.svg b/pix/e/new_document.svg
similarity index 100%
rename from lib/editor/atto/pix/e/new_document.svg
rename to pix/e/new_document.svg
diff --git a/lib/editor/atto/pix/e/numbered_list.png b/pix/e/numbered_list.png
similarity index 100%
rename from lib/editor/atto/pix/e/numbered_list.png
rename to pix/e/numbered_list.png
diff --git a/lib/editor/atto/pix/e/numbered_list.svg b/pix/e/numbered_list.svg
similarity index 100%
rename from lib/editor/atto/pix/e/numbered_list.svg
rename to pix/e/numbered_list.svg
diff --git a/lib/editor/atto/pix/e/page_break.png b/pix/e/page_break.png
similarity index 100%
rename from lib/editor/atto/pix/e/page_break.png
rename to pix/e/page_break.png
diff --git a/lib/editor/atto/pix/e/page_break.svg b/pix/e/page_break.svg
similarity index 100%
rename from lib/editor/atto/pix/e/page_break.svg
rename to pix/e/page_break.svg
diff --git a/lib/editor/atto/pix/e/paste.png b/pix/e/paste.png
similarity index 100%
rename from lib/editor/atto/pix/e/paste.png
rename to pix/e/paste.png
diff --git a/lib/editor/atto/pix/e/paste.svg b/pix/e/paste.svg
similarity index 100%
rename from lib/editor/atto/pix/e/paste.svg
rename to pix/e/paste.svg
diff --git a/lib/editor/atto/pix/e/paste_text.png b/pix/e/paste_text.png
similarity index 100%
rename from lib/editor/atto/pix/e/paste_text.png
rename to pix/e/paste_text.png
diff --git a/lib/editor/atto/pix/e/paste_text.svg b/pix/e/paste_text.svg
similarity index 100%
rename from lib/editor/atto/pix/e/paste_text.svg
rename to pix/e/paste_text.svg
diff --git a/lib/editor/atto/pix/e/paste_word.png b/pix/e/paste_word.png
similarity index 100%
rename from lib/editor/atto/pix/e/paste_word.png
rename to pix/e/paste_word.png
diff --git a/lib/editor/atto/pix/e/paste_word.svg b/pix/e/paste_word.svg
similarity index 100%
rename from lib/editor/atto/pix/e/paste_word.svg
rename to pix/e/paste_word.svg
diff --git a/lib/editor/atto/pix/e/prevent_autolink.png b/pix/e/prevent_autolink.png
similarity index 100%
rename from lib/editor/atto/pix/e/prevent_autolink.png
rename to pix/e/prevent_autolink.png
diff --git a/lib/editor/atto/pix/e/prevent_autolink.svg b/pix/e/prevent_autolink.svg
similarity index 100%
rename from lib/editor/atto/pix/e/prevent_autolink.svg
rename to pix/e/prevent_autolink.svg
diff --git a/lib/editor/atto/pix/e/preview.png b/pix/e/preview.png
similarity index 100%
rename from lib/editor/atto/pix/e/preview.png
rename to pix/e/preview.png
diff --git a/lib/editor/atto/pix/e/preview.svg b/pix/e/preview.svg
similarity index 100%
rename from lib/editor/atto/pix/e/preview.svg
rename to pix/e/preview.svg
diff --git a/lib/editor/atto/pix/e/print.png b/pix/e/print.png
similarity index 100%
rename from lib/editor/atto/pix/e/print.png
rename to pix/e/print.png
diff --git a/lib/editor/atto/pix/e/print.svg b/pix/e/print.svg
similarity index 100%
rename from lib/editor/atto/pix/e/print.svg
rename to pix/e/print.svg
diff --git a/lib/editor/atto/pix/e/question.png b/pix/e/question.png
similarity index 100%
rename from lib/editor/atto/pix/e/question.png
rename to pix/e/question.png
diff --git a/lib/editor/atto/pix/e/question.svg b/pix/e/question.svg
similarity index 100%
rename from lib/editor/atto/pix/e/question.svg
rename to pix/e/question.svg
diff --git a/lib/editor/atto/pix/e/redo.png b/pix/e/redo.png
similarity index 100%
rename from lib/editor/atto/pix/e/redo.png
rename to pix/e/redo.png
diff --git a/lib/editor/atto/pix/e/redo.svg b/pix/e/redo.svg
similarity index 100%
rename from lib/editor/atto/pix/e/redo.svg
rename to pix/e/redo.svg
diff --git a/lib/editor/atto/pix/e/remove_link.png b/pix/e/remove_link.png
similarity index 100%
rename from lib/editor/atto/pix/e/remove_link.png
rename to pix/e/remove_link.png
diff --git a/lib/editor/atto/pix/e/remove_link.svg b/pix/e/remove_link.svg
similarity index 100%
rename from lib/editor/atto/pix/e/remove_link.svg
rename to pix/e/remove_link.svg
diff --git a/lib/editor/atto/pix/e/resize.png b/pix/e/resize.png
similarity index 100%
rename from lib/editor/atto/pix/e/resize.png
rename to pix/e/resize.png
diff --git a/lib/editor/atto/pix/e/resize.svg b/pix/e/resize.svg
similarity index 100%
rename from lib/editor/atto/pix/e/resize.svg
rename to pix/e/resize.svg
diff --git a/lib/editor/atto/pix/e/restore_draft.png b/pix/e/restore_draft.png
similarity index 100%
rename from lib/editor/atto/pix/e/restore_draft.png
rename to pix/e/restore_draft.png
diff --git a/lib/editor/atto/pix/e/restore_draft.svg b/pix/e/restore_draft.svg
similarity index 100%
rename from lib/editor/atto/pix/e/restore_draft.svg
rename to pix/e/restore_draft.svg
diff --git a/lib/editor/atto/pix/e/restore_last_draft.png b/pix/e/restore_last_draft.png
similarity index 100%
rename from lib/editor/atto/pix/e/restore_last_draft.png
rename to pix/e/restore_last_draft.png
diff --git a/lib/editor/atto/pix/e/restore_last_draft.svg b/pix/e/restore_last_draft.svg
similarity index 100%
rename from lib/editor/atto/pix/e/restore_last_draft.svg
rename to pix/e/restore_last_draft.svg
diff --git a/lib/editor/atto/pix/e/right_to_left.png b/pix/e/right_to_left.png
similarity index 100%
rename from lib/editor/atto/pix/e/right_to_left.png
rename to pix/e/right_to_left.png
diff --git a/lib/editor/atto/pix/e/right_to_left.svg b/pix/e/right_to_left.svg
similarity index 100%
rename from lib/editor/atto/pix/e/right_to_left.svg
rename to pix/e/right_to_left.svg
diff --git a/lib/editor/atto/pix/e/row_props.png b/pix/e/row_props.png
similarity index 100%
rename from lib/editor/atto/pix/e/row_props.png
rename to pix/e/row_props.png
diff --git a/lib/editor/atto/pix/e/row_props.svg b/pix/e/row_props.svg
similarity index 100%
rename from lib/editor/atto/pix/e/row_props.svg
rename to pix/e/row_props.svg
diff --git a/lib/editor/atto/pix/e/save.png b/pix/e/save.png
similarity index 100%
rename from lib/editor/atto/pix/e/save.png
rename to pix/e/save.png
diff --git a/lib/editor/atto/pix/e/save.svg b/pix/e/save.svg
similarity index 100%
rename from lib/editor/atto/pix/e/save.svg
rename to pix/e/save.svg
diff --git a/lib/editor/atto/pix/e/search.png b/pix/e/search.png
similarity index 100%
rename from lib/editor/atto/pix/e/search.png
rename to pix/e/search.png
diff --git a/lib/editor/atto/pix/e/search.svg b/pix/e/search.svg
similarity index 100%
rename from lib/editor/atto/pix/e/search.svg
rename to pix/e/search.svg
diff --git a/lib/editor/atto/pix/e/select_all.png b/pix/e/select_all.png
similarity index 100%
rename from lib/editor/atto/pix/e/select_all.png
rename to pix/e/select_all.png
diff --git a/lib/editor/atto/pix/e/select_all.svg b/pix/e/select_all.svg
similarity index 100%
rename from lib/editor/atto/pix/e/select_all.svg
rename to pix/e/select_all.svg
diff --git a/lib/editor/atto/pix/e/show_invisible_characters.png b/pix/e/show_invisible_characters.png
similarity index 100%
rename from lib/editor/atto/pix/e/show_invisible_characters.png
rename to pix/e/show_invisible_characters.png
diff --git a/lib/editor/atto/pix/e/show_invisible_characters.svg b/pix/e/show_invisible_characters.svg
similarity index 100%
rename from lib/editor/atto/pix/e/show_invisible_characters.svg
rename to pix/e/show_invisible_characters.svg
diff --git a/lib/editor/atto/pix/e/source_code.png b/pix/e/source_code.png
similarity index 100%
rename from lib/editor/atto/pix/e/source_code.png
rename to pix/e/source_code.png
diff --git a/lib/editor/atto/pix/e/source_code.svg b/pix/e/source_code.svg
similarity index 100%
rename from lib/editor/atto/pix/e/source_code.svg
rename to pix/e/source_code.svg
diff --git a/lib/editor/atto/pix/e/special_character.png b/pix/e/special_character.png
similarity index 100%
rename from lib/editor/atto/pix/e/special_character.png
rename to pix/e/special_character.png
diff --git a/lib/editor/atto/pix/e/special_character.svg b/pix/e/special_character.svg
similarity index 100%
rename from lib/editor/atto/pix/e/special_character.svg
rename to pix/e/special_character.svg
diff --git a/lib/editor/atto/pix/e/spellcheck.png b/pix/e/spellcheck.png
similarity index 100%
rename from lib/editor/atto/pix/e/spellcheck.png
rename to pix/e/spellcheck.png
diff --git a/lib/editor/atto/pix/e/spellcheck.svg b/pix/e/spellcheck.svg
similarity index 100%
rename from lib/editor/atto/pix/e/spellcheck.svg
rename to pix/e/spellcheck.svg
diff --git a/lib/editor/atto/pix/e/split_cells.png b/pix/e/split_cells.png
similarity index 100%
rename from lib/editor/atto/pix/e/split_cells.png
rename to pix/e/split_cells.png
diff --git a/lib/editor/atto/pix/e/split_cells.svg b/pix/e/split_cells.svg
similarity index 100%
rename from lib/editor/atto/pix/e/split_cells.svg
rename to pix/e/split_cells.svg
diff --git a/lib/editor/atto/pix/e/strikethrough.png b/pix/e/strikethrough.png
similarity index 100%
rename from lib/editor/atto/pix/e/strikethrough.png
rename to pix/e/strikethrough.png
diff --git a/lib/editor/atto/pix/e/strikethrough.svg b/pix/e/strikethrough.svg
similarity index 100%
rename from lib/editor/atto/pix/e/strikethrough.svg
rename to pix/e/strikethrough.svg
diff --git a/lib/editor/atto/pix/e/styleprops.png b/pix/e/styleprops.png
similarity index 100%
rename from lib/editor/atto/pix/e/styleprops.png
rename to pix/e/styleprops.png
diff --git a/lib/editor/atto/pix/e/styleprops.svg b/pix/e/styleprops.svg
similarity index 100%
rename from lib/editor/atto/pix/e/styleprops.svg
rename to pix/e/styleprops.svg
diff --git a/lib/editor/atto/pix/e/subscript.png b/pix/e/subscript.png
similarity index 100%
rename from lib/editor/atto/pix/e/subscript.png
rename to pix/e/subscript.png
diff --git a/lib/editor/atto/pix/e/subscript.svg b/pix/e/subscript.svg
similarity index 100%
rename from lib/editor/atto/pix/e/subscript.svg
rename to pix/e/subscript.svg
diff --git a/lib/editor/atto/pix/e/superscript.png b/pix/e/superscript.png
similarity index 100%
rename from lib/editor/atto/pix/e/superscript.png
rename to pix/e/superscript.png
diff --git a/lib/editor/atto/pix/e/superscript.svg b/pix/e/superscript.svg
similarity index 100%
rename from lib/editor/atto/pix/e/superscript.svg
rename to pix/e/superscript.svg
diff --git a/lib/editor/atto/pix/e/table.png b/pix/e/table.png
similarity index 100%
rename from lib/editor/atto/pix/e/table.png
rename to pix/e/table.png
diff --git a/lib/editor/atto/pix/e/table.svg b/pix/e/table.svg
similarity index 100%
rename from lib/editor/atto/pix/e/table.svg
rename to pix/e/table.svg
diff --git a/lib/editor/atto/pix/e/table_props.png b/pix/e/table_props.png
similarity index 100%
rename from lib/editor/atto/pix/e/table_props.png
rename to pix/e/table_props.png
diff --git a/lib/editor/atto/pix/e/table_props.svg b/pix/e/table_props.svg
similarity index 100%
rename from lib/editor/atto/pix/e/table_props.svg
rename to pix/e/table_props.svg
diff --git a/lib/editor/atto/pix/e/template.png b/pix/e/template.png
similarity index 100%
rename from lib/editor/atto/pix/e/template.png
rename to pix/e/template.png
diff --git a/lib/editor/atto/pix/e/template.svg b/pix/e/template.svg
similarity index 100%
rename from lib/editor/atto/pix/e/template.svg
rename to pix/e/template.svg
diff --git a/lib/editor/atto/pix/e/text_color.png b/pix/e/text_color.png
similarity index 100%
rename from lib/editor/atto/pix/e/text_color.png
rename to pix/e/text_color.png
diff --git a/lib/editor/atto/pix/e/text_color.svg b/pix/e/text_color.svg
similarity index 100%
rename from lib/editor/atto/pix/e/text_color.svg
rename to pix/e/text_color.svg
diff --git a/lib/editor/atto/pix/e/text_highlight.png b/pix/e/text_highlight.png
similarity index 100%
rename from lib/editor/atto/pix/e/text_highlight.png
rename to pix/e/text_highlight.png
diff --git a/lib/editor/atto/pix/e/text_highlight.svg b/pix/e/text_highlight.svg
similarity index 100%
rename from lib/editor/atto/pix/e/text_highlight.svg
rename to pix/e/text_highlight.svg
diff --git a/lib/editor/atto/pix/e/tick.png b/pix/e/tick.png
similarity index 100%
rename from lib/editor/atto/pix/e/tick.png
rename to pix/e/tick.png
diff --git a/lib/editor/atto/pix/e/tick.svg b/pix/e/tick.svg
similarity index 100%
rename from lib/editor/atto/pix/e/tick.svg
rename to pix/e/tick.svg
diff --git a/lib/editor/atto/pix/e/toggle_blockquote.png b/pix/e/toggle_blockquote.png
similarity index 100%
rename from lib/editor/atto/pix/e/toggle_blockquote.png
rename to pix/e/toggle_blockquote.png
diff --git a/lib/editor/atto/pix/e/toggle_blockquote.svg b/pix/e/toggle_blockquote.svg
similarity index 100%
rename from lib/editor/atto/pix/e/toggle_blockquote.svg
rename to pix/e/toggle_blockquote.svg
diff --git a/lib/editor/atto/pix/e/underline.png b/pix/e/underline.png
similarity index 100%
rename from lib/editor/atto/pix/e/underline.png
rename to pix/e/underline.png
diff --git a/lib/editor/atto/pix/e/underline.svg b/pix/e/underline.svg
similarity index 100%
rename from lib/editor/atto/pix/e/underline.svg
rename to pix/e/underline.svg
diff --git a/lib/editor/atto/pix/e/undo.png b/pix/e/undo.png
similarity index 100%
rename from lib/editor/atto/pix/e/undo.png
rename to pix/e/undo.png
diff --git a/lib/editor/atto/pix/e/undo.svg b/pix/e/undo.svg
similarity index 100%
rename from lib/editor/atto/pix/e/undo.svg
rename to pix/e/undo.svg
diff --git a/lib/editor/atto/pix/e/visual_aid.png b/pix/e/visual_aid.png
similarity index 100%
rename from lib/editor/atto/pix/e/visual_aid.png
rename to pix/e/visual_aid.png
diff --git a/lib/editor/atto/pix/e/visual_aid.svg b/pix/e/visual_aid.svg
similarity index 100%
rename from lib/editor/atto/pix/e/visual_aid.svg
rename to pix/e/visual_aid.svg
diff --git a/lib/editor/atto/pix/e/visual_blocks.png b/pix/e/visual_blocks.png
similarity index 100%
rename from lib/editor/atto/pix/e/visual_blocks.png
rename to pix/e/visual_blocks.png
diff --git a/lib/editor/atto/pix/e/visual_blocks.svg b/pix/e/visual_blocks.svg
similarity index 100%
rename from lib/editor/atto/pix/e/visual_blocks.svg
rename to pix/e/visual_blocks.svg
From f1e6953b36ef265032c57d1b31b4573b54c0f318 Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Tue, 28 Jan 2014 14:12:32 +0800
Subject: [PATCH 03/63] MDL-43842 Atto: Remove licence and README.txt
---
lib/editor/atto/LICENSE | 674 -------------------------------------
lib/editor/atto/README.txt | 18 -
2 files changed, 692 deletions(-)
delete mode 100644 lib/editor/atto/LICENSE
delete mode 100644 lib/editor/atto/README.txt
diff --git a/lib/editor/atto/LICENSE b/lib/editor/atto/LICENSE
deleted file mode 100644
index 94a9ed024d3..00000000000
--- a/lib/editor/atto/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program 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.
-
- This program 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 this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/lib/editor/atto/README.txt b/lib/editor/atto/README.txt
deleted file mode 100644
index 1fc2cffe5dd..00000000000
--- a/lib/editor/atto/README.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Small, fast, accessible, mobile friendly text editor for Moodle
-
-QUICK INSTALL
-=============
-Put this entire directory at:
-
-PATHTOMOODLE/lib/editors/atto
-
-Visit your site notifications page to install the new plugins.
-
-Enable the new editor on the page:
-
-Site administration / â–º Plugins / â–º Text editors / â–º Manage editors
-
-To make it the default, change the order so this editor is the first in the list.
-
-In Moodle 2.6 users can also choose this editor from their profile preferences.
-
From 55c0403c6c62d788e1e3e06c4a25b57bcdb196b0 Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Tue, 28 Jan 2014 20:01:05 +0800
Subject: [PATCH 04/63] MDL-43853 Atto: Performance. Turbocharge the loader.
Rewrite the atto plugin loader so that:
A) We have a real settings.php page and can edit the toolbar.
B) All plugins are loaded at the same time (not serially)
C) All plugins share the YUI sandbox (more performance boost).
I had to change the loading API for this - but there are not non-core uses yet,
this is the best time to break it.
---
lib/editor/atto/lang/en/editor_atto.php | 4 ++
lib/editor/atto/lib.php | 63 ++++++++++++++-----
lib/editor/atto/plugins/bold/lib.php | 48 --------------
.../moodle-atto_bold-button-debug.js | 3 +-
.../moodle-atto_bold-button-min.js | 2 +-
.../moodle-atto_bold-button.js | 3 +-
.../plugins/bold/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/clear/lib.php | 48 --------------
.../moodle-atto_clear-button-debug.js | 3 +-
.../moodle-atto_clear-button-min.js | 2 +-
.../moodle-atto_clear-button.js | 3 +-
.../plugins/clear/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/html/lib.php | 48 --------------
.../moodle-atto_html-button-debug.js | 3 +-
.../moodle-atto_html-button-min.js | 2 +-
.../moodle-atto_html-button.js | 3 +-
.../plugins/html/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/image/lib.php | 20 +-----
.../moodle-atto_image-button-debug.js | 3 +-
.../moodle-atto_image-button-min.js | 2 +-
.../moodle-atto_image-button.js | 3 +-
.../plugins/image/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/indent/lib.php | 48 --------------
.../moodle-atto_indent-button-debug.js | 3 +-
.../moodle-atto_indent-button-min.js | 2 +-
.../moodle-atto_indent-button.js | 3 +-
.../indent/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/italic/lib.php | 48 --------------
.../moodle-atto_italic-button-debug.js | 3 +-
.../moodle-atto_italic-button-min.js | 2 +-
.../moodle-atto_italic-button.js | 3 +-
.../italic/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/link/lib.php | 18 +-----
.../moodle-atto_link-button-debug.js | 3 +-
.../moodle-atto_link-button-min.js | 2 +-
.../moodle-atto_link-button.js | 3 +-
.../plugins/link/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/media/lib.php | 21 +------
.../moodle-atto_media-button-debug.js | 3 +-
.../moodle-atto_media-button-min.js | 2 +-
.../moodle-atto_media-button.js | 3 +-
.../plugins/media/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/orderedlist/lib.php | 48 --------------
.../moodle-atto_orderedlist-button-debug.js | 3 +-
.../moodle-atto_orderedlist-button-min.js | 2 +-
.../moodle-atto_orderedlist-button.js | 3 +-
.../orderedlist/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/outdent/lib.php | 46 --------------
.../moodle-atto_outdent-button-debug.js | 3 +-
.../moodle-atto_outdent-button-min.js | 2 +-
.../moodle-atto_outdent-button.js | 3 +-
.../outdent/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/strike/lib.php | 48 --------------
.../moodle-atto_strike-button-debug.js | 3 +-
.../moodle-atto_strike-button-min.js | 2 +-
.../moodle-atto_strike-button.js | 3 +-
.../strike/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/table/lib.php | 21 +------
.../moodle-atto_table-button-debug.js | 4 +-
.../moodle-atto_table-button-min.js | 4 +-
.../moodle-atto_table-button.js | 4 +-
.../plugins/table/yui/src/button/js/button.js | 4 +-
lib/editor/atto/plugins/title/lib.php | 21 +------
.../moodle-atto_title-button-debug.js | 4 +-
.../moodle-atto_title-button-min.js | 2 +-
.../moodle-atto_title-button.js | 4 +-
.../plugins/title/yui/src/button/js/button.js | 4 +-
lib/editor/atto/plugins/underline/lib.php | 48 --------------
.../moodle-atto_underline-button-debug.js | 3 +-
.../moodle-atto_underline-button-min.js | 2 +-
.../moodle-atto_underline-button.js | 3 +-
.../underline/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/unlink/lib.php | 46 --------------
.../moodle-atto_unlink-button-debug.js | 3 +-
.../moodle-atto_unlink-button-min.js | 2 +-
.../moodle-atto_unlink-button.js | 3 +-
.../unlink/yui/src/button/js/button.js | 3 +-
lib/editor/atto/plugins/unorderedlist/lib.php | 46 --------------
.../moodle-atto_unorderedlist-button-debug.js | 3 +-
.../moodle-atto_unorderedlist-button-min.js | 2 +-
.../moodle-atto_unorderedlist-button.js | 3 +-
.../unorderedlist/yui/src/button/js/button.js | 3 +-
lib/editor/atto/settings.php | 53 ++++++++++++++++
.../moodle-editor_atto-editor-debug.js | 36 ++++++-----
.../moodle-editor_atto-editor-min.js | 4 +-
.../moodle-editor_atto-editor.js | 36 ++++++-----
.../atto/yui/src/editor/js/controlmenu.js | 3 +-
lib/editor/atto/yui/src/editor/js/editor.js | 33 ++++++----
88 files changed, 300 insertions(+), 739 deletions(-)
delete mode 100644 lib/editor/atto/plugins/bold/lib.php
delete mode 100644 lib/editor/atto/plugins/clear/lib.php
delete mode 100644 lib/editor/atto/plugins/html/lib.php
delete mode 100644 lib/editor/atto/plugins/indent/lib.php
delete mode 100644 lib/editor/atto/plugins/italic/lib.php
delete mode 100644 lib/editor/atto/plugins/orderedlist/lib.php
delete mode 100644 lib/editor/atto/plugins/outdent/lib.php
delete mode 100644 lib/editor/atto/plugins/strike/lib.php
delete mode 100644 lib/editor/atto/plugins/underline/lib.php
delete mode 100644 lib/editor/atto/plugins/unlink/lib.php
delete mode 100644 lib/editor/atto/plugins/unorderedlist/lib.php
create mode 100644 lib/editor/atto/settings.php
diff --git a/lib/editor/atto/lang/en/editor_atto.php b/lib/editor/atto/lang/en/editor_atto.php
index 3ade27d86ca..bba431f8cfe 100644
--- a/lib/editor/atto/lang/en/editor_atto.php
+++ b/lib/editor/atto/lang/en/editor_atto.php
@@ -23,3 +23,7 @@
*/
$string['pluginname'] = 'Atto HTML editor';
+$string['subplugintype_atto'] = 'Atto plugin';
+$string['settings'] = 'Atto toolbar settings';
+$string['toolbarconfig'] = 'Toolbar config';
+$string['toolbarconfig_desc'] = 'The list of plugins and the order they are displayed can be configured here. The configuration consists of groups (one per line) followed by the ordered list of plugins for that group. The group is separated from the plugins with an equals sign and the plugins are separated with commas. The group must be one of the following: style, paragraph, links, insert, other.';
diff --git a/lib/editor/atto/lib.php b/lib/editor/atto/lib.php
index f38e5cc584c..aaca577fb28 100644
--- a/lib/editor/atto/lib.php
+++ b/lib/editor/atto/lib.php
@@ -67,7 +67,7 @@ class atto_texteditor extends texteditor {
}
/**
- * Use this editor for give element.
+ * Use this editor for given element.
*
* @param string $elementid
* @param array $options
@@ -75,21 +75,49 @@ class atto_texteditor extends texteditor {
*/
public function use_editor($elementid, array $options=null, $fpoptions=null) {
global $PAGE;
- $PAGE->requires->yui_module('moodle-editor_atto-editor',
+
+ $configstr = get_config('editor_atto', 'toolbar');
+
+ $grouplines = explode("\n", $configstr);
+
+ $groups = array();
+
+ foreach ($grouplines as $groupline) {
+ $line = explode('=', $groupline);
+ if (count($line) > 1) {
+ $group = trim(array_shift($line));
+ $plugins = array_map('trim', explode(',', array_shift($line)));
+ $groups[$group] = $plugins;
+ }
+ }
+
+ $modules = array('moodle-editor_atto-editor');
+
+ $jsplugins = array();
+ foreach ($groups as $group => $plugins) {
+ $groupplugins = array();
+ foreach ($plugins as $plugin) {
+ $jsplugin = array();
+ $jsplugin['name'] = $plugin;
+ $jsplugin['params'] = array();
+ $modules[] = 'moodle-atto_' . $plugin . '-button';
+
+ component_callback('atto_' . $plugin, 'strings_for_js');
+ $extra = component_callback('atto_' . $plugin, 'params_for_js');
+
+ if ($extra) {
+ $jsplugin['params'] = $extra;
+ }
+ // We always need the plugin name.
+ $PAGE->requires->string_for_js('pluginname', 'atto_' . $plugin);
+ $groupplugins[] = $jsplugin;
+ }
+ $jsplugins[] = array('group'=>$group, 'plugins'=>$groupplugins);
+ }
+
+ $PAGE->requires->yui_module($modules,
'M.editor_atto.init',
- array($this->get_init_params($elementid, $options, $fpoptions)));
-
- $plugins = core_component::get_plugin_list('atto');
-
- foreach ($plugins as $name => $fulldir) {
- $PAGE->requires->string_for_js('pluginname', 'atto_' . $name);
- $plugins[$name] = component_callback('atto_' . $name, 'sort_order', array($elementid));
- }
-
- asort($plugins);
- foreach ($plugins as $name => $sort) {
- component_callback('atto_' . $name, 'init_editor', array($elementid));
- }
+ array($this->get_init_params($elementid, $options, $fpoptions, $jsplugins)));
}
@@ -100,7 +128,7 @@ class atto_texteditor extends texteditor {
* @param array $options
* @param array $fpoptions
*/
- protected function get_init_params($elementid, array $options=null, array $fpoptions=null) {
+ protected function get_init_params($elementid, array $options = null, array $fpoptions = null, $plugins = null) {
global $PAGE;
$directionality = get_string('thisdirection', 'langconfig');
@@ -114,7 +142,8 @@ class atto_texteditor extends texteditor {
'content_css' => $contentcss,
'language' => $lang,
'directionality' => $directionality,
- 'filepickeroptions' => array()
+ 'filepickeroptions' => array(),
+ 'plugins' => $plugins
);
if ($fpoptions) {
$params['filepickeroptions'] = $fpoptions;
diff --git a/lib/editor/atto/plugins/bold/lib.php b/lib/editor/atto/plugins/bold/lib.php
deleted file mode 100644
index 99acc45bd6d..00000000000
--- a/lib/editor/atto/plugins/bold/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_bold
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_bold_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/bold', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_bold-button',
- 'M.atto_bold.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_bold_sort_order() {
- return 0;
-}
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
index 5e00ad397ff..5319f52284e 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
@@ -34,7 +34,8 @@ M.atto_bold = M.atto_bold || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/bold', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
index b83c7a8f3ea..bb9a5f786f1 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_bold-button",function(e,t){M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"bold",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_bold-button",function(e,t){M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/bold","core");M.editor_atto.add_toolbar_button(e.elementid,"bold",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
index 5e00ad397ff..5319f52284e 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
@@ -34,7 +34,8 @@ M.atto_bold = M.atto_bold || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/bold', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/bold/yui/src/button/js/button.js b/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
index f48b4ec66da..5a29c73f621 100644
--- a/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_bold = M.atto_bold || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/bold', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/clear/lib.php b/lib/editor/atto/plugins/clear/lib.php
deleted file mode 100644
index 7a5196967e6..00000000000
--- a/lib/editor/atto/plugins/clear/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_clear
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_clear_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/clear_formatting', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_clear-button',
- 'M.atto_clear.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'clear')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_clear_sort_order() {
- return 10;
-}
diff --git a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-debug.js b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-debug.js
index 5a4187ca828..1f30fd06b46 100644
--- a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-debug.js
+++ b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-debug.js
@@ -34,7 +34,8 @@ M.atto_clear = M.atto_clear || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/clear_formatting', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'clear', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-min.js b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-min.js
index a8c661cf363..7fdec51985d 100644
--- a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-min.js
+++ b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_clear-button",function(e,t){M.atto_clear=M.atto_clear||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("removeFormat",!1),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"clear",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_clear-button",function(e,t){M.atto_clear=M.atto_clear||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("removeFormat",!1),M.editor_atto.text_updated(t)},n=M.util.image_url("e/clear_formatting","core");M.editor_atto.add_toolbar_button(e.elementid,"clear",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button.js b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button.js
index 5a4187ca828..1f30fd06b46 100644
--- a/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button.js
+++ b/lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button.js
@@ -34,7 +34,8 @@ M.atto_clear = M.atto_clear || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/clear_formatting', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'clear', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/clear/yui/src/button/js/button.js b/lib/editor/atto/plugins/clear/yui/src/button/js/button.js
index 5630e0af88c..d40078a4127 100644
--- a/lib/editor/atto/plugins/clear/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/clear/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_clear = M.atto_clear || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/clear_formatting', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'clear', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/html/lib.php b/lib/editor/atto/plugins/html/lib.php
deleted file mode 100644
index 5c60123c950..00000000000
--- a/lib/editor/atto/plugins/html/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_html
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_html_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/source_code', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_html-button',
- 'M.atto_html.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'html')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return array groupname followed by the absolute position within the toolbar
- */
-function atto_html_sort_order() {
- return 15;
-}
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
index 56e74812689..6855fe58c65 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
@@ -53,7 +53,8 @@ M.atto_html = M.atto_html || {
M.atto_html.ishtml = !M.atto_html.ishtml;
};
- M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/source_code', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'html', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
index db7828bc120..40aaeac859a 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_html-button",function(e,t){M.atto_html=M.atto_html||{ishtml:!1,init:function(t){var n=function(t,n){t.preventDefault();var r=e.one("#"+n),i=e.one("#"+n+"editable");M.atto_html.ishtml?(M.editor_atto.enable_all_widgets(n),i.setHTML(""),i.append(r.get("value")),r.hide(),i.show(),i.focus()):(M.editor_atto.disable_all_widgets(n),M.editor_atto.enable_widget(n,"html"),M.editor_atto.text_updated(n),i.hide(),r.show(),r.focus()),M.atto_html.ishtml=!M.atto_html.ishtml};M.editor_atto.add_toolbar_button(t.elementid,"html",t.icon,t.group,n)}}},"@VERSION@",{requires:["node","event-valuechange"]});
+YUI.add("moodle-atto_html-button",function(e,t){M.atto_html=M.atto_html||{ishtml:!1,init:function(t){var n=function(t,n){t.preventDefault();var r=e.one("#"+n),i=e.one("#"+n+"editable");M.atto_html.ishtml?(M.editor_atto.enable_all_widgets(n),i.setHTML(""),i.append(r.get("value")),r.hide(),i.show(),i.focus()):(M.editor_atto.disable_all_widgets(n),M.editor_atto.enable_widget(n,"html"),M.editor_atto.text_updated(n),i.hide(),r.show(),r.focus()),M.atto_html.ishtml=!M.atto_html.ishtml},r=M.util.image_url("e/source_code","core");M.editor_atto.add_toolbar_button(t.elementid,"html",r,t.group,n)}}},"@VERSION@",{requires:["node","event-valuechange"]});
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
index 56e74812689..6855fe58c65 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
@@ -53,7 +53,8 @@ M.atto_html = M.atto_html || {
M.atto_html.ishtml = !M.atto_html.ishtml;
};
- M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/source_code', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'html', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/html/yui/src/button/js/button.js b/lib/editor/atto/plugins/html/yui/src/button/js/button.js
index d739dcaff5e..d624268797e 100644
--- a/lib/editor/atto/plugins/html/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/html/yui/src/button/js/button.js
@@ -51,6 +51,7 @@ M.atto_html = M.atto_html || {
M.atto_html.ishtml = !M.atto_html.ishtml;
};
- M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/source_code', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'html', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/image/lib.php b/lib/editor/atto/plugins/image/lib.php
index 908cbfbbbbb..4624e57b92f 100644
--- a/lib/editor/atto/plugins/image/lib.php
+++ b/lib/editor/atto/plugins/image/lib.php
@@ -25,13 +25,10 @@
defined('MOODLE_INTERNAL') || die();
/**
- * Initialise this plugin
- * @param string $elementid
+ * Initialise the strings required for js
*/
-function atto_image_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/insert_edit_image', 'core');
+function atto_image_strings_for_js() {
+ global $PAGE;
$PAGE->requires->strings_for_js(array('createimage',
'enterurl',
@@ -43,16 +40,5 @@ function atto_image_init_editor($elementid) {
'presentationoraltrequired',
'width',
'height'), 'atto_image');
- $PAGE->requires->yui_module('moodle-atto_image-button',
- 'M.atto_image.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'file')));
-
}
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_image_sort_order() {
- return 13;
-}
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
index 58d3ee3b51b..92c12f49a11 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
@@ -91,7 +91,8 @@ M.atto_image = M.atto_image || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_image', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser, this);
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
index 8532ee5fe98..dc2ba5e65ae 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_image-button",function(e,t){M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(t){var n=function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var r;M.atto_image.dialogue?r=M.atto_image.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),r.set("bodyContent",M.atto_image.get_form_content(n)),r.set("headerContent",M.util.get_string("createimage","atto_image")),r.render(),r.centerDialogue(),M.atto_image.dialogue=r;var i=M.editor_atto.get_selection_text(),s=0,o=[];for(s=0;s0){var a=e.one(o[0]),f=a.getAttribute("width"),l=a.getAttribute("height");f>0&&e.one("#atto_image_widthentry").set("value",f),l>0&&e.one("#atto_image_heightentry").set("value",l),e.one("#atto_image_preview").set("src",a.get("src")),e.one("#atto_image_preview").setStyle("display","inline"),e.one("#atto_image_altentry").set("value",a.get("alt")),e.one("#atto_image_urlentry").set("value",a.get("src"));var c=a.get("role");c==="presentation"&&e.one("#atto_image_presentation").set("checked","checked")}r.show()}};M.editor_atto.add_toolbar_button(t.elementid,"image",t.icon,t.group,n,this)},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_image_urlentry");n.set("value",t.url);var r=new Image;r.onload=function(){e.one("#atto_image_widthentry").set("value",this.width),e.one("#atto_image_heightentry").set("value",this.height),e.one("#atto_image_preview").set("src",this.src),e.one("#atto_image_preview").setStyle("display","inline")},r.src=t.url}},url_changed:function(){var t=e.one("#atto_image_urlentry");if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#atto_image_widthentry"),t.get("value")===""&&t.set("value",this.width),t=e.one("#atto_image_heightentry"),t.get("value")===""&&t.set("value",this.height),t=e.one("#atto_image_preview"),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var r=t.currentTarget.ancestor(".atto_form").one("#atto_image_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry");var s=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_widthentry");var o=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_heightentry");var u=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation");var a=r.get("checked"),f;t.preventDefault();if(s===""&&!a){f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","block"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!0),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!0);return}f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","none"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!1),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide();if(i!==""){M.editor_atto.set_selection(M.atto_image.selection);var l='",document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(l):document.execCommand("insertHTML",!1,l),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_image"));return n.one("#atto_image_urlentry").on("blur",M.atto_image.url_changed,this),n.one("#atto_image_urlentrysubmit").on("click",M.atto_image.set_image,this,t),n.one("#openimagebrowser").on("click",M.atto_image.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_image-button",function(e,t){M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(t){var n=function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var r;M.atto_image.dialogue?r=M.atto_image.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),r.set("bodyContent",M.atto_image.get_form_content(n)),r.set("headerContent",M.util.get_string("createimage","atto_image")),r.render(),r.centerDialogue(),M.atto_image.dialogue=r;var i=M.editor_atto.get_selection_text(),s=0,o=[];for(s=0;s0){var a=e.one(o[0]),f=a.getAttribute("width"),l=a.getAttribute("height");f>0&&e.one("#atto_image_widthentry").set("value",f),l>0&&e.one("#atto_image_heightentry").set("value",l),e.one("#atto_image_preview").set("src",a.get("src")),e.one("#atto_image_preview").setStyle("display","inline"),e.one("#atto_image_altentry").set("value",a.get("alt")),e.one("#atto_image_urlentry").set("value",a.get("src"));var c=a.get("role");c==="presentation"&&e.one("#atto_image_presentation").set("checked","checked")}r.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(t.elementid,"image",r,t.group,n,this)},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_image_urlentry");n.set("value",t.url);var r=new Image;r.onload=function(){e.one("#atto_image_widthentry").set("value",this.width),e.one("#atto_image_heightentry").set("value",this.height),e.one("#atto_image_preview").set("src",this.src),e.one("#atto_image_preview").setStyle("display","inline")},r.src=t.url}},url_changed:function(){var t=e.one("#atto_image_urlentry");if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#atto_image_widthentry"),t.get("value")===""&&t.set("value",this.width),t=e.one("#atto_image_heightentry"),t.get("value")===""&&t.set("value",this.height),t=e.one("#atto_image_preview"),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var r=t.currentTarget.ancestor(".atto_form").one("#atto_image_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry");var s=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_widthentry");var o=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_heightentry");var u=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation");var a=r.get("checked"),f;t.preventDefault();if(s===""&&!a){f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","block"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!0),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!0);return}f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","none"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!1),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide();if(i!==""){M.editor_atto.set_selection(M.atto_image.selection);var l='",document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(l):document.execCommand("insertHTML",!1,l),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_image"));return n.one("#atto_image_urlentry").on("blur",M.atto_image.url_changed,this),n.one("#atto_image_urlentrysubmit").on("click",M.atto_image.set_image,this,t),n.one("#openimagebrowser").on("click",M.atto_image.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
index 58d3ee3b51b..92c12f49a11 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
@@ -91,7 +91,8 @@ M.atto_image = M.atto_image || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_image', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser, this);
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
index ea9fa214958..83840d65b8b 100644
--- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
@@ -89,7 +89,8 @@ M.atto_image = M.atto_image || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_image', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser, this);
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/indent/lib.php b/lib/editor/atto/plugins/indent/lib.php
deleted file mode 100644
index 308c1620977..00000000000
--- a/lib/editor/atto/plugins/indent/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_indent
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_indent_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/increase_indent', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_indent-button',
- 'M.atto_indent.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_indent_sort_order() {
- return 5;
-}
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
index f952bb08a68..10955eeb8b9 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
@@ -34,7 +34,8 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
index cd35231daca..444d5065999 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"indent",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/increase_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"indent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
index f952bb08a68..10955eeb8b9 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
@@ -34,7 +34,8 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
index 79b73a541da..70a16dfd922 100644
--- a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/italic/lib.php b/lib/editor/atto/plugins/italic/lib.php
deleted file mode 100644
index 8b01b4faec1..00000000000
--- a/lib/editor/atto/plugins/italic/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_italic
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_italic_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/italic', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_italic-button',
- 'M.atto_italic.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_italic_sort_order() {
- return 1;
-}
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
index 1cab99f2fc0..184c8058fd4 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
@@ -34,7 +34,8 @@ M.atto_italic = M.atto_italic || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/italic', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
index 545816717d3..dac855eb9f3 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_italic-button",function(e,t){M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"italic",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_italic-button",function(e,t){M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
index 1cab99f2fc0..184c8058fd4 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
@@ -34,7 +34,8 @@ M.atto_italic = M.atto_italic || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/italic', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
index 349ad9426da..a4000ff1b25 100644
--- a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_italic = M.atto_italic || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'italic', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/italic', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/link/lib.php b/lib/editor/atto/plugins/link/lib.php
index d5eee36d4d5..f9b63a0d25b 100644
--- a/lib/editor/atto/plugins/link/lib.php
+++ b/lib/editor/atto/plugins/link/lib.php
@@ -28,10 +28,8 @@ defined('MOODLE_INTERNAL') || die();
* Initialise this plugin
* @param string $elementid
*/
-function atto_link_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/insert_edit_link', 'core');
+function atto_link_strings_for_js() {
+ global $PAGE;
$PAGE->requires->strings_for_js(array('createlink',
'enterurl',
@@ -39,17 +37,5 @@ function atto_link_init_editor($elementid) {
'openinnewwindow',
'accessibilityhint'),
'atto_link');
-
- $PAGE->requires->yui_module('moodle-atto_link-button',
- 'M.atto_link.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'link')));
-
}
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_link_sort_order() {
- return 11;
-}
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
index e51f120a7d1..ce89f7bd1c1 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
@@ -85,7 +85,8 @@ M.atto_link = M.atto_link || {
* @param {Object} params
*/
init : function(params) {
- M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_link', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser, this);
},
/**
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
index 0d59b38f03f..cb6dbd6e6fa 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_link-button",function(e,t){M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){M.editor_atto.add_toolbar_button(e.elementid,"link",e.icon,e.group,this.display_chooser,this)},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_link"));return n.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),n.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_link-button",function(e,t){M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){var t=M.util.image_url("e/insert_edit_link","core");M.editor_atto.add_toolbar_button(e.elementid,"link",t,e.group,this.display_chooser,this)},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_link"));return n.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),n.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
index e51f120a7d1..ce89f7bd1c1 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
@@ -85,7 +85,8 @@ M.atto_link = M.atto_link || {
* @param {Object} params
*/
init : function(params) {
- M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_link', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser, this);
},
/**
diff --git a/lib/editor/atto/plugins/link/yui/src/button/js/button.js b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
index 7e5023533c5..85a96c05b85 100644
--- a/lib/editor/atto/plugins/link/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
@@ -83,7 +83,8 @@ M.atto_link = M.atto_link || {
* @param {Object} params
*/
init : function(params) {
- M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_link', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser, this);
},
/**
diff --git a/lib/editor/atto/plugins/media/lib.php b/lib/editor/atto/plugins/media/lib.php
index c2841bdb268..93831a9a18f 100644
--- a/lib/editor/atto/plugins/media/lib.php
+++ b/lib/editor/atto/plugins/media/lib.php
@@ -23,13 +23,10 @@
*/
/**
- * Initialise this plugin
- * @param string $elementid
+ * Initialise the js strings required for this plugin
*/
-function atto_media_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/insert_edit_video', 'core');
+function atto_media_strings_for_js() {
+ global $PAGE;
$PAGE->requires->strings_for_js(array('createmedia',
'enterurl',
@@ -37,16 +34,4 @@ function atto_media_init_editor($elementid) {
'browserepositories',
'accessibilityhint'),
'atto_media');
- $PAGE->requires->yui_module('moodle-atto_media-button',
- 'M.atto_media.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'file')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_media_sort_order() {
- return 14;
}
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js
index 5d67cc1f325..e5587493c06 100644
--- a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js
@@ -54,7 +54,8 @@ M.atto_media = M.atto_media || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_video', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', iconurl, params.group, display_chooser, this);
},
open_browser : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js
index f00a5b835f8..9571db3c8c0 100644
--- a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_media-button",function(e,t){M.atto_media=M.atto_media||{dialogue:null,selection:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_media.selection=M.editor_atto.get_selection();if(M.atto_media.selection!==!1){var n;M.atto_media.dialogue?n=M.atto_media.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_media.get_form_content(t)),n.set("headerContent",M.util.get_string("createmedia","atto_media")),n.centerDialogue(),n.show(),M.atto_media.dialogue=n}};M.editor_atto.add_toolbar_button(e.elementid,"media",e.icon,e.group,t,this)},open_browser:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"media",M.atto_media.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_media_urlentry");n.set("value",t.url),n=e.one("#atto_media_nameentry"),n.set("value",t.file)}},set_media:function(t,n){t.preventDefault(),M.atto_media.dialogue.hide();var r=t.currentTarget.ancestor(".atto_form").one("#atto_media_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_media_nameentry");var s=r.get("value");if(i!==""&&s!==""){M.editor_atto.set_selection(M.atto_media.selection);var o=''+s+"";document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(o):document.execCommand("insertHTML",!1,o),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_media"));return n.one("#atto_media_urlentrysubmit").on("click",M.atto_media.set_media,this,t),n.one("#openmediabrowser").on("click",M.atto_media.open_browser),n}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_media-button",function(e,t){M.atto_media=M.atto_media||{dialogue:null,selection:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_media.selection=M.editor_atto.get_selection();if(M.atto_media.selection!==!1){var n;M.atto_media.dialogue?n=M.atto_media.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_media.get_form_content(t)),n.set("headerContent",M.util.get_string("createmedia","atto_media")),n.centerDialogue(),n.show(),M.atto_media.dialogue=n}},n=M.util.image_url("e/insert_edit_video","core");M.editor_atto.add_toolbar_button(e.elementid,"media",n,e.group,t,this)},open_browser:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"media",M.atto_media.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_media_urlentry");n.set("value",t.url),n=e.one("#atto_media_nameentry"),n.set("value",t.file)}},set_media:function(t,n){t.preventDefault(),M.atto_media.dialogue.hide();var r=t.currentTarget.ancestor(".atto_form").one("#atto_media_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_media_nameentry");var s=r.get("value");if(i!==""&&s!==""){M.editor_atto.set_selection(M.atto_media.selection);var o=''+s+"";document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(o):document.execCommand("insertHTML",!1,o),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_media"));return n.one("#atto_media_urlentrysubmit").on("click",M.atto_media.set_media,this,t),n.one("#openmediabrowser").on("click",M.atto_media.open_browser),n}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js
index 5d67cc1f325..e5587493c06 100644
--- a/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js
+++ b/lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js
@@ -54,7 +54,8 @@ M.atto_media = M.atto_media || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_video', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', iconurl, params.group, display_chooser, this);
},
open_browser : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/media/yui/src/button/js/button.js b/lib/editor/atto/plugins/media/yui/src/button/js/button.js
index 29033965ba8..4b5c3bc3e36 100644
--- a/lib/editor/atto/plugins/media/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/media/yui/src/button/js/button.js
@@ -52,7 +52,8 @@ M.atto_media = M.atto_media || {
}
};
- M.editor_atto.add_toolbar_button(params.elementid, 'media', params.icon, params.group, display_chooser, this);
+ var iconurl = M.util.image_url('e/insert_edit_video', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'media', iconurl, params.group, display_chooser, this);
},
open_browser : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/orderedlist/lib.php b/lib/editor/atto/plugins/orderedlist/lib.php
deleted file mode 100644
index 57f658d9518..00000000000
--- a/lib/editor/atto/plugins/orderedlist/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_orderedlist
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_orderedlist_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/numbered_list', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_orderedlist-button',
- 'M.atto_orderedlist.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return array groupname followed by the absolute position within the toolbar
- */
-function atto_orderedlist_sort_order() {
- return 6;
-}
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
index 4b19e1e757f..4901f1d3b64 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
@@ -34,7 +34,8 @@ M.atto_orderedlist = M.atto_orderedlist || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/numbered_list', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
index c1863bd3a6e..245f5d96e74 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_orderedlist-button",function(e,t){M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_orderedlist-button",function(e,t){M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/numbered_list","core");M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
index 4b19e1e757f..4901f1d3b64 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
@@ -34,7 +34,8 @@ M.atto_orderedlist = M.atto_orderedlist || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/numbered_list', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
index eb3f83423a0..397b25b2364 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_orderedlist = M.atto_orderedlist || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/numbered_list', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/outdent/lib.php b/lib/editor/atto/plugins/outdent/lib.php
deleted file mode 100644
index 2e4a9337da7..00000000000
--- a/lib/editor/atto/plugins/outdent/lib.php
+++ /dev/null
@@ -1,46 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_outdent
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_outdent_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/decrease_indent', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_outdent-button',
- 'M.atto_outdent.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_outdent_sort_order() {
- return 4;
-}
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
index 4224011908d..a958ade9fde 100644
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
@@ -34,7 +34,8 @@ M.atto_outdent = M.atto_outdent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
index f86a8cda522..a8fccc477ae 100644
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_outdent-button",function(e,t){M.atto_outdent=M.atto_outdent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("outdent",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"outdent",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_outdent-button",function(e,t){M.atto_outdent=M.atto_outdent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("outdent",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/decrease_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"outdent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
index 4224011908d..a958ade9fde 100644
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
+++ b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
@@ -34,7 +34,8 @@ M.atto_outdent = M.atto_outdent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js b/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
index d3d67afddf2..8899f012ffd 100644
--- a/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_outdent = M.atto_outdent || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/strike/lib.php b/lib/editor/atto/plugins/strike/lib.php
deleted file mode 100644
index a0d4eb03e55..00000000000
--- a/lib/editor/atto/plugins/strike/lib.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_strike
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Initialise this plugin
- * @param string $elementid
- */
-function atto_strike_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/strikethrough', 'core');
-
- $PAGE->requires->yui_module('moodle-atto_strike-button',
- 'M.atto_strike.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')));
-
-}
-
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int absolute position within the toolbar
- */
-function atto_strike_sort_order() {
- return 3;
-}
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
index d2a09428ff4..4b753823204 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
@@ -34,7 +34,8 @@ M.atto_strike = M.atto_strike || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/strikethrough', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
index 067c1db0312..39f48b6e56e 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_strike-button",function(e,t){M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)};M.editor_atto.add_toolbar_button(e.elementid,"strike",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_strike-button",function(e,t){M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/strikethrough","core");M.editor_atto.add_toolbar_button(e.elementid,"strike",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
index d2a09428ff4..4b753823204 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
@@ -34,7 +34,8 @@ M.atto_strike = M.atto_strike || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/strikethrough', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
index 8e8505e2c57..c7e95459724 100644
--- a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
@@ -32,6 +32,7 @@ M.atto_strike = M.atto_strike || {
M.editor_atto.text_updated(elementid);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'strike', params.icon, params.group, click);
+ var iconurl = M.util.image_url('e/strikethrough', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/table/lib.php b/lib/editor/atto/plugins/table/lib.php
index 452afb6810a..cca2098ae16 100644
--- a/lib/editor/atto/plugins/table/lib.php
+++ b/lib/editor/atto/plugins/table/lib.php
@@ -25,13 +25,10 @@
defined('MOODLE_INTERNAL') || die();
/**
- * Initialise this plugin
- * @param string $elementid
+ * Initialise the js strings required for this module.
*/
-function atto_table_init_editor($elementid) {
- global $PAGE, $OUTPUT;
-
- $icon = array('e/table', 'core');
+function atto_table_strings_for_js() {
+ global $PAGE;
$PAGE->requires->strings_for_js(array('createtable',
'accessibilityhint',
@@ -52,17 +49,5 @@ function atto_table_init_editor($elementid) {
'deleterow',
'deletecolumn'),
'atto_table');
-
- $PAGE->requires->yui_module('moodle-atto_table-button',
- 'M.atto_table.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')));
-
}
-/**
- * Return the order this plugin should be displayed in the toolbar
- * @return int the absolute position within the toolbar
- */
-function atto_table_sort_order() {
- return 11;
-}
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
index d627f07167e..696b542a980 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
@@ -477,8 +477,8 @@ M.atto_table = M.atto_table || {
M.atto_table.menunode = anchor;
}
- M.editor_atto.add_toolbar_button(params.elementid, 'table', params.icon, params.group, this.display_chooser, this);
- //Y.one('#' + params.elementid + 'editable').on('valuechange', this.insert_table_controls, this, params.elementid);
+ var iconurl = M.util.image_url('e/table', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'table', iconurl, params.group, this.display_chooser, this);
var contenteditable = Y.one('#' + params.elementid + 'editable');
contenteditable.delegate('click', this.show_menu, 'td > .atto_control, th > .atto_control', this, params.elementid);
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
index fe9e7b4f755..ada69f31624 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_table-button",function(e,t){M.atto_table=M.atto_table||{dialogue:null,selection:null,menunode:null,controlmenu:null,lasttarget:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_table.selection=M.editor_atto.get_selection();if(M.atto_table.selection!==!1&&!M.atto_table.selection.collapsed){var n;M.atto_table.dialogue?n=M.atto_table.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_table.get_form_content(t)),n.set("headerContent",M.util.get_string("createtable","atto_table")),n.show(),M.atto_table.dialogue=n}},show_menu:function(t,n){var r=!1;t.preventDefault();if(this.controlmenu===null){r=!0;var i="
"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u}};var n="Controlmenu",r;r=function(e){e.draggable=!1,e.center=!1,e.width="auto",e.lightbox=!1,e.footerContent="",r.superclass.constructor.apply(this,[e])},e.extend(r,M.core.dialogue,{initializer:function(t){var n,i,s;r.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("editor_atto_controlmenu"),n=this.bodyNode,i=e.Node.create(""),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i),n.on("clickoutside",function(e){this.get("visible")&&(e.target
+.ancestor(".atto_control")||(e.preventDefault(),this.hide()))},this)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
index c2ff1a7b1bf..8b37d90da7e 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -531,6 +531,8 @@ M.editor_atto = M.editor_atto || {
var textarea = M.editor_atto.get_textarea_node(elementid),
cleancontent = this.get_clean_html(elementid);
textarea.set('value', cleancontent);
+ // Trigger the onchange callback on the textarea, essentially to notify moodle-core-formchangechecker.
+ textarea.simulate('change');
// Trigger handlers for this action.
var i = 0;
if (elementid in M.editor_atto.textupdatedhandlers) {
@@ -992,4 +994,4 @@ Y.Node.addMethod("cleanHTML", cleanHTML);
Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML");
-}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event", "moodle-core-notification"]});
+}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event", "event-simulate", "moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/src/editor/js/editor.js b/lib/editor/atto/yui/src/editor/js/editor.js
index dc7569fded6..d3e04bc043d 100644
--- a/lib/editor/atto/yui/src/editor/js/editor.js
+++ b/lib/editor/atto/yui/src/editor/js/editor.js
@@ -529,6 +529,8 @@ M.editor_atto = M.editor_atto || {
var textarea = M.editor_atto.get_textarea_node(elementid),
cleancontent = this.get_clean_html(elementid);
textarea.set('value', cleancontent);
+ // Trigger the onchange callback on the textarea, essentially to notify moodle-core-formchangechecker.
+ textarea.simulate('change');
// Trigger handlers for this action.
var i = 0;
if (elementid in M.editor_atto.textupdatedhandlers) {
diff --git a/lib/editor/atto/yui/src/editor/meta/editor.json b/lib/editor/atto/yui/src/editor/meta/editor.json
index e50017fd4d7..1cb269517d2 100644
--- a/lib/editor/atto/yui/src/editor/meta/editor.json
+++ b/lib/editor/atto/yui/src/editor/meta/editor.json
@@ -6,6 +6,7 @@
"overlay",
"escape",
"event",
+ "event-simulate",
"moodle-core-notification"
]
}
From 26f8822d5c40a1cd4049fc09cb6da46e6cd55f7d Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Tue, 4 Feb 2014 12:51:21 +0800
Subject: [PATCH 21/63] MDL-43867 Atto: Accessibility improvements.
1/ Set the aria-labelledby attribute on the contenteditable div (find the label from
original textarea)
2/ Store/restore the selection for the contenteditable div when it is focused. This allows
you to select some text, then go to the toolbar and click a button, and the selection
will be restored before the button effect is applied.
3/ Add an accessibility helper plugin.
From testing in all screenreaders, I found that all of their support for contenteditable is not great.
They treat it like a textbox - which means you can type and edit text, but it tells you nothing about
the styles, links or images in the editor. So I added a button to the toolbar, that is only accessible
when navigating via keyboard, that opens an accesssibility helper dialogue. The dialogue shows the list
of current styles, a global list of all links, and a global list of all images. Choosing an image or link
from here, will focus on the editable region, and select the link/image.
4/ Add an accessibility checker plugin to Atto.
Checks for images with no alt, images and links with filenames as alternate text/link text, and contrast ratios
less than WCAG 2.0 AA.
---
.../lang/en/atto_accessibilitychecker.php | 29 ++
.../atto/plugins/accessibilitychecker/lib.php | 40 +++
.../plugins/accessibilitychecker/version.php | 29 ++
...-atto_accessibilitychecker-button-debug.js | 245 +++++++++++++++
...le-atto_accessibilitychecker-button-min.js | 1 +
...moodle-atto_accessibilitychecker-button.js | 240 ++++++++++++++
.../yui/src/button/build.json | 10 +
.../yui/src/button/js/button.js | 240 ++++++++++++++
.../yui/src/button/meta/button.json | 5 +
.../lang/en/atto_accessibilityhelper.php | 33 ++
.../atto/plugins/accessibilityhelper/lib.php | 44 +++
.../plugins/accessibilityhelper/version.php | 29 ++
...e-atto_accessibilityhelper-button-debug.js | 294 ++++++++++++++++++
...dle-atto_accessibilityhelper-button-min.js | 1 +
.../moodle-atto_accessibilityhelper-button.js | 294 ++++++++++++++++++
.../yui/src/button/build.json | 10 +
.../yui/src/button/js/button.js | 289 +++++++++++++++++
.../yui/src/button/meta/editor.json | 5 +
.../moodle-atto_table-button-debug.js | 5 +-
.../moodle-atto_table-button-min.js | 4 +-
.../moodle-atto_table-button.js | 5 +-
.../plugins/table/yui/src/button/js/button.js | 5 +-
lib/editor/atto/settings.php | 2 +-
.../moodle-editor_atto-editor-debug.js | 73 ++++-
.../moodle-editor_atto-editor-min.js | 6 +-
.../moodle-editor_atto-editor.js | 73 ++++-
lib/editor/atto/yui/src/editor/js/editor.js | 73 ++++-
27 files changed, 2069 insertions(+), 15 deletions(-)
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/lang/en/atto_accessibilitychecker.php
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/lib.php
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/version.php
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button-debug.js
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button-min.js
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button.js
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/src/button/build.json
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/src/button/js/button.js
create mode 100644 lib/editor/atto/plugins/accessibilitychecker/yui/src/button/meta/button.json
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/lang/en/atto_accessibilityhelper.php
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/lib.php
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/version.php
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-debug.js
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-min.js
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button.js
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/src/button/build.json
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/src/button/js/button.js
create mode 100644 lib/editor/atto/plugins/accessibilityhelper/yui/src/button/meta/editor.json
diff --git a/lib/editor/atto/plugins/accessibilitychecker/lang/en/atto_accessibilitychecker.php b/lib/editor/atto/plugins/accessibilitychecker/lang/en/atto_accessibilitychecker.php
new file mode 100644
index 00000000000..72e2dbb1bcf
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/lang/en/atto_accessibilitychecker.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Strings for component 'atto_accessibilitychecker', language 'en'.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'Accessibility checker';
+$string['nowarnings'] = 'Congratulations, no accessibility problems found!';
+$string['report'] = 'Accessibility report:';
+$string['imagesmissingalt'] = 'Images require alternative text. To fix this warning, add an alt attribute to your img tags. An empty alt attribute may be used, but only when the image is purely decorative and carries no information.';
+$string['needsmorecontrast'] = 'The colors of the foreground and background text do not have enough contrast. To fix this warning, change either foreground or background color of the text so that it is easier to read.';
diff --git a/lib/editor/atto/plugins/accessibilitychecker/lib.php b/lib/editor/atto/plugins/accessibilitychecker/lib.php
new file mode 100644
index 00000000000..dd829f49aed
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/lib.php
@@ -0,0 +1,40 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise this plugin
+ * @param string $elementid
+ */
+function atto_accessibilitychecker_strings_for_js() {
+ global $PAGE;
+
+ $PAGE->requires->strings_for_js(array('nowarnings',
+ 'report',
+ 'imagesmissingalt',
+ 'needsmorecontrast'),
+ 'atto_accessibilitychecker');
+}
+
diff --git a/lib/editor/atto/plugins/accessibilitychecker/version.php b/lib/editor/atto/plugins/accessibilitychecker/version.php
new file mode 100644
index 00000000000..822c6c56036
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_accessibilitychecker'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button-debug.js b/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button-debug.js
new file mode 100644
index 00000000000..7db1ffad061
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button-debug.js
@@ -0,0 +1,245 @@
+YUI.add('moodle-atto_accessibilitychecker-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 .
+
+/**
+ * Atto text editor accessibilitychecker plugin.
+ *
+ * This plugin adds some functions to do things that screen readers do not do well.
+ * Specifically, listing the active styles for the selected text,
+ * listing the images in the page, listing the links in the page.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
+ /**
+ * The window used to display the accessibility ui.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * Display the ui dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_ui : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ var dialogue;
+ if (!M.atto_accessibilitychecker.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true,
+ width: '800px'
+ });
+ dialogue.set('headerContent', M.util.get_string('pluginname', 'atto_accessibilitychecker'));
+ dialogue.render();
+ } else {
+ dialogue = M.atto_accessibilitychecker.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_accessibilitychecker.get_report(elementid));
+ dialogue.centerDialogue();
+
+ dialogue.show();
+ M.atto_accessibilitychecker.dialogue = dialogue;
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ var iconurl = M.util.image_url('e/visual_blocks', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'accessibilitychecker', iconurl, params.group, this.display_ui);
+ },
+
+ /**
+ * Generate the HTML that lists the found warnings.
+ *
+ * @method add_warnings
+ * @param Y.Node list - node to append the html to.
+ * @param String description - description of this failure.
+ * @param Y.Node[] nodes - list of failing nodes.
+ */
+ add_warnings : function(list, description, nodes) {
+ var warning, fails, i;
+
+ if (nodes.length > 0) {
+ warning = Y.Node.create('
' + description + '
');
+ fails = Y.Node.create('');
+ i = 0;
+ for (i = 0; i < nodes.length; i++) {
+ fails.append(Y.Node.create('
' + Y.Escape.html(nodes[i].get('outerHTML')) + '
'));
+ }
+
+ warning.append(fails);
+ list.append(warning);
+ }
+ },
+
+ /**
+ * Convert a css color to a luminance value.
+ *
+ * @method get_luminance_from_css_color
+ * @param {String} colortext
+ * @return {Integer}
+ */
+ get_luminance_from_css_color : function(colortext) {
+ var color;
+
+ if (colortext === 'transparent') {
+ colortext = '#ffffff';
+ }
+ color = Y.Color.toArray(Y.Color.toRGB(colortext));
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ var part1 = function(a) {
+ a = parseInt(a, 10) / 255.0;
+ if (a <= 0.03928) {
+ a = a/12.92;
+ } else {
+ a = Math.pow(((a + 0.055)/1.055), 2.4);
+ }
+ return a;
+ };
+
+ var r1 = part1(color[0]),
+ g1 = part1(color[1]),
+ b1 = part1(color[2]);
+
+ return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
+ },
+
+ /**
+ * List the accessibility warnings for the current editor
+ *
+ * @method list_warnings
+ * @param string elementid
+ * @return String
+ */
+ list_warnings : function(elementid) {
+
+ var list = Y.Node.create('');
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+
+ // Missing alternatives.
+ var missingalt = [], dodgycontrast = [];
+
+ // Images with no alt text or dodgy alt text.
+ var alt;
+ editable.all('img').each(function (img) {
+ alt = img.getAttribute('alt');
+ if (typeof alt === 'undefined' || alt === '') {
+ if (img.getAttribute('role') !== 'presentation') {
+ missingalt.push(img);
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+
+ // Contrast ratios.
+ var foreground, background, lum1, lum2, ratio;
+ editable.all('*').each(function (node) {
+ // Check for non-empty text.
+ if (Y.Lang.trim(node.get('text')) !== '') {
+ foreground = node.getComputedStyle('color');
+ background = node.getComputedStyle('backgroundColor');
+
+ lum1 = this.get_luminance_from_css_color(foreground);
+ lum2 = this.get_luminance_from_css_color(background);
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ if (lum1 > lum2) {
+ ratio = (lum1 + 0.05) / (lum2 + 0.05);
+ } else {
+ ratio = (lum2 + 0.05) / (lum1 + 0.05);
+ }
+ if (ratio <= 4.5) {
+ Y.log('Contrast ratio is too low: ' + ratio +
+ ' Colour 1: ' + foreground +
+ ' Colour 2: ' + background +
+ ' Luminance 1: ' + lum1 +
+ ' Luminance 2: ' + lum2);
+
+ // We only want the highest node with dodgy contrast reported.
+ var i = 0, found = false;
+ for (i = 0; i < dodgycontrast.length; i++) {
+ if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ // Do not add node - it already has a parent in the list.
+ found = true;
+ break;
+ } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ // Replace the existing node with this one because it is higher up the DOM.
+ dodgycontrast[i] = node;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dodgycontrast.push(node);
+ }
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+
+ if (!list.hasChildNodes()) {
+ list.append('
"),n},get_report:function(t){var n='',r=e.Node.create(n);return r.append(this.list_warnings(t)),r}}},"@VERSION@",{requires:["node","escape","color-base"]});
diff --git a/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button.js b/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button.js
new file mode 100644
index 00000000000..0ae360a1e5a
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/yui/build/moodle-atto_accessibilitychecker-button/moodle-atto_accessibilitychecker-button.js
@@ -0,0 +1,240 @@
+YUI.add('moodle-atto_accessibilitychecker-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 .
+
+/**
+ * Atto text editor accessibilitychecker plugin.
+ *
+ * This plugin adds some functions to do things that screen readers do not do well.
+ * Specifically, listing the active styles for the selected text,
+ * listing the images in the page, listing the links in the page.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
+ /**
+ * The window used to display the accessibility ui.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * Display the ui dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_ui : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ var dialogue;
+ if (!M.atto_accessibilitychecker.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true,
+ width: '800px'
+ });
+ dialogue.set('headerContent', M.util.get_string('pluginname', 'atto_accessibilitychecker'));
+ dialogue.render();
+ } else {
+ dialogue = M.atto_accessibilitychecker.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_accessibilitychecker.get_report(elementid));
+ dialogue.centerDialogue();
+
+ dialogue.show();
+ M.atto_accessibilitychecker.dialogue = dialogue;
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ var iconurl = M.util.image_url('e/visual_blocks', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'accessibilitychecker', iconurl, params.group, this.display_ui);
+ },
+
+ /**
+ * Generate the HTML that lists the found warnings.
+ *
+ * @method add_warnings
+ * @param Y.Node list - node to append the html to.
+ * @param String description - description of this failure.
+ * @param Y.Node[] nodes - list of failing nodes.
+ */
+ add_warnings : function(list, description, nodes) {
+ var warning, fails, i;
+
+ if (nodes.length > 0) {
+ warning = Y.Node.create('
' + description + '
');
+ fails = Y.Node.create('');
+ i = 0;
+ for (i = 0; i < nodes.length; i++) {
+ fails.append(Y.Node.create('
' + Y.Escape.html(nodes[i].get('outerHTML')) + '
'));
+ }
+
+ warning.append(fails);
+ list.append(warning);
+ }
+ },
+
+ /**
+ * Convert a css color to a luminance value.
+ *
+ * @method get_luminance_from_css_color
+ * @param {String} colortext
+ * @return {Integer}
+ */
+ get_luminance_from_css_color : function(colortext) {
+ var color;
+
+ if (colortext === 'transparent') {
+ colortext = '#ffffff';
+ }
+ color = Y.Color.toArray(Y.Color.toRGB(colortext));
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ var part1 = function(a) {
+ a = parseInt(a, 10) / 255.0;
+ if (a <= 0.03928) {
+ a = a/12.92;
+ } else {
+ a = Math.pow(((a + 0.055)/1.055), 2.4);
+ }
+ return a;
+ };
+
+ var r1 = part1(color[0]),
+ g1 = part1(color[1]),
+ b1 = part1(color[2]);
+
+ return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
+ },
+
+ /**
+ * List the accessibility warnings for the current editor
+ *
+ * @method list_warnings
+ * @param string elementid
+ * @return String
+ */
+ list_warnings : function(elementid) {
+
+ var list = Y.Node.create('');
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+
+ // Missing alternatives.
+ var missingalt = [], dodgycontrast = [];
+
+ // Images with no alt text or dodgy alt text.
+ var alt;
+ editable.all('img').each(function (img) {
+ alt = img.getAttribute('alt');
+ if (typeof alt === 'undefined' || alt === '') {
+ if (img.getAttribute('role') !== 'presentation') {
+ missingalt.push(img);
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+
+ // Contrast ratios.
+ var foreground, background, lum1, lum2, ratio;
+ editable.all('*').each(function (node) {
+ // Check for non-empty text.
+ if (Y.Lang.trim(node.get('text')) !== '') {
+ foreground = node.getComputedStyle('color');
+ background = node.getComputedStyle('backgroundColor');
+
+ lum1 = this.get_luminance_from_css_color(foreground);
+ lum2 = this.get_luminance_from_css_color(background);
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ if (lum1 > lum2) {
+ ratio = (lum1 + 0.05) / (lum2 + 0.05);
+ } else {
+ ratio = (lum2 + 0.05) / (lum1 + 0.05);
+ }
+ if (ratio <= 4.5) {
+
+ // We only want the highest node with dodgy contrast reported.
+ var i = 0, found = false;
+ for (i = 0; i < dodgycontrast.length; i++) {
+ if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ // Do not add node - it already has a parent in the list.
+ found = true;
+ break;
+ } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ // Replace the existing node with this one because it is higher up the DOM.
+ dodgycontrast[i] = node;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dodgycontrast.push(node);
+ }
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+
+ if (!list.hasChildNodes()) {
+ list.append('
');
+ }
+ // Append the list of current styles.
+ return list;
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_report
+ * @param string elementid
+ * @return string
+ */
+ get_report : function(elementid) {
+ // Current styles.
+ var html = '';
+
+ var content = Y.Node.create(html);
+
+ content.append(this.list_warnings(elementid));
+
+ return content;
+ }
+
+};
+
+
+}, '@VERSION@', {"requires": ["node", "escape", "color-base"]});
diff --git a/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/build.json b/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/build.json
new file mode 100644
index 00000000000..0278528268a
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_accessibilitychecker-button",
+ "builds": {
+ "moodle-atto_accessibilitychecker-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/js/button.js b/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/js/button.js
new file mode 100644
index 00000000000..2d3951cf654
--- /dev/null
+++ b/lib/editor/atto/plugins/accessibilitychecker/yui/src/button/js/button.js
@@ -0,0 +1,240 @@
+// 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 .
+
+/**
+ * Atto text editor accessibilitychecker plugin.
+ *
+ * This plugin adds some functions to do things that screen readers do not do well.
+ * Specifically, listing the active styles for the selected text,
+ * listing the images in the page, listing the links in the page.
+ *
+ * @package atto_accessibilitychecker
+ * @copyright 2014 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
+ /**
+ * The window used to display the accessibility ui.
+ *
+ * @property dialogue
+ * @type M.core.dialogue
+ * @default null
+ */
+ dialogue : null,
+
+ /**
+ * Display the ui dialogue.
+ *
+ * @method init
+ * @param Event e
+ * @param string elementid
+ */
+ display_ui : function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ var dialogue;
+ if (!M.atto_accessibilitychecker.dialogue) {
+ dialogue = new M.core.dialogue({
+ visible: false,
+ modal: true,
+ close: true,
+ draggable: true,
+ width: '800px'
+ });
+ dialogue.set('headerContent', M.util.get_string('pluginname', 'atto_accessibilitychecker'));
+ dialogue.render();
+ } else {
+ dialogue = M.atto_accessibilitychecker.dialogue;
+ }
+
+ dialogue.set('bodyContent', M.atto_accessibilitychecker.get_report(elementid));
+ dialogue.centerDialogue();
+
+ dialogue.show();
+ M.atto_accessibilitychecker.dialogue = dialogue;
+ },
+
+ /**
+ * Add this button to the form.
+ *
+ * @method init
+ * @param {Object} params
+ */
+ init : function(params) {
+ var iconurl = M.util.image_url('e/visual_blocks', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'accessibilitychecker', iconurl, params.group, this.display_ui);
+ },
+
+ /**
+ * Generate the HTML that lists the found warnings.
+ *
+ * @method add_warnings
+ * @param Y.Node list - node to append the html to.
+ * @param String description - description of this failure.
+ * @param Y.Node[] nodes - list of failing nodes.
+ */
+ add_warnings : function(list, description, nodes) {
+ var warning, fails, i;
+
+ if (nodes.length > 0) {
+ warning = Y.Node.create('
' + description + '
');
+ fails = Y.Node.create('');
+ i = 0;
+ for (i = 0; i < nodes.length; i++) {
+ fails.append(Y.Node.create('
' + Y.Escape.html(nodes[i].get('outerHTML')) + '
'));
+ }
+
+ warning.append(fails);
+ list.append(warning);
+ }
+ },
+
+ /**
+ * Convert a css color to a luminance value.
+ *
+ * @method get_luminance_from_css_color
+ * @param {String} colortext
+ * @return {Integer}
+ */
+ get_luminance_from_css_color : function(colortext) {
+ var color;
+
+ if (colortext === 'transparent') {
+ colortext = '#ffffff';
+ }
+ color = Y.Color.toArray(Y.Color.toRGB(colortext));
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ var part1 = function(a) {
+ a = parseInt(a, 10) / 255.0;
+ if (a <= 0.03928) {
+ a = a/12.92;
+ } else {
+ a = Math.pow(((a + 0.055)/1.055), 2.4);
+ }
+ return a;
+ };
+
+ var r1 = part1(color[0]),
+ g1 = part1(color[1]),
+ b1 = part1(color[2]);
+
+ return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
+ },
+
+ /**
+ * List the accessibility warnings for the current editor
+ *
+ * @method list_warnings
+ * @param string elementid
+ * @return String
+ */
+ list_warnings : function(elementid) {
+
+ var list = Y.Node.create('');
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+
+ // Missing alternatives.
+ var missingalt = [], dodgycontrast = [];
+
+ // Images with no alt text or dodgy alt text.
+ var alt;
+ editable.all('img').each(function (img) {
+ alt = img.getAttribute('alt');
+ if (typeof alt === 'undefined' || alt === '') {
+ if (img.getAttribute('role') !== 'presentation') {
+ missingalt.push(img);
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+
+ // Contrast ratios.
+ var foreground, background, lum1, lum2, ratio;
+ editable.all('*').each(function (node) {
+ // Check for non-empty text.
+ if (Y.Lang.trim(node.get('text')) !== '') {
+ foreground = node.getComputedStyle('color');
+ background = node.getComputedStyle('backgroundColor');
+
+ lum1 = this.get_luminance_from_css_color(foreground);
+ lum2 = this.get_luminance_from_css_color(background);
+
+ // Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
+ if (lum1 > lum2) {
+ ratio = (lum1 + 0.05) / (lum2 + 0.05);
+ } else {
+ ratio = (lum2 + 0.05) / (lum1 + 0.05);
+ }
+ if (ratio <= 4.5) {
+ Y.log('Contrast ratio is too low: ' + ratio +
+ ' Colour 1: ' + foreground +
+ ' Colour 2: ' + background +
+ ' Luminance 1: ' + lum1 +
+ ' Luminance 2: ' + lum2);
+
+ // We only want the highest node with dodgy contrast reported.
+ var i = 0, found = false;
+ for (i = 0; i < dodgycontrast.length; i++) {
+ if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ // Do not add node - it already has a parent in the list.
+ found = true;
+ break;
+ } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ // Replace the existing node with this one because it is higher up the DOM.
+ dodgycontrast[i] = node;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dodgycontrast.push(node);
+ }
+ }
+ }
+ }, this);
+
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+
+ if (!list.hasChildNodes()) {
+ list.append('
');
+ }
+ // Append the list of current styles.
+ return list;
+ },
+
+ /**
+ * List the styles for the current selection.
+ *
+ * @method list_styles
+ * @param string elementid
+ * @return String
+ */
+ list_styles : function(elementid) {
+
+ // Clear the status node.
+
+ var list = [];
+
+ var current = M.editor_atto.get_selection_parent_node();
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var tagname;
+
+ if (current) {
+ current = Y.one(current);
+ }
+ while (current && (current !== editable)) {
+ tagname = current.get('tagName');
+ if (typeof tagname !== 'undefined') {
+ list.push(Y.Escape.html(tagname));
+ }
+ current = current.ancestor();
+ }
+ if (list.length === 0) {
+ list.push(M.util.get_string('nostyles', 'atto_accessibilityhelper'));
+ }
+
+ list.reverse();
+ // Append the list of current styles.
+ return list.join(', ');
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_content
+ * @param string elementid
+ * @return string
+ */
+ get_content : function(elementid) {
+ // Current styles.
+ var html = '
');
+ }
+ // Append the list of current styles.
+ return list;
+ },
+
+ /**
+ * List the styles for the current selection.
+ *
+ * @method list_styles
+ * @param string elementid
+ * @return String
+ */
+ list_styles : function(elementid) {
+
+ // Clear the status node.
+
+ var list = [];
+
+ var current = M.editor_atto.get_selection_parent_node();
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var tagname;
+
+ if (current) {
+ current = Y.one(current);
+ }
+ while (current && (current !== editable)) {
+ tagname = current.get('tagName');
+ if (typeof tagname !== 'undefined') {
+ list.push(Y.Escape.html(tagname));
+ }
+ current = current.ancestor();
+ }
+ if (list.length === 0) {
+ list.push(M.util.get_string('nostyles', 'atto_accessibilityhelper'));
+ }
+
+ list.reverse();
+ // Append the list of current styles.
+ return list.join(', ');
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_content
+ * @param string elementid
+ * @return string
+ */
+ get_content : function(elementid) {
+ // Current styles.
+ var html = '
');
+ }
+ // Append the list of current styles.
+ return list;
+ },
+
+ /**
+ * List the styles for the current selection.
+ *
+ * @method list_styles
+ * @param string elementid
+ * @return String
+ */
+ list_styles : function(elementid) {
+
+ // Clear the status node.
+
+ var list = [];
+
+ var current = M.editor_atto.get_selection_parent_node();
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var tagname;
+
+ if (current) {
+ current = Y.one(current);
+ }
+ while (current && (current !== editable)) {
+ tagname = current.get('tagName');
+ if (typeof tagname !== 'undefined') {
+ list.push(Y.Escape.html(tagname));
+ }
+ current = current.ancestor();
+ }
+ if (list.length === 0) {
+ list.push(M.util.get_string('nostyles', 'atto_accessibilityhelper'));
+ }
+
+ list.reverse();
+ // Append the list of current styles.
+ return list.join(', ');
+ },
+
+ /**
+ * Return the HTML of the form to show in the dialogue.
+ *
+ * @method get_content
+ * @param string elementid
+ * @return string
+ */
+ get_content : function(elementid) {
+ // Current styles.
+ var html = '
which is not ideal but that is the easiest to put in place
+ // for now. When disabling the styleWithCSS, some browser will use
so we cannot
+ // rely on it for
s, and that would not work when indenting lists either....
+ // Handling it ourselves is even worse as it would require to get a parent and wrap
+ // a div with a margin around it. Considering that multiple
should end up in the
+ // same
, that table cells should not be wrapped, and that lists work differently too.
document.execCommand('indent', false, null);
+
+ // Some browsers add style attributes to the blockquote, let's get rid of them.
+ // It is really tricky to figure out what blockquote was just added, so removing
+ // the styles on all of them seems OK.
+ // Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
+ // IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
+ editable = M.editor_atto.get_editable_node(elementid);
+ editable.all('blockquote').removeAttribute('style');
+
// Clean the YUI ids from the HTML.
M.editor_atto.text_updated(elementid);
};
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
index 444d5065999..e4eea858b45 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/increase_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"indent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){var n;e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),n=M.editor_atto.get_editable_node(t),n.all("blockquote").removeAttribute("style"),M.editor_atto.text_updated(t)},n=M.util.image_url("e/increase_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"indent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
index 10955eeb8b9..a6f626fa3c1 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
@@ -18,18 +18,36 @@ YUI.add('moodle-atto_indent-button', function (Y, NAME) {
/**
* Atto text editor indent plugin.
*
- * @package editor-atto
+ * @package atto_indent
* @copyright 2013 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_indent = M.atto_indent || {
init : function(params) {
var click = function(e, elementid) {
+ var editable;
+
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
+
+ // This is adding a
which is not ideal but that is the easiest to put in place
+ // for now. When disabling the styleWithCSS, some browser will use
so we cannot
+ // rely on it for
s, and that would not work when indenting lists either....
+ // Handling it ourselves is even worse as it would require to get a parent and wrap
+ // a div with a margin around it. Considering that multiple
should end up in the
+ // same
, that table cells should not be wrapped, and that lists work differently too.
document.execCommand('indent', false, null);
+
+ // Some browsers add style attributes to the blockquote, let's get rid of them.
+ // It is really tricky to figure out what blockquote was just added, so removing
+ // the styles on all of them seems OK.
+ // Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
+ // IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
+ editable = M.editor_atto.get_editable_node(elementid);
+ editable.all('blockquote').removeAttribute('style');
+
// Clean the YUI ids from the HTML.
M.editor_atto.text_updated(elementid);
};
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
index 70a16dfd922..918ca690f86 100644
--- a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
@@ -16,18 +16,36 @@
/**
* Atto text editor indent plugin.
*
- * @package editor-atto
+ * @package atto_indent
* @copyright 2013 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_indent = M.atto_indent || {
init : function(params) {
var click = function(e, elementid) {
+ var editable;
+
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
+
+ // This is adding a
which is not ideal but that is the easiest to put in place
+ // for now. When disabling the styleWithCSS, some browser will use
so we cannot
+ // rely on it for
s, and that would not work when indenting lists either....
+ // Handling it ourselves is even worse as it would require to get a parent and wrap
+ // a div with a margin around it. Considering that multiple
should end up in the
+ // same
, that table cells should not be wrapped, and that lists work differently too.
document.execCommand('indent', false, null);
+
+ // Some browsers add style attributes to the blockquote, let's get rid of them.
+ // It is really tricky to figure out what blockquote was just added, so removing
+ // the styles on all of them seems OK.
+ // Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
+ // IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
+ editable = M.editor_atto.get_editable_node(elementid);
+ editable.all('blockquote').removeAttribute('style');
+
// Clean the YUI ids from the HTML.
M.editor_atto.text_updated(elementid);
};
From c7ef8e455eb67d0248de439988b7d82d23bd50cd Mon Sep 17 00:00:00 2001
From: Frederic Massart
Date: Wed, 12 Feb 2014 15:33:07 +0800
Subject: [PATCH 27/63] MDL-43870 atto_title: Remove quoted style
---
lib/editor/atto/plugins/title/lang/en/atto_title.php | 1 -
lib/editor/atto/plugins/title/lib.php | 1 -
.../moodle-atto_title-button-debug.js | 5 -----
.../moodle-atto_title-button/moodle-atto_title-button-min.js | 2 +-
.../moodle-atto_title-button/moodle-atto_title-button.js | 5 -----
lib/editor/atto/plugins/title/yui/src/button/js/button.js | 5 -----
6 files changed, 1 insertion(+), 18 deletions(-)
diff --git a/lib/editor/atto/plugins/title/lang/en/atto_title.php b/lib/editor/atto/plugins/title/lang/en/atto_title.php
index 4435b2bb021..5f626acbc88 100644
--- a/lib/editor/atto/plugins/title/lang/en/atto_title.php
+++ b/lib/editor/atto/plugins/title/lang/en/atto_title.php
@@ -22,7 +22,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['blockquote'] = 'Quoted';
$string['h3'] = 'Heading 1';
$string['h4'] = 'Heading 2';
$string['h5'] = 'Heading 3';
diff --git a/lib/editor/atto/plugins/title/lib.php b/lib/editor/atto/plugins/title/lib.php
index 230aa2140b4..ae51a037e1c 100644
--- a/lib/editor/atto/plugins/title/lib.php
+++ b/lib/editor/atto/plugins/title/lib.php
@@ -30,7 +30,6 @@ function atto_title_strings_for_js() {
$PAGE->requires->strings_for_js(array('h3',
'h4',
'h5',
- 'blockquote',
'pre',
'p'), 'atto_title');
}
diff --git a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
index 688a4fed0ed..e60d30481d1 100644
--- a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
+++ b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
@@ -36,9 +36,6 @@ M.atto_title = M.atto_title || {
var click_pre = function(e, elementid) {
M.atto_title.change_title(e, elementid, '
');
};
@@ -45,7 +42,6 @@ M.atto_title = M.atto_title || {
var h4 = M.util.get_string('h4', 'atto_title');
var h5 = M.util.get_string('h5', 'atto_title');
var pre = M.util.get_string('pre', 'atto_title');
- var blockquote = M.util.get_string('blockquote', 'atto_title');
var p = M.util.get_string('p', 'atto_title');
var iconurl = M.util.image_url('e/styleprops', 'core');
@@ -59,7 +55,6 @@ M.atto_title = M.atto_title || {
{'text' : h4, 'handler' : click_h4},
{'text' : h5, 'handler' : click_h5},
{'text' : pre, 'handler' : click_pre},
- {'text' : blockquote, 'handler' : click_blockquote},
{'text' : p, 'handler' : click_p}
]);
},
From fe0d247796618c77f637c34f81a7201be9c1d691 Mon Sep 17 00:00:00 2001
From: Jerome Mouneyrac
Date: Wed, 12 Feb 2014 13:37:24 +0800
Subject: [PATCH 28/63] MDL-43869 New Atto plugin switch language LTR/RTL
---
.../atto/plugins/rtl/lang/en/atto_rtl.php | 27 ++++++++
lib/editor/atto/plugins/rtl/lib.php | 44 ++++++++++++
lib/editor/atto/plugins/rtl/version.php | 29 ++++++++
.../moodle-atto_rtl-button-debug.js | 69 +++++++++++++++++++
.../moodle-atto_rtl-button-min.js | 1 +
.../moodle-atto_rtl-button.js | 69 +++++++++++++++++++
.../plugins/rtl/yui/src/button/build.json | 10 +++
.../plugins/rtl/yui/src/button/js/button.js | 64 +++++++++++++++++
.../rtl/yui/src/button/meta/button.json | 5 ++
lib/editor/atto/styles.css | 0
.../moodle-editor_atto-editor-debug.js | 9 ++-
.../moodle-editor_atto-editor-min.js | 4 +-
.../moodle-editor_atto-editor.js | 9 ++-
lib/editor/atto/yui/src/editor/js/editor.js | 9 ++-
14 files changed, 341 insertions(+), 8 deletions(-)
create mode 100755 lib/editor/atto/plugins/rtl/lang/en/atto_rtl.php
create mode 100644 lib/editor/atto/plugins/rtl/lib.php
create mode 100755 lib/editor/atto/plugins/rtl/version.php
create mode 100644 lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js
create mode 100644 lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js
create mode 100644 lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js
create mode 100755 lib/editor/atto/plugins/rtl/yui/src/button/build.json
create mode 100755 lib/editor/atto/plugins/rtl/yui/src/button/js/button.js
create mode 100755 lib/editor/atto/plugins/rtl/yui/src/button/meta/button.json
mode change 100644 => 100755 lib/editor/atto/styles.css
mode change 100644 => 100755 lib/editor/atto/yui/src/editor/js/editor.js
diff --git a/lib/editor/atto/plugins/rtl/lang/en/atto_rtl.php b/lib/editor/atto/plugins/rtl/lang/en/atto_rtl.php
new file mode 100755
index 00000000000..77d48899655
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/lang/en/atto_rtl.php
@@ -0,0 +1,27 @@
+.
+
+/**
+ * Strings for component 'atto_rtl', language 'en'.
+ *
+ * @package atto_rtl
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['pluginname'] = 'RTL/LTR';
+$string['rtl'] = 'Right to left';
+$string['ltr'] = 'Left to right';
diff --git a/lib/editor/atto/plugins/rtl/lib.php b/lib/editor/atto/plugins/rtl/lib.php
new file mode 100644
index 00000000000..134cfa32070
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/lib.php
@@ -0,0 +1,44 @@
+.
+
+/**
+ * Atto text editor rtl plugin lib.
+ *
+ * @package atto_rtl
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise the strings required for JS.
+ *
+ * @return void
+ */
+function atto_rtl_strings_for_js() {
+ global $PAGE;
+
+ // In order to prevent extra strings to be imported, comment/uncomment the characters
+ // which are enabled in the JavaScript part of this plugin.
+ $PAGE->requires->strings_for_js(
+ array(
+ 'rtl',
+ 'ltr'
+ ),
+ 'atto_rtl'
+ );
+}
diff --git a/lib/editor/atto/plugins/rtl/version.php b/lib/editor/atto/plugins/rtl/version.php
new file mode 100755
index 00000000000..7a5af0a36a9
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Atto text editor integration version file.
+ *
+ * @package atto_rtl
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2013110500; // Requires this Moodle version.
+$plugin->component = 'atto_rtl'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js
new file mode 100644
index 00000000000..d4ded0faff5
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js
@@ -0,0 +1,69 @@
+YUI.add('moodle-atto_rtl-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 .
+
+/**
+ * Atto text editor rtl plugin.
+ *
+ * @package editor_atto
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_rtl = M.atto_rtl || {
+ init : function(params) {
+ var click = function(e, elementid, direction) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+
+ M.atto_rtl.selection = M.editor_atto.get_selection();
+ if (M.atto_rtl.selection !== false) {
+
+ // Format the selection to be sure it has a tag parent (not the contenteditable).
+ var parentnode = M.editor_atto.format_selection_block(elementid);
+ parentnodeelement = parentnode.getDOMNode();
+
+ var currentdirection = parentnodeelement.getAttribute("dir");
+ if (currentdirection === direction) {
+ parentnodeelement.removeAttribute("dir");
+ } else {
+ parentnodeelement.setAttribute("dir", direction);
+ }
+ }
+
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ var rtlclick = function(e, elementid) {
+ click(e, elementid, 'rtl');
+ };
+
+ var ltrclick = function(e, elementid) {
+ click(e, elementid, 'ltr');
+ };
+
+ var iconurl = M.util.image_url('e/left_to_right', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'ltr', iconurl, params.group, ltrclick, M.util.get_string('ltr', 'atto_rtl'));
+
+ iconurl = M.util.image_url('e/right_to_left', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'rtl', iconurl, params.group, rtlclick, M.util.get_string('rtl', 'atto_rtl'));
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js
new file mode 100644
index 00000000000..ebed822609f
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_rtl-button",function(e,t){M.atto_rtl=M.atto_rtl||{init:function(e){var t=function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_rtl.selection=M.editor_atto.get_selection();if(M.atto_rtl.selection!==!1){var r=M.editor_atto.format_selection_block(t);parentnodeelement=r.getDOMNode();var i=parentnodeelement.getAttribute("dir");i===n?parentnodeelement.removeAttribute("dir"):parentnodeelement.setAttribute("dir",n)}M.editor_atto.text_updated(t)},n=function(e,n){t(e,n,"rtl")},r=function(e,n){t(e,n,"ltr")},i=M.util.image_url("e/left_to_right","core");M.editor_atto.add_toolbar_button(e.elementid,"ltr",i,e.group,r,M.util.get_string("ltr","atto_rtl")),i=M.util.image_url("e/right_to_left","core"),M.editor_atto.add_toolbar_button(e.elementid,"rtl",i,e.group,n,M.util.get_string("rtl","atto_rtl"))}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js
new file mode 100644
index 00000000000..d4ded0faff5
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js
@@ -0,0 +1,69 @@
+YUI.add('moodle-atto_rtl-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 .
+
+/**
+ * Atto text editor rtl plugin.
+ *
+ * @package editor_atto
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_rtl = M.atto_rtl || {
+ init : function(params) {
+ var click = function(e, elementid, direction) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+
+ M.atto_rtl.selection = M.editor_atto.get_selection();
+ if (M.atto_rtl.selection !== false) {
+
+ // Format the selection to be sure it has a tag parent (not the contenteditable).
+ var parentnode = M.editor_atto.format_selection_block(elementid);
+ parentnodeelement = parentnode.getDOMNode();
+
+ var currentdirection = parentnodeelement.getAttribute("dir");
+ if (currentdirection === direction) {
+ parentnodeelement.removeAttribute("dir");
+ } else {
+ parentnodeelement.setAttribute("dir", direction);
+ }
+ }
+
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ var rtlclick = function(e, elementid) {
+ click(e, elementid, 'rtl');
+ };
+
+ var ltrclick = function(e, elementid) {
+ click(e, elementid, 'ltr');
+ };
+
+ var iconurl = M.util.image_url('e/left_to_right', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'ltr', iconurl, params.group, ltrclick, M.util.get_string('ltr', 'atto_rtl'));
+
+ iconurl = M.util.image_url('e/right_to_left', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'rtl', iconurl, params.group, rtlclick, M.util.get_string('rtl', 'atto_rtl'));
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/rtl/yui/src/button/build.json b/lib/editor/atto/plugins/rtl/yui/src/button/build.json
new file mode 100755
index 00000000000..dd12c749869
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_rtl-button",
+ "builds": {
+ "moodle-atto_rtl-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/rtl/yui/src/button/js/button.js b/lib/editor/atto/plugins/rtl/yui/src/button/js/button.js
new file mode 100755
index 00000000000..8f9d48e42f5
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/src/button/js/button.js
@@ -0,0 +1,64 @@
+// 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 .
+
+/**
+ * Atto text editor rtl plugin.
+ *
+ * @package editor_atto
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_rtl = M.atto_rtl || {
+ init : function(params) {
+ var click = function(e, elementid, direction) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+
+ M.atto_rtl.selection = M.editor_atto.get_selection();
+ if (M.atto_rtl.selection !== false) {
+
+ // Format the selection to be sure it has a tag parent (not the contenteditable).
+ var parentnode = M.editor_atto.format_selection_block(elementid);
+ parentnodeelement = parentnode.getDOMNode();
+
+ var currentdirection = parentnodeelement.getAttribute("dir");
+ if (currentdirection === direction) {
+ parentnodeelement.removeAttribute("dir");
+ } else {
+ parentnodeelement.setAttribute("dir", direction);
+ }
+ }
+
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ var rtlclick = function(e, elementid) {
+ click(e, elementid, 'rtl');
+ };
+
+ var ltrclick = function(e, elementid) {
+ click(e, elementid, 'ltr');
+ };
+
+ var iconurl = M.util.image_url('e/left_to_right', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'ltr', iconurl, params.group, ltrclick, M.util.get_string('ltr', 'atto_rtl'));
+
+ iconurl = M.util.image_url('e/right_to_left', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'rtl', iconurl, params.group, rtlclick, M.util.get_string('rtl', 'atto_rtl'));
+ }
+};
diff --git a/lib/editor/atto/plugins/rtl/yui/src/button/meta/button.json b/lib/editor/atto/plugins/rtl/yui/src/button/meta/button.json
new file mode 100755
index 00000000000..8f95ca8cff6
--- /dev/null
+++ b/lib/editor/atto/plugins/rtl/yui/src/button/meta/button.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_rtl-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/styles.css b/lib/editor/atto/styles.css
old mode 100644
new mode 100755
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index fe4af1c16cc..c4c4e39c036 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -394,14 +394,19 @@ M.editor_atto = M.editor_atto || {
* @param string plugin - the plugin defining the button.
* @param string icon - the url to the image for the icon
* @param string groupname - the group the button should be appended to.
+ * @param string title - button title. If null, then the title is the 'pluginname' string.
* @handler function handler- A function to call when the button is clicked.
*/
- add_toolbar_button : function(elementid, plugin, iconurl, groupname, handler) {
+ add_toolbar_button : function(elementid, plugin, iconurl, groupname, handler, title) {
var toolbar = M.editor_atto.get_toolbar_node(elementid),
group = toolbar.one('.atto_group.' + groupname + '_group'),
button,
currentfocus;
+ if (!title) {
+ title = M.util.get_string('pluginname', 'atto_' + plugin);
+ }
+
if (!group) {
group = Y.Node.create('');
toolbar.append(group);
@@ -411,7 +416,7 @@ M.editor_atto = M.editor_atto || {
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'tabindex="-1" ' +
'data-handler="' + Y.Escape.html(plugin) + '" ' +
- 'title="' + Y.Escape.html(M.util.get_string('pluginname', 'atto_' + plugin)) + '">' +
+ 'title="' + Y.Escape.html(title) + '">' +
'' +
'');
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
index c9b21c868d0..fa09cd845bc 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -1,3 +1,3 @@
YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n
Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.
' +
- '' +
- '' + M.util.get_string('accessibilityhint', 'atto_image');
+ '';
var content = Y.Node.create(html);
diff --git a/lib/editor/atto/plugins/link/lang/en/atto_link.php b/lib/editor/atto/plugins/link/lang/en/atto_link.php
index 83f286877f0..cff9e2f862c 100644
--- a/lib/editor/atto/plugins/link/lang/en/atto_link.php
+++ b/lib/editor/atto/plugins/link/lang/en/atto_link.php
@@ -22,7 +22,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['accessibilityhint'] = '
Accessibility recommendation
A mechanism is available to allow the purpose of each link to be identified from link text alone, except where the purpose of the link would be ambiguous to users in general.
Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.
');
+ Y.one('body').prepend(foo);
+ foo.focus();
+
+ try {
+ document.execCommand('insertText', false, 'b');
+ if (foo.getHTML() === 'ba') {
+ document.execCommand('undo', false);
+ if (foo.getHTML() === 'a') {
+ document.execCommand('redo', false);
+ if (foo.getHTML() === 'ba') {
+ undosupport = true;
+ }
+ }
+ }
+ } catch (undosupportexception) {
+ // IE9 gives us an invalid parameter error on document.execCommand('insertText'...).
+ // The try/catch catches when the execCommands fail.
+ return false;
+ }
+
+ // Remove the tmp contenteditable and reset the focussed element.
+ Y.one('body').removeChild(foo);
+ activeelement.focus();
+
+ return undosupport;
+ },
+
+ /**
+ * Add the buttons to the toolbar
+ *
+ * @method init
+ * @param {object} params containing elementid and group
+ */
+ init : function(params) {
+
+ // Retrieve undobrowsersupport global variable.
+ if (M.atto_undo.browsersupportsundo === null) {
+ M.atto_undo.browsersupportsundo = M.atto_undo.test_undo_support();
+ }
+
+ // Add the undo/redo buttons.
+ if (M.atto_undo.browsersupportsundo) {
+ // Undo button.
+ var iconurl = M.util.image_url('e/undo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.undo_handler, 'undo', M.util.get_string('undo', 'atto_undo'));
+
+ // Redo button.
+ iconurl = M.util.image_url('e/redo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.redo_handler, 'redo', M.util.get_string('redo', 'atto_undo'));
+ }
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
new file mode 100644
index 00000000000..c23e0e9d8b4
--- /dev/null
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-atto_undo-button",function(e,t){M.atto_undo=M.atto_undo||{browsersupportsundo:null,click_handler:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand(n,!1,null),M.editor_atto.text_updated(t)},undo_handler:function(e,t){M.atto_undo.click_handler(e,t,"undo")},redo_handler:function(e,t){M.atto_undo.click_handler(e,t,"redo")},test_undo_support:function(){var t=document.activeElement,n=!1,r=e.Node.create('
a
');e.one("body").prepend(r),r.focus();try{document.execCommand("insertText",!1,"b"),r.getHTML()==="ba"&&(document.execCommand("undo",!1),r.getHTML()==="a"&&(document.execCommand("redo",!1),r.getHTML()==="ba"&&(n=!0)))}catch(i){return!1}return e.one("body").removeChild(r),t.focus(),n},init:function(e){M.atto_undo.browsersupportsundo===null&&(M.atto_undo.browsersupportsundo=M.atto_undo.test_undo_support());if(M.atto_undo.browsersupportsundo){var t=M.util.image_url("e/undo","core");M.editor_atto.add_toolbar_button(e.elementid,"undo",t,e.group,M.atto_undo.undo_handler,"undo",M.util.get_string("undo","atto_undo")),t=M.util.image_url("e/redo","core"),M.editor_atto.add_toolbar_button(e.elementid,"undo",t,e.group,M.atto_undo.redo_handler,"redo",M.util.get_string("redo","atto_undo"))}}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
new file mode 100644
index 00000000000..6aa450b5a99
--- /dev/null
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
@@ -0,0 +1,146 @@
+YUI.add('moodle-atto_undo-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 .
+
+/**
+ * Atto text editor undo plugin.
+ *
+ * @package editor-undo
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_undo = M.atto_undo || {
+
+ /**
+ * Property used to cache the result of testing the undo/redo browser support.
+ *
+ * @property browsersupportsundo
+ * @type {Boolean} or null
+ * @default null
+ */
+ browsersupportsundo : null,
+
+ /**
+ * Handle a click on either button (passed via cmd)
+ *
+ * @method click_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ * @param {String} The button clicked (undo or redo)
+ */
+ click_handler : function(e, elementid, cmd) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand(cmd, false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Handle a click on undo
+ *
+ * @method undo_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ */
+ undo_handler : function(e, elementid) {
+ M.atto_undo.click_handler(e, elementid, 'undo');
+ },
+
+ /**
+ * Handle a click on redo
+ *
+ * @method redo_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ */
+ redo_handler : function(e, elementid) {
+ M.atto_undo.click_handler(e, elementid, 'redo');
+ },
+
+ /**
+ * Do a feature test to see if undo/redo is buggy in this browser.
+ *
+ * @method test_undo_support
+ * @return {Boolean} true if undo/redo is functional.
+ */
+ test_undo_support : function() {
+
+ // Check now if other browser supports it.
+ // Save the focussed element.
+ var activeelement = document.activeElement;
+
+ // Creating a temp div to test if the browser support the undo execCommand.
+ var undosupport = false;
+ var foo = Y.Node.create('
a
');
+ Y.one('body').prepend(foo);
+ foo.focus();
+
+ try {
+ document.execCommand('insertText', false, 'b');
+ if (foo.getHTML() === 'ba') {
+ document.execCommand('undo', false);
+ if (foo.getHTML() === 'a') {
+ document.execCommand('redo', false);
+ if (foo.getHTML() === 'ba') {
+ undosupport = true;
+ }
+ }
+ }
+ } catch (undosupportexception) {
+ // IE9 gives us an invalid parameter error on document.execCommand('insertText'...).
+ // The try/catch catches when the execCommands fail.
+ return false;
+ }
+
+ // Remove the tmp contenteditable and reset the focussed element.
+ Y.one('body').removeChild(foo);
+ activeelement.focus();
+
+ return undosupport;
+ },
+
+ /**
+ * Add the buttons to the toolbar
+ *
+ * @method init
+ * @param {object} params containing elementid and group
+ */
+ init : function(params) {
+
+ // Retrieve undobrowsersupport global variable.
+ if (M.atto_undo.browsersupportsundo === null) {
+ M.atto_undo.browsersupportsundo = M.atto_undo.test_undo_support();
+ }
+
+ // Add the undo/redo buttons.
+ if (M.atto_undo.browsersupportsundo) {
+ // Undo button.
+ var iconurl = M.util.image_url('e/undo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.undo_handler, 'undo', M.util.get_string('undo', 'atto_undo'));
+
+ // Redo button.
+ iconurl = M.util.image_url('e/redo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.redo_handler, 'redo', M.util.get_string('redo', 'atto_undo'));
+ }
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/build.json b/lib/editor/atto/plugins/undo/yui/src/button/build.json
new file mode 100755
index 00000000000..b16703f4422
--- /dev/null
+++ b/lib/editor/atto/plugins/undo/yui/src/button/build.json
@@ -0,0 +1,10 @@
+{
+ "name": "moodle-atto_undo-button",
+ "builds": {
+ "moodle-atto_undo-button": {
+ "jsfiles": [
+ "button.js"
+ ]
+ }
+ }
+}
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/js/button.js b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
new file mode 100755
index 00000000000..3b0be134eae
--- /dev/null
+++ b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
@@ -0,0 +1,141 @@
+// 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 .
+
+/**
+ * Atto text editor undo plugin.
+ *
+ * @package editor-undo
+ * @copyright 2014 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.atto_undo = M.atto_undo || {
+
+ /**
+ * Property used to cache the result of testing the undo/redo browser support.
+ *
+ * @property browsersupportsundo
+ * @type {Boolean} or null
+ * @default null
+ */
+ browsersupportsundo : null,
+
+ /**
+ * Handle a click on either button (passed via cmd)
+ *
+ * @method click_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ * @param {String} The button clicked (undo or redo)
+ */
+ click_handler : function(e, elementid, cmd) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand(cmd, false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Handle a click on undo
+ *
+ * @method undo_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ */
+ undo_handler : function(e, elementid) {
+ M.atto_undo.click_handler(e, elementid, 'undo');
+ },
+
+ /**
+ * Handle a click on redo
+ *
+ * @method redo_handler
+ * @param {Y.Event} The click event
+ * @param {String} The id for the editor
+ */
+ redo_handler : function(e, elementid) {
+ M.atto_undo.click_handler(e, elementid, 'redo');
+ },
+
+ /**
+ * Do a feature test to see if undo/redo is buggy in this browser.
+ *
+ * @method test_undo_support
+ * @return {Boolean} true if undo/redo is functional.
+ */
+ test_undo_support : function() {
+
+ // Check now if other browser supports it.
+ // Save the focussed element.
+ var activeelement = document.activeElement;
+
+ // Creating a temp div to test if the browser support the undo execCommand.
+ var undosupport = false;
+ var foo = Y.Node.create('
a
');
+ Y.one('body').prepend(foo);
+ foo.focus();
+
+ try {
+ document.execCommand('insertText', false, 'b');
+ if (foo.getHTML() === 'ba') {
+ document.execCommand('undo', false);
+ if (foo.getHTML() === 'a') {
+ document.execCommand('redo', false);
+ if (foo.getHTML() === 'ba') {
+ undosupport = true;
+ }
+ }
+ }
+ } catch (undosupportexception) {
+ // IE9 gives us an invalid parameter error on document.execCommand('insertText'...).
+ // The try/catch catches when the execCommands fail.
+ return false;
+ }
+
+ // Remove the tmp contenteditable and reset the focussed element.
+ Y.one('body').removeChild(foo);
+ activeelement.focus();
+
+ return undosupport;
+ },
+
+ /**
+ * Add the buttons to the toolbar
+ *
+ * @method init
+ * @param {object} params containing elementid and group
+ */
+ init : function(params) {
+
+ // Retrieve undobrowsersupport global variable.
+ if (M.atto_undo.browsersupportsundo === null) {
+ M.atto_undo.browsersupportsundo = M.atto_undo.test_undo_support();
+ }
+
+ // Add the undo/redo buttons.
+ if (M.atto_undo.browsersupportsundo) {
+ // Undo button.
+ var iconurl = M.util.image_url('e/undo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.undo_handler, 'undo', M.util.get_string('undo', 'atto_undo'));
+
+ // Redo button.
+ iconurl = M.util.image_url('e/redo', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.redo_handler, 'redo', M.util.get_string('redo', 'atto_undo'));
+ }
+ }
+};
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json b/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json
new file mode 100755
index 00000000000..59737f492c8
--- /dev/null
+++ b/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json
@@ -0,0 +1,5 @@
+{
+ "moodle-atto_undo-button": {
+ "requires": ["node"]
+ }
+}
diff --git a/lib/editor/atto/settings.php b/lib/editor/atto/settings.php
old mode 100644
new mode 100755
index 12e22bb566c..b7768e78833
--- a/lib/editor/atto/settings.php
+++ b/lib/editor/atto/settings.php
@@ -30,17 +30,18 @@ $settings = new admin_settingpage('editorsettingsatto', new lang_string('setting
if ($ADMIN->fulltree) {
$name = new lang_string('toolbarconfig', 'editor_atto');
$desc = new lang_string('toolbarconfig_desc', 'editor_atto');
- $default = 'collapse = collapse' . "\n" .
- 'style1 = title, bold, italic' . "\n" .
- 'list = unorderedlist, orderedlist' . "\n" .
- 'links = link, unlink' . "\n" .
- 'files = image, media, managefiles' . "\n" .
- 'style2 = underline, strike, subscript, superscript' . "\n" .
- 'align = align' . "\n" .
- 'indent = indent, outdent' . "\n" .
- 'insert = equation, charmap, table, clear' . "\n" .
- 'accessibility = accessibilitychecker, accessibilityhelper' . "\n" .
- 'other = html';
+ $default = 'collapse = collapse
+style1 = title, bold, italic
+list = unorderedlist, orderedlist
+links = link, unlink
+files = image, media, managefiles
+style2 = underline, strike, subscript, superscript
+align = align
+indent = indent, outdent
+insert = equation, charmap, table, clear
+undo = undo
+accessibility = accessibilitychecker, accessibilityhelper
+other = html';
$setting = new admin_setting_configtextarea('editor_atto/toolbar',
$name,
$desc,
From 3ee53a425f5f5d53a24f5a2e9b3ca87599f1284d Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Wed, 12 Feb 2014 17:32:03 +0800
Subject: [PATCH 37/63] MDL-44092 Atto: Add selectionchanged event and
listeners in the buttons.
This means the buttons can indicate the state of the text under the cursor.
---
.../moodle-atto_bold-button-debug.js | 21 ++
.../moodle-atto_bold-button-min.js | 2 +-
.../moodle-atto_bold-button.js | 21 ++
.../plugins/bold/yui/src/button/js/button.js | 21 ++
.../moodle-atto_equation-button-debug.js | 22 +-
.../moodle-atto_equation-button-min.js | 2 +-
.../moodle-atto_equation-button.js | 22 +-
.../equation/yui/src/button/js/button.js | 22 +-
.../moodle-atto_image-button-debug.js | 43 ++-
.../moodle-atto_image-button-min.js | 4 +-
.../moodle-atto_image-button.js | 43 ++-
.../plugins/image/yui/src/button/js/button.js | 43 ++-
.../moodle-atto_italic-button-debug.js | 21 ++
.../moodle-atto_italic-button-min.js | 2 +-
.../moodle-atto_italic-button.js | 21 ++
.../italic/yui/src/button/js/button.js | 21 ++
.../moodle-atto_link-button-debug.js | 20 ++
.../moodle-atto_link-button-min.js | 2 +-
.../moodle-atto_link-button.js | 20 ++
.../plugins/link/yui/src/button/js/button.js | 20 ++
.../moodle-atto_managefiles-button-debug.js | 2 +-
.../moodle-atto_managefiles-button-min.js | 2 +-
.../moodle-atto_managefiles-button.js | 2 +-
.../managefiles/yui/src/button/js/button.js | 2 +-
.../moodle-atto_orderedlist-button-debug.js | 21 ++
.../moodle-atto_orderedlist-button-min.js | 2 +-
.../moodle-atto_orderedlist-button.js | 21 ++
.../orderedlist/yui/src/button/js/button.js | 21 ++
.../moodle-atto_strike-button-debug.js | 21 ++
.../moodle-atto_strike-button-min.js | 2 +-
.../moodle-atto_strike-button.js | 21 ++
.../strike/yui/src/button/js/button.js | 21 ++
.../moodle-atto_subscript-button-debug.js | 21 ++
.../moodle-atto_subscript-button-min.js | 2 +-
.../moodle-atto_subscript-button.js | 21 ++
.../subscript/yui/src/button/js/button.js | 21 ++
.../moodle-atto_superscript-button-debug.js | 21 ++
.../moodle-atto_superscript-button-min.js | 2 +-
.../moodle-atto_superscript-button.js | 21 ++
.../superscript/yui/src/button/js/button.js | 21 ++
.../moodle-atto_table-button-debug.js | 20 ++
.../moodle-atto_table-button-min.js | 4 +-
.../moodle-atto_table-button.js | 20 ++
.../plugins/table/yui/src/button/js/button.js | 20 ++
.../moodle-atto_underline-button-debug.js | 21 ++
.../moodle-atto_underline-button-min.js | 2 +-
.../moodle-atto_underline-button.js | 21 ++
.../underline/yui/src/button/js/button.js | 21 ++
.../moodle-atto_unlink-button-debug.js | 21 ++
.../moodle-atto_unlink-button-min.js | 2 +-
.../moodle-atto_unlink-button.js | 21 ++
.../unlink/yui/src/button/js/button.js | 21 ++
.../moodle-atto_unorderedlist-button-debug.js | 21 ++
.../moodle-atto_unorderedlist-button-min.js | 2 +-
.../moodle-atto_unorderedlist-button.js | 21 ++
.../unorderedlist/yui/src/button/js/button.js | 21 ++
lib/editor/atto/styles.css | 2 +-
.../moodle-editor_atto-editor-debug.js | 318 +++++++++++++++++-
.../moodle-editor_atto-editor-min.js | 6 +-
.../moodle-editor_atto-editor.js | 318 +++++++++++++++++-
lib/editor/atto/yui/src/editor/js/editor.js | 304 ++++++++++++++++-
.../atto/yui/src/editor/meta/editor.json | 2 +
62 files changed, 1773 insertions(+), 97 deletions(-)
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
index 5319f52284e..697f4de41ff 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'b,strong'
+};
+
/**
* Atto text editor bold plugin.
*
@@ -36,6 +45,18 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'bold');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'bold');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
index bb9a5f786f1..c7e378b20d7 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_bold-button",function(e,t){M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/bold","core");M.editor_atto.add_toolbar_button(e.elementid,"bold",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_bold-button",function(e,t){var n={TAGS:"b,strong"};M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bold","core");M.editor_atto.add_toolbar_button(e.elementid,"bold",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"bold"):M.editor_atto.remove_widget_highlight(e.elementid,"bold")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
index 5319f52284e..697f4de41ff 100644
--- a/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
+++ b/lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'b,strong'
+};
+
/**
* Atto text editor bold plugin.
*
@@ -36,6 +45,18 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'bold');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'bold');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/bold/yui/src/button/js/button.js b/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
index 5a29c73f621..b59bd518b4d 100644
--- a/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/bold/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'b,strong'
+};
+
/**
* Atto text editor bold plugin.
*
@@ -34,5 +43,17 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'bold');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'bold');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
index 0667bacc269..120d5358337 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
@@ -105,7 +105,10 @@ M.atto_equation = M.atto_equation || {
tabview.render();
dialogue.show();
- M.atto_equation.resolve_equation();
+ var equation = M.atto_equation.resolve_equation();
+ if (equation) {
+ Y.one('#atto_equation_equation').set('text', equation);
+ }
M.atto_equation.update_preview(false, elementid);
M.atto_equation.dialogue = dialogue;
}
@@ -128,6 +131,17 @@ M.atto_equation = M.atto_equation || {
// Add the button to the toolbar.
M.editor_atto.add_toolbar_button(params.elementid, 'equation', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.atto_equation.resolve_equation() !== false) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'equation');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'equation');
+ }
+ });
}
},
@@ -136,6 +150,7 @@ M.atto_equation = M.atto_equation || {
* extract the equation (and set it in the form).
*
* @method resolve_equation
+ * @return {String|Boolean} The equation or false.
*/
resolve_equation : function() {
// Find the equation in the surrounding text.
@@ -145,7 +160,7 @@ M.atto_equation = M.atto_equation || {
// Note this is a document fragment and YUI doesn't like them.
if (!selectednode) {
- return;
+ return false;
}
text = Y.one(selectednode).get('text');
@@ -156,8 +171,9 @@ M.atto_equation = M.atto_equation || {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
- Y.one('#atto_equation_equation').set('text', equation);
+ return equation;
}
+ return false;
},
/**
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
index ba9c3cacf3e..375fac12cb2 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_equation-button",function(e,t){M.atto_equation=M.atto_equation||{dialogue:null,selection:null,contextids:{},library:{},lastcursor:0,display_chooser:function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_equation.selection=M.editor_atto.get_selection();if(M.atto_equation.selection!==!1&&!M.atto_equation.selection.collapsed){var r;M.atto_equation.dialogue?r=M.atto_equation.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.render(),r.set("bodyContent",M.atto_equation.get_form_content(n)),r.set("headerContent",M.util.get_string("pluginname","atto_equation"));var i=new e.TabView({srcNode:"#atto_equation_library"});i.render(),r.show(),M.atto_equation.resolve_equation(),M.atto_equation.update_preview(!1,n),M.atto_equation.dialogue=r}},init:function(e){var t=M.util.image_url("e/math","core");e.texfilteractive&&(this.contextids[e.elementid]=e.contextid,this.library=e.library,M.editor_atto.add_toolbar_button(e.elementid,"equation",t,e.group,this.display_chooser))},resolve_equation:function(){var t=M.editor_atto.get_selection_parent_node(),n,r;if(!t)return;n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length&&(r=r.pop(),r=r.substring(2,r.length-2),e.one("#atto_equation_equation").set("text",r))},set_equation:function(t,n){var r,i,s,o,u,a;t.preventDefault(),M.atto_equation.dialogue.hide(),M.editor_atto.set_selection(M.atto_equation.selection),r=t.currentTarget.ancestor(".atto_form").one("textarea"),a=r.get("value"),a!==""&&(a="$$ "+a.trim()+" $$",i=e.one(M.editor_atto.get_selection_parent_node()),s=i.get("text"),o=/\$\$[\S\s]*\$\$/,u=o.exec(s),u&&u.length?(u=u.pop(),s=s.replace(u,"$$"+a+"$$"),i.set("text",s)):document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(a):document.execCommand("insertHTML",!1,a),M.editor_atto.text_updated(n))},update_preview:function(t,n){var r=e.one("#atto_equation_equation"),i=r.get("value"),s,o,u="",a="\\square ",f=r.get("selectionStart");f||(f=0);while(i.charAt(f)==="\\"&&f>0)f-=1;var l=/[\w\{\}]/;while(l.test(i.charAt(f))&&f'+this.get_library_html(t)+'"+' '+"
";var o=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",u={sesskey:M.cfg.sesskey,contextid:this.contextids[t],action:"filtertext",text:n};return preview=e.io(o,{sync:!0,data:u,method:"POST"}),preview.status===200&&(n=preview.responseText),n}}},"@VERSION@",{requires:["node","escape","io","event-valuechange","tabview"]});
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
index 0667bacc269..120d5358337 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
@@ -105,7 +105,10 @@ M.atto_equation = M.atto_equation || {
tabview.render();
dialogue.show();
- M.atto_equation.resolve_equation();
+ var equation = M.atto_equation.resolve_equation();
+ if (equation) {
+ Y.one('#atto_equation_equation').set('text', equation);
+ }
M.atto_equation.update_preview(false, elementid);
M.atto_equation.dialogue = dialogue;
}
@@ -128,6 +131,17 @@ M.atto_equation = M.atto_equation || {
// Add the button to the toolbar.
M.editor_atto.add_toolbar_button(params.elementid, 'equation', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.atto_equation.resolve_equation() !== false) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'equation');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'equation');
+ }
+ });
}
},
@@ -136,6 +150,7 @@ M.atto_equation = M.atto_equation || {
* extract the equation (and set it in the form).
*
* @method resolve_equation
+ * @return {String|Boolean} The equation or false.
*/
resolve_equation : function() {
// Find the equation in the surrounding text.
@@ -145,7 +160,7 @@ M.atto_equation = M.atto_equation || {
// Note this is a document fragment and YUI doesn't like them.
if (!selectednode) {
- return;
+ return false;
}
text = Y.one(selectednode).get('text');
@@ -156,8 +171,9 @@ M.atto_equation = M.atto_equation || {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
- Y.one('#atto_equation_equation').set('text', equation);
+ return equation;
}
+ return false;
},
/**
diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
index 8a38f592c5c..c614e152c11 100644
--- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
@@ -103,7 +103,10 @@ M.atto_equation = M.atto_equation || {
tabview.render();
dialogue.show();
- M.atto_equation.resolve_equation();
+ var equation = M.atto_equation.resolve_equation();
+ if (equation) {
+ Y.one('#atto_equation_equation').set('text', equation);
+ }
M.atto_equation.update_preview(false, elementid);
M.atto_equation.dialogue = dialogue;
}
@@ -126,6 +129,17 @@ M.atto_equation = M.atto_equation || {
// Add the button to the toolbar.
M.editor_atto.add_toolbar_button(params.elementid, 'equation', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.atto_equation.resolve_equation() !== false) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'equation');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'equation');
+ }
+ });
}
},
@@ -134,6 +148,7 @@ M.atto_equation = M.atto_equation || {
* extract the equation (and set it in the form).
*
* @method resolve_equation
+ * @return {String|Boolean} The equation or false.
*/
resolve_equation : function() {
// Find the equation in the surrounding text.
@@ -143,7 +158,7 @@ M.atto_equation = M.atto_equation || {
// Note this is a document fragment and YUI doesn't like them.
if (!selectednode) {
- return;
+ return false;
}
text = Y.one(selectednode).get('text');
@@ -154,8 +169,9 @@ M.atto_equation = M.atto_equation || {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
- Y.one('#atto_equation_equation').set('text', equation);
+ return equation;
}
+ return false;
},
/**
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
index 8ae8b92065a..81e01626367 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_image-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'img'
+};
+
/**
* Atto text editor image plugin.
*
@@ -51,25 +60,11 @@ M.atto_image = M.atto_image || {
dialogue.centerDialogue();
M.atto_image.dialogue = dialogue;
- var selectedText = M.editor_atto.get_selection_text();
- var i = 0;
+ var images = M.editor_atto.get_selected_nodes(elementid);
+ images = images.filter(SELECTORS.TAGS);
- var images = [];
- for (i = 0; i < selectedText.childNodes.length; i++) {
- var child = selectedText.childNodes[0];
- if (images.length === 0) {
- if (child.nodeName.toLowerCase() === 'img') {
- images[0] = child;
- } else {
- if (child.getElementsByTagName) {
- images = child.getElementsByTagName('img');
- }
- }
- }
- }
-
- if (images.length > 0) {
- var image = Y.one(images[0]);
+ if (images.size() > 0) {
+ var image = images.item(0);
var width = image.getAttribute('width');
var height = image.getAttribute('height');
if (width > 0) {
@@ -93,6 +88,18 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ }
+ });
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
index eae2c60b4af..0f48260ffa5 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_image-button",function(e,t){M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(t){var n=function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var r;M.atto_image.dialogue?r=M.atto_image.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),r.set("bodyContent",M.atto_image.get_form_content(n)),r.set("headerContent",M.util.get_string("createimage","atto_image")),r.render(),r.centerDialogue(),M.atto_image.dialogue=r;var i=M.editor_atto.get_selection_text(),s=0,o=[];for(s=0;s0){var a=e.one(o[0]),f=a.getAttribute("width"),l=a.getAttribute("height");f>0&&e.one("#atto_image_widthentry").set("value",f),l>0&&e.one("#atto_image_heightentry").set("value",l),e.one("#atto_image_preview").set("src",a.get("src")),e.one("#atto_image_preview").setStyle("display","inline"),e.one("#atto_image_altentry").set("value",a.get("alt")),e.one("#atto_image_urlentry").set("value",a.get("src"));var c=a.get("role");c==="presentation"&&e.one("#atto_image_presentation").set("checked","checked")}r.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(t.elementid,"image",r,t.group,n)},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_image_urlentry");n.set("value",t.url);var r=new Image;r.onload=function(){e.one("#atto_image_widthentry").set("value",this.width),e.one("#atto_image_heightentry").set("value",this.height),e.one("#atto_image_preview").set("src",this.src),e.one("#atto_image_preview").setStyle("display","inline")},r.src=t.url}},url_changed:function(){var t=e.one("#atto_image_urlentry");if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#atto_image_widthentry"),t.get("value")===""&&t.set("value",this.width),t=e.one("#atto_image_heightentry"),t.get("value")===""&&t.set("value",this.height),t=e.one("#atto_image_preview"),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var r=t.currentTarget.ancestor(".atto_form").one("#atto_image_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry");var s=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_widthentry");var o=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_heightentry");var u=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation");var a=r.get("checked"),f;t.preventDefault();if(s===""&&!a){f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","block"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!0),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!0);return}f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","none"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!1),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide();if(i!==""){M.editor_atto.set_selection(M.atto_image.selection);var l='",document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(l):document.execCommand("insertHTML",!1,l),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_image_urlentry").on("blur",M.atto_image.url_changed,this),r.one("#atto_image_urlentrysubmit").on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&r.one("#openimagebrowser").on("click",M.atto_image.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"
-]});
+YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"};M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(t){var r=function(t,r){t.preventDefault(),M.editor_atto.is_active(r)||M.editor_atto.focus(r),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var i;M.atto_image.dialogue?i=M.atto_image.dialogue:i=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),i.set("bodyContent",M.atto_image.get_form_content(r)),i.set("headerContent",M.util.get_string("createimage","atto_image")),i.render(),i.centerDialogue(),M.atto_image.dialogue=i;var s=M.editor_atto.get_selected_nodes(r);s=s.filter(n.TAGS);if(s.size()>0){var o=s.item(0),u=o.getAttribute("width"),a=o.getAttribute("height");u>0&&e.one("#atto_image_widthentry").set("value",u),a>0&&e.one("#atto_image_heightentry").set("value",a),e.one("#atto_image_preview").set("src",o.get("src")),e.one("#atto_image_preview").setStyle("display","inline"),e.one("#atto_image_altentry").set("value",o.get("alt")),e.one("#atto_image_urlentry").set("value",o.get("src"));var f=o.get("role");f==="presentation"&&e.one("#atto_image_presentation").set("checked","checked")}i.show()}},i=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(t.elementid,"image",i,t.group,r);var s=M.editor_atto.get_editable_node(t.elementid);s.on("atto:selectionchanged",function(e){e.selectedNodes.filter(n.TAGS).size()>0?M.editor_atto.add_widget_highlight(e.elementid,"image"):M.editor_atto.remove_widget_highlight(e.elementid,"image")})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#atto_image_urlentry");n.set("value",t.url);var r=new Image;r.onload=function(){e.one("#atto_image_widthentry").set("value",this.width),e.one("#atto_image_heightentry").set("value",this.height),e.one("#atto_image_preview").set("src",this.src),e.one("#atto_image_preview").setStyle("display","inline")},r.src=t.url}},url_changed:function(){var t=e.one("#atto_image_urlentry");if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#atto_image_widthentry"),t.get("value")===""&&t.set("value",this.width),t=e.one("#atto_image_heightentry"),t.get("value")===""&&t.set("value",this.height),t=e.one("#atto_image_preview"),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var r=t.currentTarget.ancestor(".atto_form").one("#atto_image_urlentry"),i=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry");var s=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_widthentry");var o=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_heightentry");var u=r.get("value");r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation");var a=r.get("checked"),f;t.preventDefault();if(s===""&&!a){f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","block"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!0),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!0);return}f=t.currentTarget.ancestor(".atto_form").one("#atto_image_altwarning"),f.setStyle("display","none"),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_altentry"),r.setAttribute("aria-invalid",!1),r=t.currentTarget.ancestor(".atto_form").one("#atto_image_presentation"),r.setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide();if(i!==""){M.editor_atto.set_selection(M.atto_image.selection);var l='",document.selection&&document.selection.createRange().pasteHTML?document.selection.createRange().pasteHTML(l):document.execCommand("insertHTML",!1,l),M.editor_atto.text_updated(n)}},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_image_urlentry").on("blur",M.atto_image.url_changed,this),r.one("#atto_image_urlentrysubmit").on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&
+r.one("#openimagebrowser").on("click",M.atto_image.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
index 8ae8b92065a..81e01626367 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_image-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'img'
+};
+
/**
* Atto text editor image plugin.
*
@@ -51,25 +60,11 @@ M.atto_image = M.atto_image || {
dialogue.centerDialogue();
M.atto_image.dialogue = dialogue;
- var selectedText = M.editor_atto.get_selection_text();
- var i = 0;
+ var images = M.editor_atto.get_selected_nodes(elementid);
+ images = images.filter(SELECTORS.TAGS);
- var images = [];
- for (i = 0; i < selectedText.childNodes.length; i++) {
- var child = selectedText.childNodes[0];
- if (images.length === 0) {
- if (child.nodeName.toLowerCase() === 'img') {
- images[0] = child;
- } else {
- if (child.getElementsByTagName) {
- images = child.getElementsByTagName('img');
- }
- }
- }
- }
-
- if (images.length > 0) {
- var image = Y.one(images[0]);
+ if (images.size() > 0) {
+ var image = images.item(0);
var width = image.getAttribute('width');
var height = image.getAttribute('height');
if (width > 0) {
@@ -93,6 +88,18 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ }
+ });
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
index 26bff9fa202..b6b94b19066 100644
--- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'img'
+};
+
/**
* Atto text editor image plugin.
*
@@ -49,25 +58,11 @@ M.atto_image = M.atto_image || {
dialogue.centerDialogue();
M.atto_image.dialogue = dialogue;
- var selectedText = M.editor_atto.get_selection_text();
- var i = 0;
+ var images = M.editor_atto.get_selected_nodes(elementid);
+ images = images.filter(SELECTORS.TAGS);
- var images = [];
- for (i = 0; i < selectedText.childNodes.length; i++) {
- var child = selectedText.childNodes[0];
- if (images.length === 0) {
- if (child.nodeName.toLowerCase() === 'img') {
- images[0] = child;
- } else {
- if (child.getElementsByTagName) {
- images = child.getElementsByTagName('img');
- }
- }
- }
- }
-
- if (images.length > 0) {
- var image = Y.one(images[0]);
+ if (images.size() > 0) {
+ var image = images.item(0);
var width = image.getAttribute('width');
var height = image.getAttribute('height');
if (width > 0) {
@@ -91,6 +86,18 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ }
+ });
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
index 184c8058fd4..373b0e22d70 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_italic-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'i'
+};
+
/**
* Atto text editor italic plugin.
*
@@ -36,6 +45,18 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'italic');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'italic');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
index dac855eb9f3..28a75d8d788 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_italic-button",function(e,t){M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_italic-button",function(e,t){var n={TAGS:"i"};M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"italic"):M.editor_atto.remove_widget_highlight(e.elementid,"italic")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
index 184c8058fd4..373b0e22d70 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_italic-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'i'
+};
+
/**
* Atto text editor italic plugin.
*
@@ -36,6 +45,18 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'italic');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'italic');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
index a4000ff1b25..c97236859b6 100644
--- a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'i'
+};
+
/**
* Atto text editor italic plugin.
*
@@ -34,5 +43,17 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'italic');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'italic');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
index c1cf4864db2..9e87f788a27 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_link-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor link plugin.
*
@@ -87,6 +96,17 @@ M.atto_link = M.atto_link || {
init : function(params) {
var iconurl = M.util.image_url('e/insert_edit_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'link');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'link');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
index 923e9a977ba..f3e56775b7a 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_link-button",function(e,t){M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){var t=M.util.image_url("e/insert_edit_link","core");M.editor_atto.add_toolbar_button(e.elementid,"link",t,e.group,this.display_chooser)},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),M.editor_atto.can_show_filepicker(t,"link")&&r.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_link-button",function(e,t){var n={TAGS:"a"};M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){var t=M.util.image_url("e/insert_edit_link","core");M.editor_atto.add_toolbar_button(e.elementid,"link",t,e.group,this.display_chooser);var r=M.editor_atto.get_editable_node(e.elementid);r.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"link"):M.editor_atto.remove_widget_highlight(e.elementid,"link")})},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),M.editor_atto.can_show_filepicker(t,"link")&&r.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
index c1cf4864db2..9e87f788a27 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_link-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor link plugin.
*
@@ -87,6 +96,17 @@ M.atto_link = M.atto_link || {
init : function(params) {
var iconurl = M.util.image_url('e/insert_edit_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'link');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'link');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/link/yui/src/button/js/button.js b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
index 2dcb6c8f011..1993300499f 100644
--- a/lib/editor/atto/plugins/link/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor link plugin.
*
@@ -85,6 +94,17 @@ M.atto_link = M.atto_link || {
init : function(params) {
var iconurl = M.util.image_url('e/insert_edit_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'link', iconurl, params.group, this.display_chooser);
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'link');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'link');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-debug.js b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-debug.js
index 91ba7b6f96f..9372ad7f954 100644
--- a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-debug.js
+++ b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-debug.js
@@ -148,7 +148,7 @@ M.atto_managefiles = M.atto_managefiles || {
content = editableNode.getHTML(),
params = M.atto_managefiles.params[elementid],
baseUrl = M.cfg.wwwroot + '/draftfile.php/' + params.usercontext + '/user/draft/' + params.area.itemid + '/',
- pattern = new RegExp(baseUrl.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
+ pattern = new RegExp(baseUrl.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
filename = '',
match = '',
usedFiles = {};
diff --git a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-min.js b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-min.js
index 6b5dacee2df..8e311dd40cd 100644
--- a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-min.js
+++ b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_managefiles-button",function(e,t){M.atto_managefiles=M.atto_managefiles||{currentElementId:null,dialogue:null,params:{},init:function(t){if(t.disabled)return;if(!(!t.area.itemid&&M.editor_atto.filepickeroptions[t.elementid]&&M.editor_atto.filepickeroptions[t.elementid].image&&M.editor_atto.filepickeroptions[t.elementid].image.itemid)){console.log("Plugin managefiles not available because itemid is missing.");return}t.area.itemid=M.editor_atto.filepickeroptions[t.elementid].image.itemid,M.atto_managefiles.params[t.elementid]=t;var n=function(t,n){var r,i;t.preventDefault(),M.atto_managefiles.currentElementId=n,M.atto_managefiles.dialogue?r=M.atto_managefiles.dialogue:(r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.set("headerContent",M.util.get_string("managefiles","atto_managefiles")),M.atto_managefiles.dialogue=r,r.render(),r.centerDialogue()),i=e.Node.create(""),i.setStyle("height","700px"),i.setStyle("border","none"),i.setStyle("width","100%"),i.setAttribute("src",M.atto_managefiles.getIframeURL()),r.set("bodyContent",i),r.show(),M.editor_atto.text_updated(n)},r=M.util.image_url("e/manage_files","core");M.editor_atto.add_toolbar_button(t.elementid,"managefiles",r,t.group,n)},getIframeURL:function(){var e,t,n="";n=M.cfg.wwwroot+"/lib/editor/atto/plugins/managefiles/manage.php?",t=M.atto_managefiles.params[M.atto_managefiles.currentElementId];for(e in t.area)n+=encodeURIComponent(e)+"="+encodeURIComponent(t.area[e])+"&";return n},getUsedFiles:function(){var e=M.atto_managefiles.currentElementId,t=M.editor_atto.get_editable_node(e),n=t.getHTML(),r=M.atto_managefiles.params[e],i=M.cfg.wwwroot+"/draftfile.php/"+r.usercontext+"/user/draft/"+r.area.itemid+"/",s=new RegExp(i.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")+"(.+?)[\\?\"']","gm"),o="",u="",a={};while((u=s.exec(n))!==null)o=unescape(u[1]),a[o]=!0;return a}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_managefiles-button",function(e,t){M.atto_managefiles=M.atto_managefiles||{currentElementId:null,dialogue:null,params:{},init:function(t){if(t.disabled)return;if(!(!t.area.itemid&&M.editor_atto.filepickeroptions[t.elementid]&&M.editor_atto.filepickeroptions[t.elementid].image&&M.editor_atto.filepickeroptions[t.elementid].image.itemid)){console.log("Plugin managefiles not available because itemid is missing.");return}t.area.itemid=M.editor_atto.filepickeroptions[t.elementid].image.itemid,M.atto_managefiles.params[t.elementid]=t;var n=function(t,n){var r,i;t.preventDefault(),M.atto_managefiles.currentElementId=n,M.atto_managefiles.dialogue?r=M.atto_managefiles.dialogue:(r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.set("headerContent",M.util.get_string("managefiles","atto_managefiles")),M.atto_managefiles.dialogue=r,r.render(),r.centerDialogue()),i=e.Node.create(""),i.setStyle("height","700px"),i.setStyle("border","none"),i.setStyle("width","100%"),i.setAttribute("src",M.atto_managefiles.getIframeURL()),r.set("bodyContent",i),r.show(),M.editor_atto.text_updated(n)},r=M.util.image_url("e/manage_files","core");M.editor_atto.add_toolbar_button(t.elementid,"managefiles",r,t.group,n)},getIframeURL:function(){var e,t,n="";n=M.cfg.wwwroot+"/lib/editor/atto/plugins/managefiles/manage.php?",t=M.atto_managefiles.params[M.atto_managefiles.currentElementId];for(e in t.area)n+=encodeURIComponent(e)+"="+encodeURIComponent(t.area[e])+"&";return n},getUsedFiles:function(){var e=M.atto_managefiles.currentElementId,t=M.editor_atto.get_editable_node(e),n=t.getHTML(),r=M.atto_managefiles.params[e],i=M.cfg.wwwroot+"/draftfile.php/"+r.usercontext+"/user/draft/"+r.area.itemid+"/",s=new RegExp(i.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g,"\\$&")+"(.+?)[\\?\"']","gm"),o="",u="",a={};while((u=s.exec(n))!==null)o=unescape(u[1]),a[o]=!0;return a}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button.js b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button.js
index 91ba7b6f96f..9372ad7f954 100644
--- a/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button.js
+++ b/lib/editor/atto/plugins/managefiles/yui/build/moodle-atto_managefiles-button/moodle-atto_managefiles-button.js
@@ -148,7 +148,7 @@ M.atto_managefiles = M.atto_managefiles || {
content = editableNode.getHTML(),
params = M.atto_managefiles.params[elementid],
baseUrl = M.cfg.wwwroot + '/draftfile.php/' + params.usercontext + '/user/draft/' + params.area.itemid + '/',
- pattern = new RegExp(baseUrl.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
+ pattern = new RegExp(baseUrl.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
filename = '',
match = '',
usedFiles = {};
diff --git a/lib/editor/atto/plugins/managefiles/yui/src/button/js/button.js b/lib/editor/atto/plugins/managefiles/yui/src/button/js/button.js
index ca047246a04..c7c914fcd06 100644
--- a/lib/editor/atto/plugins/managefiles/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/managefiles/yui/src/button/js/button.js
@@ -146,7 +146,7 @@ M.atto_managefiles = M.atto_managefiles || {
content = editableNode.getHTML(),
params = M.atto_managefiles.params[elementid],
baseUrl = M.cfg.wwwroot + '/draftfile.php/' + params.usercontext + '/user/draft/' + params.area.itemid + '/',
- pattern = new RegExp(baseUrl.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
+ pattern = new RegExp(baseUrl.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'),
filename = '',
match = '',
usedFiles = {};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
index 4901f1d3b64..9befd50f99c 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_orderedlist-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ol'
+};
+
/**
* Atto text editor orderedlist plugin.
*
@@ -36,6 +45,18 @@ M.atto_orderedlist = M.atto_orderedlist || {
var iconurl = M.util.image_url('e/numbered_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'orderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
index 245f5d96e74..47e6a7fb47c 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_orderedlist-button",function(e,t){M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/numbered_list","core");M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_orderedlist-button",function(e,t){var n={TAGS:"ol"};M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/numbered_list","core");M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"orderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"orderedlist")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
index 4901f1d3b64..9befd50f99c 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_orderedlist-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ol'
+};
+
/**
* Atto text editor orderedlist plugin.
*
@@ -36,6 +45,18 @@ M.atto_orderedlist = M.atto_orderedlist || {
var iconurl = M.util.image_url('e/numbered_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'orderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
index 397b25b2364..3b7cfa35d43 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ol'
+};
+
/**
* Atto text editor orderedlist plugin.
*
@@ -34,5 +43,17 @@ M.atto_orderedlist = M.atto_orderedlist || {
var iconurl = M.util.image_url('e/numbered_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'orderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'orderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
index 4b753823204..9509588740e 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_strike-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'strike'
+};
+
/**
* Atto text editor strike plugin.
*
@@ -36,6 +45,18 @@ M.atto_strike = M.atto_strike || {
var iconurl = M.util.image_url('e/strikethrough', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'strike');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'strike');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
index 39f48b6e56e..0a1b6980298 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_strike-button",function(e,t){M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/strikethrough","core");M.editor_atto.add_toolbar_button(e.elementid,"strike",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_strike-button",function(e,t){var n={TAGS:"strike"};M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/strikethrough","core");M.editor_atto.add_toolbar_button(e.elementid,"strike",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"strike"):M.editor_atto.remove_widget_highlight(e.elementid,"strike")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
index 4b753823204..9509588740e 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_strike-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'strike'
+};
+
/**
* Atto text editor strike plugin.
*
@@ -36,6 +45,18 @@ M.atto_strike = M.atto_strike || {
var iconurl = M.util.image_url('e/strikethrough', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'strike');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'strike');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
index c7e95459724..326a30c7319 100644
--- a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'strike'
+};
+
/**
* Atto text editor strike plugin.
*
@@ -34,5 +43,17 @@ M.atto_strike = M.atto_strike || {
var iconurl = M.util.image_url('e/strikethrough', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'strike', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'strike');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'strike');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
index db620b46aad..53574086f47 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_subscript-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sub'
+};
+
/**
* Atto text editor subscript plugin.
*
@@ -36,6 +45,18 @@ M.atto_subscript = M.atto_subscript || {
var iconurl = M.util.image_url('e/subscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'subscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sub');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sub');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
index ffb79f06369..a786ff68f7a 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_subscript-button",function(e,t){M.atto_subscript=M.atto_subscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("subscript",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/subscript","core");M.editor_atto.add_toolbar_button(e.elementid,"subscript",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_subscript-button",function(e,t){var n={TAGS:"sub"};M.atto_subscript=M.atto_subscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("subscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/subscript","core");M.editor_atto.add_toolbar_button(e.elementid,"subscript",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sub"):M.editor_atto.remove_widget_highlight(e.elementid,"sub")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
index db620b46aad..53574086f47 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_subscript-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sub'
+};
+
/**
* Atto text editor subscript plugin.
*
@@ -36,6 +45,18 @@ M.atto_subscript = M.atto_subscript || {
var iconurl = M.util.image_url('e/subscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'subscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sub');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sub');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js b/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
index 28afa23827d..aa6f38082e9 100644
--- a/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sub'
+};
+
/**
* Atto text editor subscript plugin.
*
@@ -34,5 +43,17 @@ M.atto_subscript = M.atto_subscript || {
var iconurl = M.util.image_url('e/subscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'subscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sub');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sub');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
index 6a746b8bf9f..c9248427745 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_superscript-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sup'
+};
+
/**
* Atto text editor superscript plugin.
*
@@ -36,6 +45,18 @@ M.atto_superscript = M.atto_superscript || {
var iconurl = M.util.image_url('e/superscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'superscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sup');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sup');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
index 3a0c30cd0de..8ff64ef3a34 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_superscript-button",function(e,t){M.atto_superscript=M.atto_superscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("superscript",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/superscript","core");M.editor_atto.add_toolbar_button(e.elementid,"superscript",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_superscript-button",function(e,t){var n={TAGS:"sup"};M.atto_superscript=M.atto_superscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("superscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/superscript","core");M.editor_atto.add_toolbar_button(e.elementid,"superscript",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sup"):M.editor_atto.remove_widget_highlight(e.elementid,"sup")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
index 6a746b8bf9f..c9248427745 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_superscript-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sup'
+};
+
/**
* Atto text editor superscript plugin.
*
@@ -36,6 +45,18 @@ M.atto_superscript = M.atto_superscript || {
var iconurl = M.util.image_url('e/superscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'superscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sup');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sup');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js b/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
index 708009e3f26..8374e35bd3d 100644
--- a/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'sup'
+};
+
/**
* Atto text editor superscript plugin.
*
@@ -34,5 +43,17 @@ M.atto_superscript = M.atto_superscript || {
var iconurl = M.util.image_url('e/superscript', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'superscript', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'sup');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'sup');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
index 90fbdd6e7f8..34f867e185c 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_table-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'table'
+};
+
/**
* Atto text editor table plugin.
*
@@ -519,6 +528,17 @@ M.atto_table = M.atto_table || {
document.execCommand("enableObjectResizing", false, false);
}
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'table');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'table');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
index 056fcf96a32..8cd9e242030 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_table-button",function(e,t){M.atto_table=M.atto_table||{dialogue:null,selection:null,menunode:null,controlmenu:null,lasttarget:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_table.selection=M.editor_atto.get_selection();if(M.atto_table.selection!==!1&&!M.atto_table.selection.collapsed){var n;M.atto_table.dialogue?n=M.atto_table.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_table.get_form_content(t)),n.set("headerContent",M.util.get_string("createtable","atto_table")),n.show(),M.atto_table.dialogue=n}},show_menu:function(t,n){var r=!1;t.preventDefault();if(this.controlmenu===null){r=!0;var i="
"+l+" ",document.execCommand("insertHTML",!1,u),M.editor_atto.text_updated(n)},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_table"));return n.one("#atto_table_submit").on("click",M.atto_table.set_table,this,t),n}}},"@VERSION@",{requires:["node","escape","event","event-valuechange"]});
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
index 90fbdd6e7f8..34f867e185c 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_table-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'table'
+};
+
/**
* Atto text editor table plugin.
*
@@ -519,6 +528,17 @@ M.atto_table = M.atto_table || {
document.execCommand("enableObjectResizing", false, false);
}
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'table');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'table');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/table/yui/src/button/js/button.js b/lib/editor/atto/plugins/table/yui/src/button/js/button.js
index 8d6850c7c96..311c51de2e8 100644
--- a/lib/editor/atto/plugins/table/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/table/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'table'
+};
+
/**
* Atto text editor table plugin.
*
@@ -517,6 +526,17 @@ M.atto_table = M.atto_table || {
document.execCommand("enableObjectResizing", false, false);
}
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'table');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'table');
+ }
+ });
},
/**
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
index faaec75191c..da25d1a32dc 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_underline-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'u'
+};
+
/**
* Atto text editor underline plugin.
*
@@ -36,6 +45,18 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'underline');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'underline');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
index 523d8b41712..798d0f7d277 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_underline-button",function(e,t){M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_underline-button",function(e,t){var n={TAGS:"u"};M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"underline"):M.editor_atto.remove_widget_highlight(e.elementid,"underline")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
index faaec75191c..da25d1a32dc 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_underline-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'u'
+};
+
/**
* Atto text editor underline plugin.
*
@@ -36,6 +45,18 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'underline');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'underline');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
index 415c4175446..e2dd6b89026 100644
--- a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'u'
+};
+
/**
* Atto text editor underline plugin.
*
@@ -34,5 +43,17 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'underline');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'underline');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
index 49600b02f8f..e8eee96e9d2 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_unlink-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor unlink plugin.
*
@@ -36,6 +45,18 @@ M.atto_unlink = M.atto_unlink || {
var iconurl = M.util.image_url('e/remove_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unlink', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unlink');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
index 74e953d341a..be16b4b81f0 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unlink-button",function(e,t){M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/remove_link","core");M.editor_atto.add_toolbar_button(e.elementid,"unlink",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unlink-button",function(e,t){var n={TAGS:"a"};M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/remove_link","core");M.editor_atto.add_toolbar_button(e.elementid,"unlink",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unlink"):M.editor_atto.remove_widget_highlight(e.elementid,"unlink")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
index 49600b02f8f..e8eee96e9d2 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_unlink-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor unlink plugin.
*
@@ -36,6 +45,18 @@ M.atto_unlink = M.atto_unlink || {
var iconurl = M.util.image_url('e/remove_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unlink', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unlink');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
index 328ea7f1a4c..b01c8ef8b5e 100644
--- a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'a'
+};
+
/**
* Atto text editor unlink plugin.
*
@@ -34,5 +43,17 @@ M.atto_unlink = M.atto_unlink || {
var iconurl = M.util.image_url('e/remove_link', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unlink', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unlink');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
index 26357ac3e4e..4ee39f03540 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_unorderedlist-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ul'
+};
+
/**
* Atto text editor unorderedlist plugin.
*
@@ -36,6 +45,18 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
var iconurl = M.util.image_url('e/bullet_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unorderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
index 013e578820c..d1c67c188d4 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unorderedlist-button",function(e,t){M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/bullet_list","core");M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unorderedlist-button",function(e,t){var n={TAGS:"ul"};M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bullet_list","core");M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unorderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"unorderedlist")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
index 26357ac3e4e..4ee39f03540 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
@@ -15,6 +15,15 @@ YUI.add('moodle-atto_unorderedlist-button', function (Y, NAME) {
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ul'
+};
+
/**
* Atto text editor unorderedlist plugin.
*
@@ -36,6 +45,18 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
var iconurl = M.util.image_url('e/bullet_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unorderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
index d1082b8e72c..d04a38104c2 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
@@ -13,6 +13,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Selectors.
+ *
+ * @type {Object}
+ */
+var SELECTORS = {
+ TAGS : 'ul'
+};
+
/**
* Atto text editor unorderedlist plugin.
*
@@ -34,5 +43,17 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
var iconurl = M.util.image_url('e/bullet_list', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', iconurl, params.group, click);
+
+ // Attach an event listner to watch for "changes" in the contenteditable.
+ // This includes cursor changes, we check if the button should be active or not, based
+ // on the text selection.
+ var editable = M.editor_atto.get_editable_node(params.elementid);
+ editable.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
+ } else {
+ M.editor_atto.remove_widget_highlight(e.elementid, 'unorderedlist');
+ }
+ });
}
};
diff --git a/lib/editor/atto/styles.css b/lib/editor/atto/styles.css
index 2c1032baa2f..981899e3021 100755
--- a/lib/editor/atto/styles.css
+++ b/lib/editor/atto/styles.css
@@ -53,7 +53,7 @@ div.editor_atto_toolbar button[disabled] {
background-color: #ebebeb;
}
-.editor_atto_toolbar button:active {
+.editor_atto_toolbar button:active, .editor_atto_toolbar button.highlight {
background-image: radial-gradient(ellipse at center, #ffffff 40%,#dfdfdf 100%);
background-color: #dfdfdf;
}
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index 4c6c78ea91c..9dedaf58a2d 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -30,7 +30,8 @@ CSS = {
CONTENT: 'editor_atto_content',
CONTENTWRAPPER: 'editor_atto_content_wrap',
TOOLBAR: 'editor_atto_toolbar',
- WRAPPER: 'editor_atto'
+ WRAPPER: 'editor_atto',
+ HIGHLIGHT: 'highlight'
};
/**
@@ -253,6 +254,22 @@ M.editor_atto = M.editor_atto || {
return !element.hasAttribute('disabled');
},
+ /**
+ * Determine if the specified toolbar button/menu is highlighted.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the plugin that created the button/menu.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ is_highlighted : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ return !element.hasClass(CSS.HIGHLIGHT);
+ },
+
/**
* Enable a single widget in the toolbar.
* @param string elementid, the element id of this editor.
@@ -271,6 +288,42 @@ M.editor_atto = M.editor_atto || {
}
},
+ /**
+ * Highlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ add_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.addClass(CSS.HIGHLIGHT);
+ }
+ },
+
+ /**
+ * Unhighlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ remove_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.removeClass(CSS.HIGHLIGHT);
+ }
+ },
+
/**
* Enable all buttons and menus in the toolbar.
* @param string elementid, the element id of this editor.
@@ -558,8 +611,9 @@ M.editor_atto = M.editor_atto || {
// Hide the old textarea.
textarea.hide();
- atto.on('keydown', this.save_selection, this, params.elementid);
- atto.on('mouseup', this.save_selection, this, params.elementid);
+
+ this.publish_events();
+ atto.on('atto:selectionchanged', this.save_selection, this, params.elementid);
atto.on('focus', this.restore_selection, this, params.elementid);
// Do not restore selection when focus is from a click event.
atto.on('mousedown', function() { this.focusfromclick = true; }, this);
@@ -607,6 +661,87 @@ M.editor_atto = M.editor_atto || {
}
}
}
+
+ },
+
+ /**
+ * Add code to trigger the a set of custom events on either the toolbar, or the contenteditable node
+ * that can be listened to by plugins (or even this class).
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ publish_events : function() {
+ Y.Event.define("atto:selectionchanged", {
+ /**
+ * Catch the keydown/mouseup events, and fire a synthetic event for the change event.
+ * @param {Y.Event} e - The real event that triggers this synthetic one.
+ * @param {Object} params - Object containing the subscription object and the notifier.
+ */
+ changeHandler: function (e, params) {
+ // Add 3 properties to the event.
+ // 1. add the elementid.
+ var elementid = params.sub.node.getAttribute('id');
+ // Strip 'editable' from the end of the id.
+ elementid = elementid.substring(0, elementid.length - 8);
+ e.elementid = elementid;
+
+ // 2. The list of leaf nodes contained in the selection.
+ e.selectedNodes = M.editor_atto.get_selected_nodes(elementid);
+ // 3. The current selection (Range)
+ e.selection = M.editor_atto.get_selection();
+ params.notifier.fire(e);
+ },
+
+ /**
+ * Subscribe to the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ */
+ on: function (node, sub, notifier) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetach = node.on('keydown', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoMouseUpDetach = node.on('mouseup', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoFocusDetach = node.on('focus', Y.throttle(this.changeHandler, 100), this, params);
+ },
+
+ /**
+ * Detach the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detach: function (node, sub) {
+ sub.attoKeyDownDetach.detach();
+ sub.attoMouseUpDetach.detach();
+ sub.attoFocusDetach.detach();
+ },
+
+ /**
+ * Delegate the atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ * @param {String} filter - CSS selector for the filter.
+ */
+ delegate: function(node, sub, notifier, filter) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetachDelegate = node.delegate('keydown', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoMouseUpDetachDelegate = node.delegate('mouseup', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoFocusDetachDelegate = node.delegate('focus', Y.throttle(this.changeHandler, 100), filter, this, params);
+ },
+
+ /**
+ * Detach a delegated atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detachDelegate: function(node, sub) {
+ sub.attoKeyDownDetachDelegate.detach();
+ sub.attoMouseUpDetachDelegate.detach();
+ sub.attoFocusDetachDelegate.detach();
+ }
+ });
},
/**
@@ -825,6 +960,169 @@ M.editor_atto = M.editor_atto || {
return sel.inRange(range);
},
+ /**
+ * Runs a selector on each node in the selection and will only return true if all nodes
+ * in the selection match the filter.
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up.
+ * @return {Boolean}
+ */
+ selection_filter_matches : function(elementid, selector, selectednodes) {
+ var result = true;
+
+ if (!selectednodes) {
+ // Find this because it was not passed as a param.
+ selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ }
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+
+ if (selectednodes.size() === 0) {
+ return false;
+ }
+ selectednodes.each(function(node) {
+ if (!node.ancestor(selector, true)) {
+ result = false;
+ }
+ });
+ return result;
+ },
+
+ /**
+ * Returns a list of nodes that are ancestors of the selection nodes,
+ * and match the specified css selector (and are contained within the editable div).
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @return Y.NodeList
+ */
+ selection_filter : function(elementid, selector) {
+ var selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ var result = new Y.NodeList();
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+ selectednodes.each(function(node) {
+ node.ancestors(selector, true).each(function(match) {
+ if (result.indexOf(match) === -1) {
+ result.push(match);
+ }
+ });
+ });
+ return result;
+ },
+
+ /**
+ * Get the deepest possible list of nodes in the current selection.
+ * @param {String} elementid
+ * @return Y.NodeList
+ */
+ get_selected_nodes : function(elementid) {
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var startnode = M.editor_atto.get_selection_start_container();
+ var endnode = M.editor_atto.get_selection_end_container();
+
+ if (!startnode || !endnode) {
+ return new Y.NodeList();
+ }
+
+ /**
+ * Recursive function to walk the dom tree between 2 nodes and build
+ * a list of leaf nodes.
+ * @param {Y.Node} node - The current node.
+ * @param {Y.Node} endnode - The node to stop at.
+ * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
+ * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
+ * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
+ * @return {Y.NodeList} The list of leaf nodes found.
+ */
+ var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
+ var leafnodes = new Y.NodeList();
+ if (endnode === node) {
+ // If there are child nodes and we didn't come from a child node, then skip this end condition.
+ if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
+ // If the end node is a leaf - return it.
+ if (!node.hasChildNodes()) {
+ leafnodes.push(node);
+ }
+ // We reached the end of the selection.
+ return leafnodes;
+ }
+ }
+
+ // Node is not a leaf, look at it's children.
+ if (node.hasChildNodes()) {
+ var children = node.get('childNodes');
+ // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
+ var child = children.item(skipchildren);
+ if (child) {
+ // There was a child node, find all the leaf nodes in it.
+ leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ }
+ } else {
+ // This node has no children - so save it.
+ leafnodes.push(node);
+ }
+ // If we hit the bounding node - this is an end condition.
+ if (node === boundingnode) {
+ return leafnodes;
+ }
+ // We have added all the nodes in this branch, look at the next child of the parent.
+ var parent = node.ancestor();
+ if (parent) {
+ var currentindex = parent.get('childNodes').indexOf(node);
+ leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
+ }
+ return leafnodes;
+ };
+
+ // Kick off the recursive function.
+ return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ },
+
+ /**
+ * Get the first node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_start_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.startContainer) {
+ return Y.one(selection.startContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(true);
+ return Y.one(range.parentElement());
+ }
+ },
+
+ /**
+ * Get the last node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_end_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.endContainer) {
+ return Y.one(selection.endContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(false);
+ return Y.one(range.parentElement());
+ }
+ return false;
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
@@ -1150,4 +1448,16 @@ Y.Node.addMethod("cleanHTML", cleanHTML);
Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML");
-}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event", "event-simulate", "moodle-core-notification"]});
+}, '@VERSION@', {
+ "requires": [
+ "node",
+ "io",
+ "overlay",
+ "escape",
+ "event",
+ "event-simulate",
+ "event-custom",
+ "yui-throttle",
+ "moodle-core-notification"
+ ]
+});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
index 2b09b2356c0..00602830d96 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -1,3 +1,3 @@
-YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n'),l.append(c)),v=M.util.image_url("t/expanded","moodle"),p=e.Node.create(''+''+''+""),c.append(p),h=l.getAttribute("aria-activedescendant"),h||(p.setAttribute("tabindex","0"),l.setAttribute("aria-activedescendant",p.generateID())),M.editor_atto.widgets[d]=d;var m=e.Node.create(''),g=0,y={};for(g=0;g'+y.text+""+"")),M.editor_atto.buttonhandlers[n+"_action_"+g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",lightbox:!1
-,closeButton:!1,center:!1});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.hide(),b.headerNode.hide(),b.render()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create(''),a.append(f)),l=e.Node.create(''+''+""),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&(n=n.pop());var r=null;n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer);var i=M.editor_atto.get_editable_node(t);return r&&i.contains(r)},focus:function(e){M.editor_atto.get_editable_node(e).focus()},init:function(t){var n=e.Node.create(''),r=e.Node.create(''),i=e.Node.create(''),s=e.Node.create(''),o=M.editor_atto.get_textarea_node(t.elementid),u=e.one('[for="'+t.elementid+'"]');u&&(u.generateID(),r.setAttribute("aria-labelledby",u.get("id")),i.setAttribute("aria-labelledby",u.get("id"))),s.appendChild(r),n.appendChild(i),n.appendChild(s),r.setStyle("minHeight",1.2*o.getAttribute("rows")+"em"),r.append(o.get("value")),r.cleanHTML(),o.get("parentNode").insert(n,o),M.editor_atto.disable_css_styling(),o.hide(),r.on("keydown",this.save_selection,this,t.elementid),r.on("mouseup",this.save_selection,this,t.elementid),r.on("focus",this.restore_selection,this,t.elementid),r.on("mousedown",function(){this.focusfromclick=!0},this),r.on("blur",function(){this.focusfromclick=!1,this.text_updated(t.elementid)},this),e.one(e.config.doc.body).delegate("key",this.keyboard_navigation,"down:37,39","#"+t.elementid+"_toolbar",this,t.elementid),M.editor_atto.filepickeroptions[t.elementid]=t.filepickeroptions;var a,f,l,c;for(a=0;a=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t,n,r){e.use("core_filepicker",function(e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},save_selection:function(e,t){if(this.is_active(t)){var n=this.get_selection();n.length>0&&(this.selections[t]=n)}},restore_selection:function(e,t){e.preventDefault(),this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0&&e[0].cloneContents)return e[0].cloneContents()},set_selection:function(e){var t,n;if(window.getSelection){t=window.getSelection(),t.removeAllRanges();for(n=0;n"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}}};var n="Controlmenu",r;r=function(e){e.draggable=!1,e.center=!1,e.width="auto",e.lightbox=!1,e.footerContent="",e.hideOn=[{eventName:"clickoutside"}],r.superclass.constructor.apply(this,[e])},e.extend(r,M.core.dialogue,{initializer:function(t){var n,i,s;r.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("editor_atto_controlmenu"),n=this.bodyNode,i=e.Node.create(""),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","moodle-core-notification"]});
+YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n'),l.append(c)),v=M.util.image_url("t/expanded","moodle"),p=e.Node.create(''+''+''+""),c.append(p),h=l.getAttribute("aria-activedescendant"),h||(p.setAttribute("tabindex","0"),l.setAttribute("aria-activedescendant",p.generateID())),M.editor_atto.widgets[d]=d;var m=e.Node.create(''),g=0,y={};for(g=0;g'+y.text+""+"")),M.editor_atto.buttonhandlers[n+"_action_"+
+g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",lightbox:!1,closeButton:!1,center:!1});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.hide(),b.headerNode.hide(),b.render()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create(''),a.append(f)),l=e.Node.create(''+''+""),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&(n=n.pop());var r=null;n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer);var i=M.editor_atto.get_editable_node(t);return r&&i.contains(r)},focus:function(e){M.editor_atto.get_editable_node(e).focus()},init:function(t){var n=e.Node.create(''),r=e.Node.create(''),i=e.Node.create(''),s=e.Node.create(''),o=M.editor_atto.get_textarea_node(t.elementid),u=e.one('[for="'+t.elementid+'"]');u&&(u.generateID(),r.setAttribute("aria-labelledby",u.get("id")),i.setAttribute("aria-labelledby",u.get("id"))),s.appendChild(r),n.appendChild(i),n.appendChild(s),r.setStyle("minHeight",1.2*o.getAttribute("rows")+"em"),r.append(o.get("value")),r.cleanHTML(),o.get("parentNode").insert(n,o),M.editor_atto.disable_css_styling(),o.hide(),this.publish_events(),r.on("atto:selectionchanged",this.save_selection,this,t.elementid),r.on("focus",this.restore_selection,this,t.elementid),r.on("mousedown",function(){this.focusfromclick=!0},this),r.on("blur",function(){this.focusfromclick=!1,this.text_updated(t.elementid)},this),e.one(e.config.doc.body).delegate("key",this.keyboard_navigation,"down:37,39","#"+t.elementid+"_toolbar",this,t.elementid),M.editor_atto.filepickeroptions[t.elementid]=t.filepickeroptions;var a,f,l,c;for(a=0;a=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t,n,r){e.use("core_filepicker",function(
+e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},save_selection:function(e,t){if(this.is_active(t)){var n=this.get_selection();n.length>0&&(this.selections[t]=n)}},restore_selection:function(e,t){e.preventDefault(),this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0&&e[0].cloneContents)return e[0].cloneContents()},set_selection:function(e){var t,n;if(window.getSelection){t=window.getSelection(),t.removeAllRanges();for(n=0;n"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}}};var n="Controlmenu",r;r=function(e){e.draggable=!1,e.center=!1,e.width="auto",e.lightbox=!1,e.footerContent="",e.hideOn=[{eventName:"clickoutside"}],r.superclass.constructor.apply(this,[e])},e.extend(r,M.core.dialogue,{initializer:function(t){var n,i,s;r.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("editor_atto_controlmenu"),n=this.bodyNode,i=e.Node.create(""),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","event-custom","yui-throttle","moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
index 4c6c78ea91c..9dedaf58a2d 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -30,7 +30,8 @@ CSS = {
CONTENT: 'editor_atto_content',
CONTENTWRAPPER: 'editor_atto_content_wrap',
TOOLBAR: 'editor_atto_toolbar',
- WRAPPER: 'editor_atto'
+ WRAPPER: 'editor_atto',
+ HIGHLIGHT: 'highlight'
};
/**
@@ -253,6 +254,22 @@ M.editor_atto = M.editor_atto || {
return !element.hasAttribute('disabled');
},
+ /**
+ * Determine if the specified toolbar button/menu is highlighted.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the plugin that created the button/menu.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ is_highlighted : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ return !element.hasClass(CSS.HIGHLIGHT);
+ },
+
/**
* Enable a single widget in the toolbar.
* @param string elementid, the element id of this editor.
@@ -271,6 +288,42 @@ M.editor_atto = M.editor_atto || {
}
},
+ /**
+ * Highlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ add_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.addClass(CSS.HIGHLIGHT);
+ }
+ },
+
+ /**
+ * Unhighlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ remove_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.removeClass(CSS.HIGHLIGHT);
+ }
+ },
+
/**
* Enable all buttons and menus in the toolbar.
* @param string elementid, the element id of this editor.
@@ -558,8 +611,9 @@ M.editor_atto = M.editor_atto || {
// Hide the old textarea.
textarea.hide();
- atto.on('keydown', this.save_selection, this, params.elementid);
- atto.on('mouseup', this.save_selection, this, params.elementid);
+
+ this.publish_events();
+ atto.on('atto:selectionchanged', this.save_selection, this, params.elementid);
atto.on('focus', this.restore_selection, this, params.elementid);
// Do not restore selection when focus is from a click event.
atto.on('mousedown', function() { this.focusfromclick = true; }, this);
@@ -607,6 +661,87 @@ M.editor_atto = M.editor_atto || {
}
}
}
+
+ },
+
+ /**
+ * Add code to trigger the a set of custom events on either the toolbar, or the contenteditable node
+ * that can be listened to by plugins (or even this class).
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ publish_events : function() {
+ Y.Event.define("atto:selectionchanged", {
+ /**
+ * Catch the keydown/mouseup events, and fire a synthetic event for the change event.
+ * @param {Y.Event} e - The real event that triggers this synthetic one.
+ * @param {Object} params - Object containing the subscription object and the notifier.
+ */
+ changeHandler: function (e, params) {
+ // Add 3 properties to the event.
+ // 1. add the elementid.
+ var elementid = params.sub.node.getAttribute('id');
+ // Strip 'editable' from the end of the id.
+ elementid = elementid.substring(0, elementid.length - 8);
+ e.elementid = elementid;
+
+ // 2. The list of leaf nodes contained in the selection.
+ e.selectedNodes = M.editor_atto.get_selected_nodes(elementid);
+ // 3. The current selection (Range)
+ e.selection = M.editor_atto.get_selection();
+ params.notifier.fire(e);
+ },
+
+ /**
+ * Subscribe to the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ */
+ on: function (node, sub, notifier) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetach = node.on('keydown', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoMouseUpDetach = node.on('mouseup', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoFocusDetach = node.on('focus', Y.throttle(this.changeHandler, 100), this, params);
+ },
+
+ /**
+ * Detach the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detach: function (node, sub) {
+ sub.attoKeyDownDetach.detach();
+ sub.attoMouseUpDetach.detach();
+ sub.attoFocusDetach.detach();
+ },
+
+ /**
+ * Delegate the atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ * @param {String} filter - CSS selector for the filter.
+ */
+ delegate: function(node, sub, notifier, filter) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetachDelegate = node.delegate('keydown', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoMouseUpDetachDelegate = node.delegate('mouseup', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoFocusDetachDelegate = node.delegate('focus', Y.throttle(this.changeHandler, 100), filter, this, params);
+ },
+
+ /**
+ * Detach a delegated atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detachDelegate: function(node, sub) {
+ sub.attoKeyDownDetachDelegate.detach();
+ sub.attoMouseUpDetachDelegate.detach();
+ sub.attoFocusDetachDelegate.detach();
+ }
+ });
},
/**
@@ -825,6 +960,169 @@ M.editor_atto = M.editor_atto || {
return sel.inRange(range);
},
+ /**
+ * Runs a selector on each node in the selection and will only return true if all nodes
+ * in the selection match the filter.
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up.
+ * @return {Boolean}
+ */
+ selection_filter_matches : function(elementid, selector, selectednodes) {
+ var result = true;
+
+ if (!selectednodes) {
+ // Find this because it was not passed as a param.
+ selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ }
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+
+ if (selectednodes.size() === 0) {
+ return false;
+ }
+ selectednodes.each(function(node) {
+ if (!node.ancestor(selector, true)) {
+ result = false;
+ }
+ });
+ return result;
+ },
+
+ /**
+ * Returns a list of nodes that are ancestors of the selection nodes,
+ * and match the specified css selector (and are contained within the editable div).
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @return Y.NodeList
+ */
+ selection_filter : function(elementid, selector) {
+ var selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ var result = new Y.NodeList();
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+ selectednodes.each(function(node) {
+ node.ancestors(selector, true).each(function(match) {
+ if (result.indexOf(match) === -1) {
+ result.push(match);
+ }
+ });
+ });
+ return result;
+ },
+
+ /**
+ * Get the deepest possible list of nodes in the current selection.
+ * @param {String} elementid
+ * @return Y.NodeList
+ */
+ get_selected_nodes : function(elementid) {
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var startnode = M.editor_atto.get_selection_start_container();
+ var endnode = M.editor_atto.get_selection_end_container();
+
+ if (!startnode || !endnode) {
+ return new Y.NodeList();
+ }
+
+ /**
+ * Recursive function to walk the dom tree between 2 nodes and build
+ * a list of leaf nodes.
+ * @param {Y.Node} node - The current node.
+ * @param {Y.Node} endnode - The node to stop at.
+ * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
+ * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
+ * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
+ * @return {Y.NodeList} The list of leaf nodes found.
+ */
+ var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
+ var leafnodes = new Y.NodeList();
+ if (endnode === node) {
+ // If there are child nodes and we didn't come from a child node, then skip this end condition.
+ if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
+ // If the end node is a leaf - return it.
+ if (!node.hasChildNodes()) {
+ leafnodes.push(node);
+ }
+ // We reached the end of the selection.
+ return leafnodes;
+ }
+ }
+
+ // Node is not a leaf, look at it's children.
+ if (node.hasChildNodes()) {
+ var children = node.get('childNodes');
+ // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
+ var child = children.item(skipchildren);
+ if (child) {
+ // There was a child node, find all the leaf nodes in it.
+ leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ }
+ } else {
+ // This node has no children - so save it.
+ leafnodes.push(node);
+ }
+ // If we hit the bounding node - this is an end condition.
+ if (node === boundingnode) {
+ return leafnodes;
+ }
+ // We have added all the nodes in this branch, look at the next child of the parent.
+ var parent = node.ancestor();
+ if (parent) {
+ var currentindex = parent.get('childNodes').indexOf(node);
+ leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
+ }
+ return leafnodes;
+ };
+
+ // Kick off the recursive function.
+ return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ },
+
+ /**
+ * Get the first node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_start_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.startContainer) {
+ return Y.one(selection.startContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(true);
+ return Y.one(range.parentElement());
+ }
+ },
+
+ /**
+ * Get the last node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_end_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.endContainer) {
+ return Y.one(selection.endContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(false);
+ return Y.one(range.parentElement());
+ }
+ return false;
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
@@ -1150,4 +1448,16 @@ Y.Node.addMethod("cleanHTML", cleanHTML);
Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML");
-}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event", "event-simulate", "moodle-core-notification"]});
+}, '@VERSION@', {
+ "requires": [
+ "node",
+ "io",
+ "overlay",
+ "escape",
+ "event",
+ "event-simulate",
+ "event-custom",
+ "yui-throttle",
+ "moodle-core-notification"
+ ]
+});
diff --git a/lib/editor/atto/yui/src/editor/js/editor.js b/lib/editor/atto/yui/src/editor/js/editor.js
index 2bfc2261e4e..359468b5906 100755
--- a/lib/editor/atto/yui/src/editor/js/editor.js
+++ b/lib/editor/atto/yui/src/editor/js/editor.js
@@ -28,7 +28,8 @@ CSS = {
CONTENT: 'editor_atto_content',
CONTENTWRAPPER: 'editor_atto_content_wrap',
TOOLBAR: 'editor_atto_toolbar',
- WRAPPER: 'editor_atto'
+ WRAPPER: 'editor_atto',
+ HIGHLIGHT: 'highlight'
};
/**
@@ -251,6 +252,22 @@ M.editor_atto = M.editor_atto || {
return !element.hasAttribute('disabled');
},
+ /**
+ * Determine if the specified toolbar button/menu is highlighted.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the plugin that created the button/menu.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ is_highlighted : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ return !element.hasClass(CSS.HIGHLIGHT);
+ },
+
/**
* Enable a single widget in the toolbar.
* @param string elementid, the element id of this editor.
@@ -269,6 +286,42 @@ M.editor_atto = M.editor_atto || {
}
},
+ /**
+ * Highlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ add_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.addClass(CSS.HIGHLIGHT);
+ }
+ },
+
+ /**
+ * Unhighlight a single widget in the toolbar.
+ * @param string elementid, the element id of this editor.
+ * @param string plugin, the name of the plugin that created the widget.
+ * @param string buttonname, optional - used when a plugin has multiple buttons.
+ */
+ remove_widget_highlight : function(elementid, plugin, button) {
+ var buttonpath = plugin;
+ if (button) {
+ buttonpath += '_' + button;
+ }
+ var element = M.editor_atto.get_toolbar_node(elementid).one('.atto_' + buttonpath + '_button');
+
+ if (element) {
+ element.removeClass(CSS.HIGHLIGHT);
+ }
+ },
+
/**
* Enable all buttons and menus in the toolbar.
* @param string elementid, the element id of this editor.
@@ -556,8 +609,9 @@ M.editor_atto = M.editor_atto || {
// Hide the old textarea.
textarea.hide();
- atto.on('keydown', this.save_selection, this, params.elementid);
- atto.on('mouseup', this.save_selection, this, params.elementid);
+
+ this.publish_events();
+ atto.on('atto:selectionchanged', this.save_selection, this, params.elementid);
atto.on('focus', this.restore_selection, this, params.elementid);
// Do not restore selection when focus is from a click event.
atto.on('mousedown', function() { this.focusfromclick = true; }, this);
@@ -605,6 +659,87 @@ M.editor_atto = M.editor_atto || {
}
}
}
+
+ },
+
+ /**
+ * Add code to trigger the a set of custom events on either the toolbar, or the contenteditable node
+ * that can be listened to by plugins (or even this class).
+ * @param string elementid - the id of the textarea we created this editor from.
+ */
+ publish_events : function() {
+ Y.Event.define("atto:selectionchanged", {
+ /**
+ * Catch the keydown/mouseup events, and fire a synthetic event for the change event.
+ * @param {Y.Event} e - The real event that triggers this synthetic one.
+ * @param {Object} params - Object containing the subscription object and the notifier.
+ */
+ changeHandler: function (e, params) {
+ // Add 3 properties to the event.
+ // 1. add the elementid.
+ var elementid = params.sub.node.getAttribute('id');
+ // Strip 'editable' from the end of the id.
+ elementid = elementid.substring(0, elementid.length - 8);
+ e.elementid = elementid;
+
+ // 2. The list of leaf nodes contained in the selection.
+ e.selectedNodes = M.editor_atto.get_selected_nodes(elementid);
+ // 3. The current selection (Range)
+ e.selection = M.editor_atto.get_selection();
+ params.notifier.fire(e);
+ },
+
+ /**
+ * Subscribe to the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ */
+ on: function (node, sub, notifier) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetach = node.on('keydown', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoMouseUpDetach = node.on('mouseup', Y.throttle(this.changeHandler, 100), this, params);
+ sub.attoFocusDetach = node.on('focus', Y.throttle(this.changeHandler, 100), this, params);
+ },
+
+ /**
+ * Detach the atto:selectionchanged event.
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detach: function (node, sub) {
+ sub.attoKeyDownDetach.detach();
+ sub.attoMouseUpDetach.detach();
+ sub.attoFocusDetach.detach();
+ },
+
+ /**
+ * Delegate the atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ * @param {Y.Event.Notifier} notifier - YUI notifier object.
+ * @param {String} filter - CSS selector for the filter.
+ */
+ delegate: function(node, sub, notifier, filter) {
+ var params = { notifier: notifier, sub: sub };
+ sub.attoKeyDownDetachDelegate = node.delegate('keydown', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoMouseUpDetachDelegate = node.delegate('mouseup', Y.throttle(this.changeHandler, 100), filter, this, params);
+ sub.attoFocusDetachDelegate = node.delegate('focus', Y.throttle(this.changeHandler, 100), filter, this, params);
+ },
+
+ /**
+ * Detach a delegated atto:selectionchanged event.
+ *
+ * @param {Y.Node} node - The node for the subscription - must be the contenteditable node.
+ * @param {Y.Subscription} sub - YUI Subscription object.
+ */
+ detachDelegate: function(node, sub) {
+ sub.attoKeyDownDetachDelegate.detach();
+ sub.attoMouseUpDetachDelegate.detach();
+ sub.attoFocusDetachDelegate.detach();
+ }
+ });
},
/**
@@ -823,6 +958,169 @@ M.editor_atto = M.editor_atto || {
return sel.inRange(range);
},
+ /**
+ * Runs a selector on each node in the selection and will only return true if all nodes
+ * in the selection match the filter.
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up.
+ * @return {Boolean}
+ */
+ selection_filter_matches : function(elementid, selector, selectednodes) {
+ var result = true;
+
+ if (!selectednodes) {
+ // Find this because it was not passed as a param.
+ selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ }
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+
+ if (selectednodes.size() === 0) {
+ return false;
+ }
+ selectednodes.each(function(node) {
+ if (!node.ancestor(selector, true)) {
+ result = false;
+ }
+ });
+ return result;
+ },
+
+ /**
+ * Returns a list of nodes that are ancestors of the selection nodes,
+ * and match the specified css selector (and are contained within the editable div).
+ *
+ * @param {String} elementid
+ * @param {String} selector
+ * @return Y.NodeList
+ */
+ selection_filter : function(elementid, selector) {
+ var selectednodes = M.editor_atto.get_selected_nodes(elementid);
+ var result = new Y.NodeList();
+ selector = '.' + CSS.CONTENT + ' ' + selector;
+ selectednodes.each(function(node) {
+ node.ancestors(selector, true).each(function(match) {
+ if (result.indexOf(match) === -1) {
+ result.push(match);
+ }
+ });
+ });
+ return result;
+ },
+
+ /**
+ * Get the deepest possible list of nodes in the current selection.
+ * @param {String} elementid
+ * @return Y.NodeList
+ */
+ get_selected_nodes : function(elementid) {
+
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var startnode = M.editor_atto.get_selection_start_container();
+ var endnode = M.editor_atto.get_selection_end_container();
+
+ if (!startnode || !endnode) {
+ return new Y.NodeList();
+ }
+
+ /**
+ * Recursive function to walk the dom tree between 2 nodes and build
+ * a list of leaf nodes.
+ * @param {Y.Node} node - The current node.
+ * @param {Y.Node} endnode - The node to stop at.
+ * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
+ * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
+ * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
+ * @return {Y.NodeList} The list of leaf nodes found.
+ */
+ var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
+ var leafnodes = new Y.NodeList();
+ if (endnode === node) {
+ // If there are child nodes and we didn't come from a child node, then skip this end condition.
+ if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
+ // If the end node is a leaf - return it.
+ if (!node.hasChildNodes()) {
+ leafnodes.push(node);
+ }
+ // We reached the end of the selection.
+ return leafnodes;
+ }
+ }
+
+ // Node is not a leaf, look at it's children.
+ if (node.hasChildNodes()) {
+ var children = node.get('childNodes');
+ // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
+ var child = children.item(skipchildren);
+ if (child) {
+ // There was a child node, find all the leaf nodes in it.
+ leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ }
+ } else {
+ // This node has no children - so save it.
+ leafnodes.push(node);
+ }
+ // If we hit the bounding node - this is an end condition.
+ if (node === boundingnode) {
+ return leafnodes;
+ }
+ // We have added all the nodes in this branch, look at the next child of the parent.
+ var parent = node.ancestor();
+ if (parent) {
+ var currentindex = parent.get('childNodes').indexOf(node);
+ leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
+ }
+ return leafnodes;
+ };
+
+ // Kick off the recursive function.
+ return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ },
+
+ /**
+ * Get the first node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_start_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.startContainer) {
+ return Y.one(selection.startContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(true);
+ return Y.one(range.parentElement());
+ }
+ },
+
+ /**
+ * Get the last node that contains the current selection.
+ * @return DOMNode or false
+ */
+ get_selection_end_container : function() {
+ var selection = M.editor_atto.get_selection();
+
+ if (selection.length) {
+ selection = selection.pop();
+ }
+
+ if (selection.endContainer) {
+ return Y.one(selection.endContainer);
+ } else if (selection.parentElement) {
+ var range = selection.duplicate();
+
+ range.collapse(false);
+ return Y.one(range.parentElement());
+ }
+ return false;
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
diff --git a/lib/editor/atto/yui/src/editor/meta/editor.json b/lib/editor/atto/yui/src/editor/meta/editor.json
index 1cb269517d2..7f887402add 100644
--- a/lib/editor/atto/yui/src/editor/meta/editor.json
+++ b/lib/editor/atto/yui/src/editor/meta/editor.json
@@ -7,6 +7,8 @@
"escape",
"event",
"event-simulate",
+ "event-custom",
+ "yui-throttle",
"moodle-core-notification"
]
}
From e7109b59494634f0806156b74530f617819109c5 Mon Sep 17 00:00:00 2001
From: Jason Fowler
Date: Wed, 19 Feb 2014 15:16:46 +0800
Subject: [PATCH 38/63] MDL-44216 Atto: Reversing the indent outdent buttons
---
.../plugins/indent/lang/en/atto_indent.php | 2 +
.../en/atto_outdent.php => indent/lib.php} | 18 ++++++--
lib/editor/atto/plugins/indent/version.php | 2 +-
.../moodle-atto_indent-button-debug.js | 22 ++++++++--
.../moodle-atto_indent-button-min.js | 2 +-
.../moodle-atto_indent-button.js | 22 ++++++++--
.../indent/yui/src/button/js/button.js | 22 ++++++++--
lib/editor/atto/plugins/outdent/version.php | 29 -------------
.../moodle-atto_outdent-button-debug.js | 43 -------------------
.../moodle-atto_outdent-button-min.js | 1 -
.../moodle-atto_outdent-button.js | 43 -------------------
.../plugins/outdent/yui/src/button/build.json | 10 -----
.../outdent/yui/src/button/js/button.js | 38 ----------------
.../outdent/yui/src/button/meta/button.json | 5 ---
lib/editor/atto/settings.php | 2 +-
15 files changed, 76 insertions(+), 185 deletions(-)
rename lib/editor/atto/plugins/{outdent/lang/en/atto_outdent.php => indent/lib.php} (69%)
delete mode 100644 lib/editor/atto/plugins/outdent/version.php
delete mode 100644 lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
delete mode 100644 lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
delete mode 100644 lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
delete mode 100644 lib/editor/atto/plugins/outdent/yui/src/button/build.json
delete mode 100644 lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
delete mode 100644 lib/editor/atto/plugins/outdent/yui/src/button/meta/button.json
diff --git a/lib/editor/atto/plugins/indent/lang/en/atto_indent.php b/lib/editor/atto/plugins/indent/lang/en/atto_indent.php
index 9c5a3f0ac3e..780febfd1b4 100644
--- a/lib/editor/atto/plugins/indent/lang/en/atto_indent.php
+++ b/lib/editor/atto/plugins/indent/lang/en/atto_indent.php
@@ -23,3 +23,5 @@
*/
$string['pluginname'] = 'Indent';
+$string['indent'] = "Indent";
+$string['outdent'] = "Outdent";
\ No newline at end of file
diff --git a/lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php b/lib/editor/atto/plugins/indent/lib.php
similarity index 69%
rename from lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php
rename to lib/editor/atto/plugins/indent/lib.php
index 6fb8976ac1b..02c28af9837 100644
--- a/lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php
+++ b/lib/editor/atto/plugins/indent/lib.php
@@ -15,11 +15,21 @@
// along with Moodle. If not, see .
/**
- * Strings for component 'atto_outdent', language 'en'.
+ * Atto text editor align plugin lib.
*
- * @package atto_outdent
- * @copyright 2013 Damyon Wiese
+ * @package atto_align
+ * @copyright 2014 Jason Fowler
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['pluginname'] = 'Outdent';
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Initialise the strings required for JS.
+ *
+ * @return void
+ */
+function atto_indent_strings_for_js() {
+ global $PAGE;
+ $PAGE->requires->strings_for_js(array('indent', 'outdent'), 'atto_indent');
+}
diff --git a/lib/editor/atto/plugins/indent/version.php b/lib/editor/atto/plugins/indent/version.php
index e7aeaa0f877..decf5217aed 100644
--- a/lib/editor/atto/plugins/indent/version.php
+++ b/lib/editor/atto/plugins/indent/version.php
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2014021902; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013110500; // Requires this Moodle version.
$plugin->component = 'atto_indent'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
index a6f626fa3c1..223be234408 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
@@ -24,7 +24,8 @@ YUI.add('moodle-atto_indent-button', function (Y, NAME) {
*/
M.atto_indent = M.atto_indent || {
init : function(params) {
- var click = function(e, elementid) {
+ var iconurl, indent, outdent;
+ indent = function(e, elementid) {
var editable;
e.preventDefault();
@@ -52,8 +53,23 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- var iconurl = M.util.image_url('e/increase_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
+ outdent = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, outdent,
+ 'outdent', M.util.get_string('outdent', 'atto_indent'));
+
+ iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, indent,
+ 'indent', M.util.get_string('indent', 'atto_indent'));
}
};
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
index e4eea858b45..6a88d2579c9 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t=function(e,t){var n;e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),n=M.editor_atto.get_editable_node(t),n.all("blockquote").removeAttribute("style"),M.editor_atto.text_updated(t)},n=M.util.image_url("e/increase_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"indent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_indent-button",function(e,t){M.atto_indent=M.atto_indent||{init:function(e){var t,n,r;n=function(e,t){var n;e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("indent",!1,null),n=M.editor_atto.get_editable_node(t),n.all("blockquote").removeAttribute("style"),M.editor_atto.text_updated(t)},r=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("outdent",!1,null),M.editor_atto.text_updated(t)},t=M.util.image_url("e/decrease_indent","core"),M.editor_atto.add_toolbar_button(e.elementid,"indent",t,e.group,r,"outdent",M.util.get_string("outdent","atto_indent")),t=M.util.image_url("e/increase_indent","core"),M.editor_atto.add_toolbar_button(e.elementid,"indent",t,e.group,n,"indent",M.util.get_string("indent","atto_indent"))}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
index a6f626fa3c1..223be234408 100644
--- a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
+++ b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
@@ -24,7 +24,8 @@ YUI.add('moodle-atto_indent-button', function (Y, NAME) {
*/
M.atto_indent = M.atto_indent || {
init : function(params) {
- var click = function(e, elementid) {
+ var iconurl, indent, outdent;
+ indent = function(e, elementid) {
var editable;
e.preventDefault();
@@ -52,8 +53,23 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- var iconurl = M.util.image_url('e/increase_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
+ outdent = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, outdent,
+ 'outdent', M.util.get_string('outdent', 'atto_indent'));
+
+ iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, indent,
+ 'indent', M.util.get_string('indent', 'atto_indent'));
}
};
diff --git a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
index 918ca690f86..8cdbf334480 100644
--- a/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/indent/yui/src/button/js/button.js
@@ -22,7 +22,8 @@
*/
M.atto_indent = M.atto_indent || {
init : function(params) {
- var click = function(e, elementid) {
+ var iconurl, indent, outdent;
+ indent = function(e, elementid) {
var editable;
e.preventDefault();
@@ -50,7 +51,22 @@ M.atto_indent = M.atto_indent || {
M.editor_atto.text_updated(elementid);
};
- var iconurl = M.util.image_url('e/increase_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, click);
+ outdent = function(e, elementid) {
+ e.preventDefault();
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand('outdent', false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ };
+
+ iconurl = M.util.image_url('e/decrease_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, outdent,
+ 'outdent', M.util.get_string('outdent', 'atto_indent'));
+
+ iconurl = M.util.image_url('e/increase_indent', 'core');
+ M.editor_atto.add_toolbar_button(params.elementid, 'indent', iconurl, params.group, indent,
+ 'indent', M.util.get_string('indent', 'atto_indent'));
}
};
diff --git a/lib/editor/atto/plugins/outdent/version.php b/lib/editor/atto/plugins/outdent/version.php
deleted file mode 100644
index 40db5bdd047..00000000000
--- a/lib/editor/atto/plugins/outdent/version.php
+++ /dev/null
@@ -1,29 +0,0 @@
-.
-
-/**
- * Atto text editor integration version file.
- *
- * @package atto_outdent
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2013110500; // Requires this Moodle version.
-$plugin->component = 'atto_outdent'; // Full name of the plugin (used for diagnostics).
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
deleted file mode 100644
index a958ade9fde..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js
+++ /dev/null
@@ -1,43 +0,0 @@
-YUI.add('moodle-atto_outdent-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 .
-
-/**
- * Atto text editor outdent plugin.
- *
- * @package editor-atto
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-M.atto_outdent = M.atto_outdent || {
- init : function(params) {
- var click = function(e, elementid) {
- e.preventDefault();
- if (!M.editor_atto.is_active(elementid)) {
- M.editor_atto.focus(elementid);
- }
- document.execCommand('outdent', false, null);
- // Clean the YUI ids from the HTML.
- M.editor_atto.text_updated(elementid);
- };
-
- var iconurl = M.util.image_url('e/decrease_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
- }
-};
-
-
-}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
deleted file mode 100644
index a8fccc477ae..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js
+++ /dev/null
@@ -1 +0,0 @@
-YUI.add("moodle-atto_outdent-button",function(e,t){M.atto_outdent=M.atto_outdent||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("outdent",!1,null),M.editor_atto.text_updated(t)},n=M.util.image_url("e/decrease_indent","core");M.editor_atto.add_toolbar_button(e.elementid,"outdent",n,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js b/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
deleted file mode 100644
index a958ade9fde..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js
+++ /dev/null
@@ -1,43 +0,0 @@
-YUI.add('moodle-atto_outdent-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 .
-
-/**
- * Atto text editor outdent plugin.
- *
- * @package editor-atto
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-M.atto_outdent = M.atto_outdent || {
- init : function(params) {
- var click = function(e, elementid) {
- e.preventDefault();
- if (!M.editor_atto.is_active(elementid)) {
- M.editor_atto.focus(elementid);
- }
- document.execCommand('outdent', false, null);
- // Clean the YUI ids from the HTML.
- M.editor_atto.text_updated(elementid);
- };
-
- var iconurl = M.util.image_url('e/decrease_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
- }
-};
-
-
-}, '@VERSION@', {"requires": ["node"]});
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/build.json b/lib/editor/atto/plugins/outdent/yui/src/button/build.json
deleted file mode 100644
index 177e1c7f1d4..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/src/button/build.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "moodle-atto_outdent-button",
- "builds": {
- "moodle-atto_outdent-button": {
- "jsfiles": [
- "button.js"
- ]
- }
- }
-}
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js b/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
deleted file mode 100644
index 8899f012ffd..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/src/button/js/button.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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 .
-
-/**
- * Atto text editor outdent plugin.
- *
- * @package editor-atto
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-M.atto_outdent = M.atto_outdent || {
- init : function(params) {
- var click = function(e, elementid) {
- e.preventDefault();
- if (!M.editor_atto.is_active(elementid)) {
- M.editor_atto.focus(elementid);
- }
- document.execCommand('outdent', false, null);
- // Clean the YUI ids from the HTML.
- M.editor_atto.text_updated(elementid);
- };
-
- var iconurl = M.util.image_url('e/decrease_indent', 'core');
- M.editor_atto.add_toolbar_button(params.elementid, 'outdent', iconurl, params.group, click);
- }
-};
diff --git a/lib/editor/atto/plugins/outdent/yui/src/button/meta/button.json b/lib/editor/atto/plugins/outdent/yui/src/button/meta/button.json
deleted file mode 100644
index 5c46b6c399e..00000000000
--- a/lib/editor/atto/plugins/outdent/yui/src/button/meta/button.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "moodle-atto_outdent-button": {
- "requires": ["node"]
- }
-}
diff --git a/lib/editor/atto/settings.php b/lib/editor/atto/settings.php
index b7768e78833..e383cec1ebe 100755
--- a/lib/editor/atto/settings.php
+++ b/lib/editor/atto/settings.php
@@ -37,7 +37,7 @@ links = link, unlink
files = image, media, managefiles
style2 = underline, strike, subscript, superscript
align = align
-indent = indent, outdent
+indent = indent
insert = equation, charmap, table, clear
undo = undo
accessibility = accessibilitychecker, accessibilityhelper
From 7ca3ad91a0292e8a12e5a0632a4063deb92e169a Mon Sep 17 00:00:00 2001
From: Jerome Mouneyrac
Date: Tue, 25 Feb 2014 14:03:38 +0800
Subject: [PATCH 39/63] MDL-44227 Atto: the screenreader helper now displays
some texts when alt or title don\'t exist
---
.../moodle-atto_accessibilityhelper-button-debug.js | 12 +++++++++++-
.../moodle-atto_accessibilityhelper-button-min.js | 2 +-
.../moodle-atto_accessibilityhelper-button.js | 12 +++++++++++-
.../accessibilityhelper/yui/src/button/js/button.js | 12 +++++++++++-
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-debug.js b/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-debug.js
index 776fde8e97b..6a849558ade 100644
--- a/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-debug.js
+++ b/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-debug.js
@@ -138,9 +138,19 @@ M.atto_accessibilityhelper = M.atto_accessibilityhelper || {
listitem, selectimage;
editable.all('img').each(function(image) {
+
+ // Get the alt or title or img url of the image.
+ var imgalt = image.getAttribute('alt');
+ if (imgalt === '') {
+ imgalt = image.getAttribute('title');
+ if (imgalt === '') {
+ imgalt = image.getAttribute('src');
+ }
+ }
+
selectimage = Y.Node.create('' +
- Y.Escape.html(image.getAttribute('alt')) +
+ Y.Escape.html(imgalt) +
'');
selectimage.setData('sourceimage', image);
diff --git a/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-min.js b/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-min.js
index fea706586cc..80e0359464a 100644
--- a/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-min.js
+++ b/lib/editor/atto/plugins/accessibilityhelper/yui/build/moodle-atto_accessibilityhelper-button/moodle-atto_accessibilityhelper-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_accessibilityhelper-button",function(e,t){var n={STYLESLABEL:"atto_accessibilityhelper_styleslabel",LISTSTYLES:"atto_accessibilityhelper_liststyles",LINKSLABEL:"atto_accessibilityhelper_linkslabel",LISTLINKS:"atto_accessibilityhelper_listlinks",IMAGESLABEL:"atto_accessibilityhelper_imageslabel",LISTIMAGES:"atto_accessibilityhelper_listimages"},r={LISTSTYLES:"#atto_accessibilityhelper_liststyles",LISTLINKS:"#atto_accessibilityhelper_listlinks",LISTIMAGES:"#atto_accessibilityhelper_listimages"};M.atto_accessibilityhelper=M.atto_accessibilityhelper||{dialogue:null,display_ui:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t);var n;M.atto_accessibilityhelper.dialogue?n=M.atto_accessibilityhelper.dialogue:(n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("headerContent",M.util.get_string("pluginname","atto_accessibilityhelper")),n.render()),n.set("bodyContent",M.atto_accessibilityhelper.get_content(t)),n.centerDialogue(),n.show(),M.atto_accessibilityhelper.dialogue=n},init:function(e){var t=M.util.image_url("e/visual_aid","core");M.editor_atto.add_toolbar_button(e.elementid,"accessibilityhelper",t,e.group,this.display_ui)},image_selected:function(e,t){e.preventDefault(),M.atto_accessibilityhelper.dialogue.hide();var n=e.target.getData("sourceimage"),r=M.editor_atto.get_selection_from_node(n);M.editor_atto.selections[t]=r,M.editor_atto.focus(t)},list_images:function(t){var n=e.Node.create(""),r=M.editor_atto.get_editable_node(t),i,s;return r.all("img").each(function(r){s=e.Node.create(''+e.Escape.html(r.getAttribute("alt"))+""),s.setData("sourceimage",r),s.on("click",this.image_selected,this,t),i=e.Node.create(""),i.append(s),n.append(i)},this),n.hasChildNodes()||n.append("
'));
+ }
+ M.atto_accessibilitychecker.displayedwarnings[key] = nodes[i];
}
warning.append(fails);
@@ -151,8 +187,7 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
var editable = M.editor_atto.get_editable_node(elementid);
- // Missing alternatives.
- var missingalt = [], dodgycontrast = [];
+ var problemnodes = [];
// Images with no alt text or dodgy alt text.
var alt;
@@ -160,14 +195,15 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
- missingalt.push(img);
+ problemnodes.push(img);
}
}
}, this);
- this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), problemnodes, true);
// Contrast ratios.
+ problemnodes = [];
var foreground, background, lum1, lum2, ratio;
editable.all('*').each(function (node) {
// Check for non-empty text.
@@ -193,26 +229,26 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
// We only want the highest node with dodgy contrast reported.
var i = 0, found = false;
- for (i = 0; i < dodgycontrast.length; i++) {
- if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ for (i = 0; i < problemnodes.length; i++) {
+ if (node.ancestors('*').indexOf(problemnodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
- } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ } else if (problemnodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
- dodgycontrast[i] = node;
+ problemnodes[i] = node;
found = true;
break;
}
}
if (!found) {
- dodgycontrast.push(node);
+ problemnodes.push(node);
}
}
}
}, this);
- this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), problemnodes, false);
if (!list.hasChildNodes()) {
list.append('
'));
+ }
+ M.atto_accessibilitychecker.displayedwarnings[key] = nodes[i];
}
warning.append(fails);
@@ -151,8 +187,7 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
var editable = M.editor_atto.get_editable_node(elementid);
- // Missing alternatives.
- var missingalt = [], dodgycontrast = [];
+ var problemnodes = [];
// Images with no alt text or dodgy alt text.
var alt;
@@ -160,14 +195,15 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
- missingalt.push(img);
+ problemnodes.push(img);
}
}
}, this);
- this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), problemnodes, true);
// Contrast ratios.
+ problemnodes = [];
var foreground, background, lum1, lum2, ratio;
editable.all('*').each(function (node) {
// Check for non-empty text.
@@ -188,26 +224,26 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
// We only want the highest node with dodgy contrast reported.
var i = 0, found = false;
- for (i = 0; i < dodgycontrast.length; i++) {
- if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ for (i = 0; i < problemnodes.length; i++) {
+ if (node.ancestors('*').indexOf(problemnodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
- } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ } else if (problemnodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
- dodgycontrast[i] = node;
+ problemnodes[i] = node;
found = true;
break;
}
}
if (!found) {
- dodgycontrast.push(node);
+ problemnodes.push(node);
}
}
}
}, this);
- this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), problemnodes, false);
if (!list.hasChildNodes()) {
list.append('
'));
+ }
+ M.atto_accessibilitychecker.displayedwarnings[key] = nodes[i];
}
warning.append(fails);
@@ -149,8 +185,7 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
var editable = M.editor_atto.get_editable_node(elementid);
- // Missing alternatives.
- var missingalt = [], dodgycontrast = [];
+ var problemnodes = [];
// Images with no alt text or dodgy alt text.
var alt;
@@ -158,14 +193,15 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
- missingalt.push(img);
+ problemnodes.push(img);
}
}
}, this);
- this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), missingalt);
+ this.add_warnings(list, M.util.get_string('imagesmissingalt', 'atto_accessibilitychecker'), problemnodes, true);
// Contrast ratios.
+ problemnodes = [];
var foreground, background, lum1, lum2, ratio;
editable.all('*').each(function (node) {
// Check for non-empty text.
@@ -191,26 +227,26 @@ M.atto_accessibilitychecker = M.atto_accessibilitychecker || {
// We only want the highest node with dodgy contrast reported.
var i = 0, found = false;
- for (i = 0; i < dodgycontrast.length; i++) {
- if (node.ancestors('*').indexOf(dodgycontrast[i]) !== -1) {
+ for (i = 0; i < problemnodes.length; i++) {
+ if (node.ancestors('*').indexOf(problemnodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
- } else if (dodgycontrast[i].ancestors('*').indexOf(node) !== -1) {
+ } else if (problemnodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
- dodgycontrast[i] = node;
+ problemnodes[i] = node;
found = true;
break;
}
}
if (!found) {
- dodgycontrast.push(node);
+ problemnodes.push(node);
}
}
}
}, this);
- this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), dodgycontrast);
+ this.add_warnings(list, M.util.get_string('needsmorecontrast', 'atto_accessibilitychecker'), problemnodes, false);
if (!list.hasChildNodes()) {
list.append('
";var o=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",u={sesskey:M.cfg.sesskey,contextid:this.contextids[t],action:"filtertext",text:n};return preview=e.io(o,{sync:!0,data:u,method:"POST"}),preview.status===200&&(n=preview.responseText),n}}},"@VERSION@",{requires:["node","escape","io","event-valuechange","tabview"]});
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
index 60a3533b5af..31ac2fd4116 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
@@ -322,8 +322,21 @@ M.atto_equation = M.atto_equation || {
value = value.substring(0, this.lastcursorpos) + tex + value.substring(this.lastcursorpos, value.length);
input.set('value', value);
- M.atto_equation.update_preview(false, elementid);
input.focus();
+
+ var focusPoint = this.lastcursorpos + tex.length,
+ realInput = input.getDOMNode();
+ if (typeof realInput.selectionStart === "number") {
+ // Modern browsers have selectionStart and selectionEnd to control the cursor position.
+ realInput.selectionStart = realInput.selectionEnd = focusPoint;
+ } else if (typeof realInput.createTextRange !== "undefined") {
+ // Legacy browsers (IE<=9) use createTextRange().
+ var range = realInput.createTextRange();
+ range.moveToPoint(focusPoint);
+ range.select();
+ }
+ // Focus must be set before updating the preview for the cursor box to be in the correct location.
+ M.atto_equation.update_preview(false, elementid);
},
/**
diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
index cb37710c9c9..7d8a58eb22c 100644
--- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
@@ -320,8 +320,21 @@ M.atto_equation = M.atto_equation || {
value = value.substring(0, this.lastcursorpos) + tex + value.substring(this.lastcursorpos, value.length);
input.set('value', value);
- M.atto_equation.update_preview(false, elementid);
input.focus();
+
+ var focusPoint = this.lastcursorpos + tex.length,
+ realInput = input.getDOMNode();
+ if (typeof realInput.selectionStart === "number") {
+ // Modern browsers have selectionStart and selectionEnd to control the cursor position.
+ realInput.selectionStart = realInput.selectionEnd = focusPoint;
+ } else if (typeof realInput.createTextRange !== "undefined") {
+ // Legacy browsers (IE<=9) use createTextRange().
+ var range = realInput.createTextRange();
+ range.moveToPoint(focusPoint);
+ range.select();
+ }
+ // Focus must be set before updating the preview for the cursor box to be in the correct location.
+ M.atto_equation.update_preview(false, elementid);
},
/**
From b5a83da1cfee85e610765311cdaa163dd82dea72 Mon Sep 17 00:00:00 2001
From: Jason Fowler
Date: Tue, 25 Feb 2014 15:57:50 +0800
Subject: [PATCH 52/63] MDL-44215 Atto: Setting toolbar to break at
predetermined point
---
lib/editor/atto/plugins/collapse/styles.css | 5 +++++
.../moodle-atto_collapse-button-debug.js | 3 +++
.../moodle-atto_collapse-button-min.js | 2 +-
.../moodle-atto_collapse-button.js | 3 +++
lib/editor/atto/plugins/collapse/yui/src/button/js/button.js | 3 +++
5 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 lib/editor/atto/plugins/collapse/styles.css
diff --git a/lib/editor/atto/plugins/collapse/styles.css b/lib/editor/atto/plugins/collapse/styles.css
new file mode 100644
index 00000000000..dea44eccf2d
--- /dev/null
+++ b/lib/editor/atto/plugins/collapse/styles.css
@@ -0,0 +1,5 @@
+@media (max-width: 768px) {
+ .toolbarbreak {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-debug.js b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-debug.js
index d99e8bc14fa..dea66443540 100644
--- a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-debug.js
+++ b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-debug.js
@@ -97,7 +97,10 @@ M.atto_collapse = M.atto_collapse || {
after_init : function(params) {
var toolbar = M.editor_atto.get_toolbar_node(params.elementid);
var button = toolbar.one(SELECTORS.BUTTON);
+ var firstgroup = toolbar.all(SELECTORS.GROUPS).item(this.showgroups);
+ // Set the toolbar to break after the initial those displayed by default.
+ firstgroup.insert('', 'before');
// Set the state to "not collapsed" (which is the state when the page loads).
button.setData('collapsed', false);
// Call toggle to change the state when the page loads to "collapsed".
diff --git a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-min.js b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-min.js
index 6e9ef1224d2..ac12d8d9faf 100644
--- a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-min.js
+++ b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_collapse-button",function(e,t){var n={GROUPS:".atto_group",BUTTON:".atto_collapse_button"};M.atto_collapse=M.atto_collapse||{showgroups:3,init:function(e){var t=function(e,t){e.preventDefault(),M.atto_collapse.toggle(t)};this.showgroups=e.showgroups;var n=M.util.image_url("icon","atto_collapse");M.editor_atto.add_toolbar_button(e.elementid,"collapse",n,e.group,t)},toggle:function(e){var t=M.editor_atto.get_toolbar_node(e),r=t.one(n.BUTTON),i=t.all(n.GROUPS).slice(this.showgroups);r.getData("collapsed")?(r.set("title",M.util.get_string("showmore","atto_collapse")),i.show(),r.setData("collapsed",!1)):(r.set("title",M.util.get_string("showless","atto_collapse")),i.hide(),r.setData("collapsed",!0))},after_init:function(e){var t=M.editor_atto.get_toolbar_node(e.elementid),r=t.one(n.BUTTON);r.setData("collapsed",!1),M.atto_collapse.toggle(e.elementid)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_collapse-button",function(e,t){var n={GROUPS:".atto_group",BUTTON:".atto_collapse_button"};M.atto_collapse=M.atto_collapse||{showgroups:3,init:function(e){var t=function(e,t){e.preventDefault(),M.atto_collapse.toggle(t)};this.showgroups=e.showgroups;var n=M.util.image_url("icon","atto_collapse");M.editor_atto.add_toolbar_button(e.elementid,"collapse",n,e.group,t)},toggle:function(e){var t=M.editor_atto.get_toolbar_node(e),r=t.one(n.BUTTON),i=t.all(n.GROUPS).slice(this.showgroups);r.getData("collapsed")?(r.set("title",M.util.get_string("showmore","atto_collapse")),i.show(),r.setData("collapsed",!1)):(r.set("title",M.util.get_string("showless","atto_collapse")),i.hide(),r.setData("collapsed",!0))},after_init:function(e){var t=M.editor_atto.get_toolbar_node(e.elementid),r=t.one(n.BUTTON),i=t.all(n.GROUPS).item(this.showgroups);i.insert('',"before"),r.setData("collapsed",!1),M.atto_collapse.toggle(e.elementid)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button.js b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button.js
index d99e8bc14fa..dea66443540 100644
--- a/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button.js
+++ b/lib/editor/atto/plugins/collapse/yui/build/moodle-atto_collapse-button/moodle-atto_collapse-button.js
@@ -97,7 +97,10 @@ M.atto_collapse = M.atto_collapse || {
after_init : function(params) {
var toolbar = M.editor_atto.get_toolbar_node(params.elementid);
var button = toolbar.one(SELECTORS.BUTTON);
+ var firstgroup = toolbar.all(SELECTORS.GROUPS).item(this.showgroups);
+ // Set the toolbar to break after the initial those displayed by default.
+ firstgroup.insert('', 'before');
// Set the state to "not collapsed" (which is the state when the page loads).
button.setData('collapsed', false);
// Call toggle to change the state when the page loads to "collapsed".
diff --git a/lib/editor/atto/plugins/collapse/yui/src/button/js/button.js b/lib/editor/atto/plugins/collapse/yui/src/button/js/button.js
index 2715e711f50..d0e09e1839e 100644
--- a/lib/editor/atto/plugins/collapse/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/collapse/yui/src/button/js/button.js
@@ -95,7 +95,10 @@ M.atto_collapse = M.atto_collapse || {
after_init : function(params) {
var toolbar = M.editor_atto.get_toolbar_node(params.elementid);
var button = toolbar.one(SELECTORS.BUTTON);
+ var firstgroup = toolbar.all(SELECTORS.GROUPS).item(this.showgroups);
+ // Set the toolbar to break after the initial those displayed by default.
+ firstgroup.insert('', 'before');
// Set the state to "not collapsed" (which is the state when the page loads).
button.setData('collapsed', false);
// Call toggle to change the state when the page loads to "collapsed".
From 86a83e3a9fa9e0563b3c207db0f19d3da3e1a516 Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Wed, 5 Mar 2014 16:25:29 +0800
Subject: [PATCH 53/63] MDL-44063 Atto: Set the default block element to p (if
the browser supports it)
Also insert an empty paragraph by default and remove it on save.
---
.../moodle-atto_html-button-debug.js | 4 +-
.../moodle-atto_html-button-min.js | 2 +-
.../moodle-atto_html-button.js | 4 +-
.../plugins/html/yui/src/button/js/button.js | 4 +-
.../moodle-editor_atto-editor-debug.js | 47 ++++++++++++++++---
.../moodle-editor_atto-editor-min.js | 6 +--
.../moodle-editor_atto-editor.js | 47 ++++++++++++++++---
lib/editor/atto/yui/src/editor/js/editor.js | 47 ++++++++++++++++---
8 files changed, 133 insertions(+), 28 deletions(-)
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
index 41cc4a45cff..d1feb4d812f 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js
@@ -36,8 +36,8 @@ M.atto_html = M.atto_html || {
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
- atto.setHTML('');
- atto.append(textarea.get('value'));
+ // Copy the text to the contenteditable div.
+ M.editor_atto.update_from_textarea(elementid);
textarea.hide();
atto.show();
atto.focus();
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
index 527cd8798fc..d0efdf30013 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_html-button",function(e,t){M.atto_html=M.atto_html||{ishtml:!1,init:function(e){var t=function(e,t){e.preventDefault();var n=M.editor_atto.get_textarea_node(t),r=M.editor_atto.get_editable_node(t);M.atto_html.ishtml?(M.editor_atto.enable_all_widgets(t),r.setHTML(""),r.append(n.get("value")),n.hide(),r.show(),r.focus()):(M.editor_atto.disable_all_widgets(t),M.editor_atto.enable_widget(t,"html"),M.editor_atto.text_updated(t),r.hide(),n.show(),n.focus()),M.atto_html.ishtml=!M.atto_html.ishtml},n=M.util.image_url("e/source_code","core");M.editor_atto.add_toolbar_button(e.elementid,"html",n,e.group,t)}}},"@VERSION@",{requires:["node","event-valuechange"]});
+YUI.add("moodle-atto_html-button",function(e,t){M.atto_html=M.atto_html||{ishtml:!1,init:function(e){var t=function(e,t){e.preventDefault();var n=M.editor_atto.get_textarea_node(t),r=M.editor_atto.get_editable_node(t);M.atto_html.ishtml?(M.editor_atto.enable_all_widgets(t),M.editor_atto.update_from_textarea(t),n.hide(),r.show(),r.focus()):(M.editor_atto.disable_all_widgets(t),M.editor_atto.enable_widget(t,"html"),M.editor_atto.text_updated(t),r.hide(),n.show(),n.focus()),M.atto_html.ishtml=!M.atto_html.ishtml},n=M.util.image_url("e/source_code","core");M.editor_atto.add_toolbar_button(e.elementid,"html",n,e.group,t)}}},"@VERSION@",{requires:["node","event-valuechange"]});
diff --git a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
index 41cc4a45cff..d1feb4d812f 100644
--- a/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
+++ b/lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js
@@ -36,8 +36,8 @@ M.atto_html = M.atto_html || {
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
- atto.setHTML('');
- atto.append(textarea.get('value'));
+ // Copy the text to the contenteditable div.
+ M.editor_atto.update_from_textarea(elementid);
textarea.hide();
atto.show();
atto.focus();
diff --git a/lib/editor/atto/plugins/html/yui/src/button/js/button.js b/lib/editor/atto/plugins/html/yui/src/button/js/button.js
index b3af8f57a45..5d148f3a4b0 100644
--- a/lib/editor/atto/plugins/html/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/html/yui/src/button/js/button.js
@@ -34,8 +34,8 @@ M.atto_html = M.atto_html || {
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
- atto.setHTML('');
- atto.append(textarea.get('value'));
+ // Copy the text to the contenteditable div.
+ M.editor_atto.update_from_textarea(elementid);
textarea.hide();
atto.show();
atto.focus();
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index 4a959342890..69288d5c6d2 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -563,6 +563,35 @@ M.editor_atto = M.editor_atto || {
M.editor_atto.get_editable_node(elementid).focus();
},
+ /**
+ * Copy and clean the text from the textarea into the contenteditable div.
+ * If the text is empty, provide a default paragraph tag to hold the content.
+ *
+ * @param string elementid of this editor
+ */
+ update_from_textarea : function(elementid) {
+ var editable = M.editor_atto.get_editable_node(elementid);
+ var textarea = M.editor_atto.get_textarea_node(elementid);
+
+ // Clear it first.
+ editable.setHTML('');
+
+ // Copy text to editable div.
+ editable.append(textarea.get('value'));
+
+ // Clean it.
+ editable.cleanHTML();
+
+ // Insert a paragraph in the empty contenteditable div.
+ if (editable.getHTML() === '') {
+ if (Y.UA.ie && Y.UA.ie < 10) {
+ editable.setHTML('');
+ } else {
+ editable.setHTML('
');
+ }
+ }
+ },
+
/**
* Initialise the editor
* @param object params for this editor instance.
@@ -600,11 +629,6 @@ M.editor_atto = M.editor_atto || {
// Style the editor.
atto.setStyle('minHeight', (1.2 * (textarea.getAttribute('rows'))) + 'em');
- // Copy text to editable div.
- atto.append(textarea.get('value'));
-
- // Clean it.
- atto.cleanHTML();
// Add the toolbar and editable zone to the page.
textarea.get('parentNode').insert(wrapper, textarea);
@@ -615,6 +639,9 @@ M.editor_atto = M.editor_atto || {
// Hide the old textarea.
textarea.hide();
+ // Copy the text to the contenteditable div.
+ this.update_from_textarea(params.elementid);
+
this.publish_events();
atto.on('atto:selectionchanged', this.save_selection, this, params.elementid);
atto.on('focus', this.restore_selection, this, params.elementid);
@@ -627,6 +654,10 @@ M.editor_atto = M.editor_atto || {
this.text_updated(params.elementid);
}, this);
+ // Use paragraphs not divs.
+ if (document.queryCommandSupported('DefaultParagraphSeparator')) {
+ document.execCommand('DefaultParagraphSeparator', false, 'p');
+ }
// Listen for Arrow left and Arrow right keys.
Y.one(Y.config.doc.body).delegate('key',
this.keyboard_navigation,
@@ -786,6 +817,11 @@ M.editor_atto = M.editor_atto || {
// Remove any and all nasties from source.
atto.cleanHTML();
+ // Revert untouched editor contents to an empty string.
+ if (atto.getHTML() === '' || atto.getHTML() === '
";var o=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",u={sesskey:M.cfg.sesskey,contextid:this.contextids[t],action:"filtertext",text:n};return preview=e.io(o,{sync:!0,data:u,method:"POST"}),preview.status===200&&(n=preview.responseText),n}}},"@VERSION@",{requires:["node","escape","io","event-valuechange","tabview"]});
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
index 31ac2fd4116..4fee53bca80 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
@@ -134,9 +134,8 @@ M.atto_equation = M.atto_equation || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
- if (M.atto_equation.resolve_equation() !== false) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
+ if (M.atto_equation.resolve_equation()) {
M.editor_atto.add_widget_highlight(e.elementid, 'equation');
} else {
M.editor_atto.remove_widget_highlight(e.elementid, 'equation');
diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
index 7d8a58eb22c..9253c0b5a3c 100644
--- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
@@ -132,9 +132,8 @@ M.atto_equation = M.atto_equation || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
- if (M.atto_equation.resolve_equation() !== false) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
+ if (M.atto_equation.resolve_equation()) {
M.editor_atto.add_widget_highlight(e.elementid, 'equation');
} else {
M.editor_atto.remove_widget_highlight(e.elementid, 'equation');
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
index d151158e5a3..566b652527d 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
@@ -144,6 +144,7 @@ ALIGNMENTS = [
M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
+ currentlyselected : {},
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -175,16 +176,19 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+ M.editor_atto.currentlyselected = M.editor_atto.currentlyselected || {};
+ M.editor_atto.currentlyselected[params.elementid] = null;
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
- if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = null;
}
});
},
@@ -303,11 +307,17 @@ M.atto_image = M.atto_image || {
display: 'inline',
presentation: false
},
- images = M.editor_atto.get_selected_nodes(elementid).filter(SELECTORS.TAGS),
+ images = M.editor_atto.currentlyselected[elementid],
i, image, width, height, style;
- if (images.size() > 0) {
+ if (images !== null && images.size() > 0) {
image = images.item(0);
+ for (i in images) {
+ if (images[i].test('img')) {
+ image = images[i];
+ break;
+ }
+ }
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
index 4bd5ae54923..074c97fd6eb 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){e.selectedNodes.filter(n.TAGS).size()>0?M.editor_atto.add_widget_highlight(e.elementid,"image"):M.editor_atto.remove_widget_highlight(e.elementid,"image")})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),s!==""&&(M.editor_atto.set_selection(M.atto_image.selection),c='",M.editor_atto.insert_html_at_focus_point(c),M.editor_atto.text_updated(n))},_get_selected_image_properties:function(e){var t={src:null,alt:null,width:null,height:null,align:null,display:"inline",presentation:!1},r=M.editor_atto.get_selected_nodes(e).filter(n.TAGS),s,o,u,a,f;if(r.size()>0){o=r.item(0),f=o.getAttribute("style"),u=parseInt(o.getAttribute("width"),10),a=parseInt(o.getAttribute("height"),10),u>0&&(t.width=u),a>0&&(t.height=a);for(s in i)if(i[s].test(f)){t.align=i[s];break}return t.src=o.getAttribute("src"),t.alt=o.getAttribute("alt")||"",t.presentation=o.get("role")==="presentation",t}return!1},get_form_content:function(t){var n={alignment:M.util.get_string("alignment","atto_image"),alt:M.util.get_string("enteralt","atto_image"),browse:M.util.get_string("browserepositories","atto_image"),create:M.util.get_string("createimage","atto_image"),height:M.util.get_string("height","atto_image"),presentation:M.util.get_string("presentation","atto_image"),presentationrequired:M.util.get_string("presentationoraltrequired","atto_image"),preview:M.util.get_string("preview","atto_image"),width:M.util.get_string("width","atto_image"),url:M.util.get_string("enterurl","atto_image")},s,o;s='";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,currentlyselected:{},init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t),M.editor_atto.currentlyselected=M.editor_atto.currentlyselected||{},M.editor_atto.currentlyselected[e.elementid]=null,M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?(M.editor_atto.add_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=e.selectedNodes):(M.editor_atto.remove_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=null)})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),s!==""&&(M.editor_atto.set_selection(M.atto_image.selection),c='",M.editor_atto.insert_html_at_focus_point(c),M.editor_atto.text_updated(n))},_get_selected_image_properties:function(e){var t={src:null,alt:null,width:null,height:null,align:null,display:"inline",presentation:!1},n=M.editor_atto.currentlyselected[e],r,s,o,u,a;if(n!==null&&n.size()>0){s=n.item(0);for(r in n)if(n[r].test("img")){s=n[r];break}a=s.getAttribute("style"),o=parseInt(s.getAttribute("width"),10),u=parseInt(s.getAttribute("height"),10),o>0&&(t.width=o),u>0&&(t.height=u);for(r in i)if(i[r].test(a)){t.align=i[r];break}return t.src=s.getAttribute("src"),t.alt=s.getAttribute("alt")||"",t.presentation=s.get("role")==="presentation",t}return!1},get_form_content:function(t){var n={alignment:M.util.get_string("alignment","atto_image"),alt:M.util.get_string("enteralt","atto_image"),browse:M.util.get_string("browserepositories","atto_image"),create:M.util.get_string("createimage","atto_image"),height:M.util.get_string("height","atto_image"),presentation:M.util.get_string("presentation","atto_image"),presentationrequired:M.util.get_string("presentationoraltrequired","atto_image"),preview:M.util.get_string("preview","atto_image"),width:M.util.get_string("width","atto_image"),url:M.util.get_string("enterurl","atto_image")},s,o;s='";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
index d151158e5a3..566b652527d 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
@@ -144,6 +144,7 @@ ALIGNMENTS = [
M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
+ currentlyselected : {},
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -175,16 +176,19 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+ M.editor_atto.currentlyselected = M.editor_atto.currentlyselected || {};
+ M.editor_atto.currentlyselected[params.elementid] = null;
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
- if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = null;
}
});
},
@@ -303,11 +307,17 @@ M.atto_image = M.atto_image || {
display: 'inline',
presentation: false
},
- images = M.editor_atto.get_selected_nodes(elementid).filter(SELECTORS.TAGS),
+ images = M.editor_atto.currentlyselected[elementid],
i, image, width, height, style;
- if (images.size() > 0) {
+ if (images !== null && images.size() > 0) {
image = images.item(0);
+ for (i in images) {
+ if (images[i].test('img')) {
+ image = images[i];
+ break;
+ }
+ }
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
index 83b376779b6..2b77cc803ce 100644
--- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
@@ -142,6 +142,7 @@ ALIGNMENTS = [
M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
+ currentlyselected : {},
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -173,16 +174,19 @@ M.atto_image = M.atto_image || {
var iconurl = M.util.image_url('e/insert_edit_image', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'image', iconurl, params.group, display_chooser);
+ M.editor_atto.currentlyselected = M.editor_atto.currentlyselected || {};
+ M.editor_atto.currentlyselected[params.elementid] = null;
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
- if (e.selectedNodes.filter(SELECTORS.TAGS).size() > 0) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
M.editor_atto.remove_widget_highlight(e.elementid, 'image');
+ M.editor_atto.currentlyselected[e.elementid] = null;
}
});
},
@@ -301,11 +305,17 @@ M.atto_image = M.atto_image || {
display: 'inline',
presentation: false
},
- images = M.editor_atto.get_selected_nodes(elementid).filter(SELECTORS.TAGS),
+ images = M.editor_atto.currentlyselected[elementid],
i, image, width, height, style;
- if (images.size() > 0) {
+ if (images !== null && images.size() > 0) {
image = images.item(0);
+ for (i in images) {
+ if (images[i].test('img')) {
+ image = images[i];
+ break;
+ }
+ }
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
index ed5f947505e..5c095951e0f 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js
@@ -50,8 +50,7 @@ M.atto_italic = M.atto_italic || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'italic');
} else {
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
index 073511cf59c..a2f065346a9 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_italic-button",function(e,t){var n={TAGS:"i"};M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",r,e.group,t),M.editor_atto.add_button_shortcut({action:"italic",keys:73});var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"italic"):M.editor_atto.remove_widget_highlight(e.elementid,"italic")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
+YUI.add("moodle-atto_italic-button",function(e,t){var n={TAGS:"i"};M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",r,e.group,t),M.editor_atto.add_button_shortcut({action:"italic",keys:73}),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"italic"):M.editor_atto.remove_widget_highlight(e.elementid,"italic")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
diff --git a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
index ed5f947505e..5c095951e0f 100644
--- a/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
+++ b/lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js
@@ -50,8 +50,7 @@ M.atto_italic = M.atto_italic || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'italic');
} else {
diff --git a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
index b6310e7a95e..97db19d4a88 100644
--- a/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/italic/yui/src/button/js/button.js
@@ -48,8 +48,7 @@ M.atto_italic = M.atto_italic || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'italic');
} else {
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
index 9e87f788a27..ecc840b6eef 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
@@ -99,8 +99,7 @@ M.atto_link = M.atto_link || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'link');
} else {
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
index f3e56775b7a..bdd8e03930e 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_link-button",function(e,t){var n={TAGS:"a"};M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){var t=M.util.image_url("e/insert_edit_link","core");M.editor_atto.add_toolbar_button(e.elementid,"link",t,e.group,this.display_chooser);var r=M.editor_atto.get_editable_node(e.elementid);r.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"link"):M.editor_atto.remove_widget_highlight(e.elementid,"link")})},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),M.editor_atto.can_show_filepicker(t,"link")&&r.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_link-button",function(e,t){var n={TAGS:"a"};M.atto_link=M.atto_link||{dialogue:null,selection:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_link.selection=M.editor_atto.get_selection();if(M.atto_link.selection!==!1&&!M.atto_link.selection.collapsed){var n;M.atto_link.dialogue?n=M.atto_link.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_link.get_form_content(t)),n.set("headerContent",M.util.get_string("createlink","atto_link")),M.atto_link.resolve_anchors(),n.show(),M.atto_link.dialogue=n}},init:function(e){var t=M.util.image_url("e/insert_edit_link","core");M.editor_atto.add_toolbar_button(e.elementid,"link",t,e.group,this.display_chooser),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"link"):M.editor_atto.remove_widget_highlight(e.elementid,"link")})},resolve_anchors:function(){var t=M.editor_atto.get_selection_parent_node(),n,r,i;if(!t)return;n=M.atto_link.find_selected_anchors(e.one(t)),n.length>0&&(r=n[0],M.atto_link.selection=M.editor_atto.get_selection_from_node(r),i=r.getAttribute("href"),target=r.getAttribute("target"),i!==""&&e.one("#atto_link_urlentry").set("value",i),target==="_blank"?e.one("#atto_link_openinnewwindow").set("checked","checked"):e.one("#atto_link_openinnewwindow").set("checked",""))},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"link",M.atto_link.filepicker_callback)},filepicker_callback:function(e){M.atto_link.dialogue.hide(),e.url!==""&&(M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,e.url))},set_link:function(t,n){var r,i,s,o,u;t.preventDefault(),M.atto_link.dialogue.hide(),r=t.currentTarget.ancestor(".atto_form").one("input[type=url]"),u=r.get("value");if(u!==""){M.editor_atto.set_selection(M.atto_link.selection),document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,u),s=M.editor_atto.get_selection_parent_node();if(!s)return;o=M.atto_link.find_selected_anchors(e.one(s)),e.Array.each(o,function(e){i=t.currentTarget.ancestor(".atto_form").one("input[type=checkbox]"),i.get("checked")?e.setAttribute("target","_blank"):e.removeAttribute("target")}),M.editor_atto.text_updated(n)}},find_selected_anchors:function(e){var t=e.get("tagName"),n,r;return t&&t.toLowerCase()==="a"?[e]:(r=[],e.all("a").each(function(e){!n&&M.editor_atto.selection_contains_node(e)&&r.push(e)}),r.length>0?r:(n=e.ancestor("a"),n?[n]:[]))},get_form_content:function(t){var n='";var r=e.Node.create(n);return r.one("#atto_link_urlentrysubmit").on("click",M.atto_link.set_link,this,t),M.editor_atto.can_show_filepicker(t,"link")&&r.one("#openlinkbrowser").on("click",M.atto_link.open_filepicker),r}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
index 9e87f788a27..ecc840b6eef 100644
--- a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
+++ b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
@@ -99,8 +99,7 @@ M.atto_link = M.atto_link || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'link');
} else {
diff --git a/lib/editor/atto/plugins/link/yui/src/button/js/button.js b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
index 1993300499f..fe0ab67359e 100644
--- a/lib/editor/atto/plugins/link/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
@@ -97,8 +97,7 @@ M.atto_link = M.atto_link || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'link');
} else {
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
index 9befd50f99c..7f82fc3ddd2 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js
@@ -49,8 +49,7 @@ M.atto_orderedlist = M.atto_orderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
} else {
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
index 47e6a7fb47c..cc6a5acd3b5 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_orderedlist-button",function(e,t){var n={TAGS:"ol"};M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/numbered_list","core");M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"orderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"orderedlist")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_orderedlist-button",function(e,t){var n={TAGS:"ol"};M.atto_orderedlist=M.atto_orderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertOrderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/numbered_list","core");M.editor_atto.add_toolbar_button(e.elementid,"orderedlist",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"orderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"orderedlist")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
index 9befd50f99c..7f82fc3ddd2 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js
@@ -49,8 +49,7 @@ M.atto_orderedlist = M.atto_orderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
} else {
diff --git a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
index 3b7cfa35d43..d69a741e355 100644
--- a/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_orderedlist = M.atto_orderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'orderedlist');
} else {
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
index 9509588740e..56a17415f67 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js
@@ -49,8 +49,7 @@ M.atto_strike = M.atto_strike || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'strike');
} else {
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
index 0a1b6980298..76cc7c1b945 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_strike-button",function(e,t){var n={TAGS:"strike"};M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/strikethrough","core");M.editor_atto.add_toolbar_button(e.elementid,"strike",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"strike"):M.editor_atto.remove_widget_highlight(e.elementid,"strike")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_strike-button",function(e,t){var n={TAGS:"strike"};M.atto_strike=M.atto_strike||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("strikeThrough",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/strikethrough","core");M.editor_atto.add_toolbar_button(e.elementid,"strike",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"strike"):M.editor_atto.remove_widget_highlight(e.elementid,"strike")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
index 9509588740e..56a17415f67 100644
--- a/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
+++ b/lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js
@@ -49,8 +49,7 @@ M.atto_strike = M.atto_strike || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'strike');
} else {
diff --git a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
index 326a30c7319..3e41a787843 100644
--- a/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/strike/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_strike = M.atto_strike || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'strike');
} else {
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
index 53574086f47..995afffb80a 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-debug.js
@@ -49,8 +49,7 @@ M.atto_subscript = M.atto_subscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sub');
} else {
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
index a786ff68f7a..f19b79a8b17 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_subscript-button",function(e,t){var n={TAGS:"sub"};M.atto_subscript=M.atto_subscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("subscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/subscript","core");M.editor_atto.add_toolbar_button(e.elementid,"subscript",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sub"):M.editor_atto.remove_widget_highlight(e.elementid,"sub")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_subscript-button",function(e,t){var n={TAGS:"sub"};M.atto_subscript=M.atto_subscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("subscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/subscript","core");M.editor_atto.add_toolbar_button(e.elementid,"subscript",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sub"):M.editor_atto.remove_widget_highlight(e.elementid,"sub")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
index 53574086f47..995afffb80a 100644
--- a/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
+++ b/lib/editor/atto/plugins/subscript/yui/build/moodle-atto_subscript-button/moodle-atto_subscript-button.js
@@ -49,8 +49,7 @@ M.atto_subscript = M.atto_subscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sub');
} else {
diff --git a/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js b/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
index aa6f38082e9..fcb5b3e6184 100644
--- a/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/subscript/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_subscript = M.atto_subscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sub');
} else {
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
index c9248427745..bdbf75dc868 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-debug.js
@@ -49,8 +49,7 @@ M.atto_superscript = M.atto_superscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sup');
} else {
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
index 8ff64ef3a34..f8c62cc350a 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_superscript-button",function(e,t){var n={TAGS:"sup"};M.atto_superscript=M.atto_superscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("superscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/superscript","core");M.editor_atto.add_toolbar_button(e.elementid,"superscript",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sup"):M.editor_atto.remove_widget_highlight(e.elementid,"sup")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_superscript-button",function(e,t){var n={TAGS:"sup"};M.atto_superscript=M.atto_superscript||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("superscript",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/superscript","core");M.editor_atto.add_toolbar_button(e.elementid,"superscript",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"sup"):M.editor_atto.remove_widget_highlight(e.elementid,"sup")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
index c9248427745..bdbf75dc868 100644
--- a/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
+++ b/lib/editor/atto/plugins/superscript/yui/build/moodle-atto_superscript-button/moodle-atto_superscript-button.js
@@ -49,8 +49,7 @@ M.atto_superscript = M.atto_superscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sup');
} else {
diff --git a/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js b/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
index 8374e35bd3d..368c65f9de8 100644
--- a/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/superscript/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_superscript = M.atto_superscript || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'sup');
} else {
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
index d09ff40befc..09385512d0d 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
@@ -531,8 +531,7 @@ M.atto_table = M.atto_table || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'table');
} else {
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
index 5f33edfb65c..d943b6adb5a 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_table-button",function(e,t){var n={TAGS:"table"};M.atto_table=M.atto_table||{dialogue:null,selection:null,menunode:null,controlmenu:null,lasttarget:null,display_chooser:function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_table.selection=M.editor_atto.get_selection();if(M.atto_table.selection!==!1&&!M.atto_table.selection.collapsed){var n;M.atto_table.dialogue?n=M.atto_table.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.render(),n.set("bodyContent",M.atto_table.get_form_content(t)),n.set("headerContent",M.util.get_string("createtable","atto_table")),n.show(),M.atto_table.dialogue=n}},show_menu:function(t,n){var r=!1;t.preventDefault();if(this.controlmenu===null){r=!0;var i="
"+l+" ",M.editor_atto.insert_html_at_focus_point(u),M.editor_atto.text_updated(n)},get_form_content:function(t){var n=e.Node.create('"+""+M.util.get_string("accessibilityhint","atto_table"));return n.one("#atto_table_submit").on("click",M.atto_table.set_table,this,t),n}}},"@VERSION@",{requires:["node","escape","event","event-valuechange"]});
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
index d09ff40befc..09385512d0d 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
@@ -531,8 +531,7 @@ M.atto_table = M.atto_table || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'table');
} else {
diff --git a/lib/editor/atto/plugins/table/yui/src/button/js/button.js b/lib/editor/atto/plugins/table/yui/src/button/js/button.js
index cd0cb0dbf63..29dcf8983b5 100644
--- a/lib/editor/atto/plugins/table/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/table/yui/src/button/js/button.js
@@ -529,8 +529,7 @@ M.atto_table = M.atto_table || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'table');
} else {
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
index b33432adf82..2eb63f9fd89 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
@@ -50,8 +50,7 @@ M.atto_underline = M.atto_underline || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'underline');
} else {
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
index 80c5066b5a8..4a5f5dada6f 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_underline-button",function(e,t){var n={TAGS:"u"};M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",r,e.group,t),M.editor_atto.add_button_shortcut({action:"underline",keys:85});var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"underline"):M.editor_atto.remove_widget_highlight(e.elementid,"underline")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
+YUI.add("moodle-atto_underline-button",function(e,t){var n={TAGS:"u"};M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",r,e.group,t),M.editor_atto.add_button_shortcut({action:"underline",keys:85}),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"underline"):M.editor_atto.remove_widget_highlight(e.elementid,"underline")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
index b33432adf82..2eb63f9fd89 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
@@ -50,8 +50,7 @@ M.atto_underline = M.atto_underline || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'underline');
} else {
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
index d13ff4b5eac..c2f0b3246f5 100644
--- a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
@@ -48,8 +48,7 @@ M.atto_underline = M.atto_underline || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'underline');
} else {
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
index e8eee96e9d2..6ab94d6fb24 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
@@ -49,8 +49,7 @@ M.atto_unlink = M.atto_unlink || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
} else {
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
index be16b4b81f0..980573aa576 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unlink-button",function(e,t){var n={TAGS:"a"};M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/remove_link","core");M.editor_atto.add_toolbar_button(e.elementid,"unlink",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unlink"):M.editor_atto.remove_widget_highlight(e.elementid,"unlink")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unlink-button",function(e,t){var n={TAGS:"a"};M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/remove_link","core");M.editor_atto.add_toolbar_button(e.elementid,"unlink",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unlink"):M.editor_atto.remove_widget_highlight(e.elementid,"unlink")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
index e8eee96e9d2..6ab94d6fb24 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
@@ -49,8 +49,7 @@ M.atto_unlink = M.atto_unlink || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
} else {
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
index b01c8ef8b5e..add917386e7 100644
--- a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_unlink = M.atto_unlink || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unlink');
} else {
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
index 4ee39f03540..cd6916ee3bf 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
@@ -49,8 +49,7 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
} else {
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
index d1c67c188d4..f9d354c1d5d 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unorderedlist-button",function(e,t){var n={TAGS:"ul"};M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bullet_list","core");M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unorderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"unorderedlist")})}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unorderedlist-button",function(e,t){var n={TAGS:"ul"};M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bullet_list","core");M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",r,e.group,t),M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"unorderedlist"):M.editor_atto.remove_widget_highlight(e.elementid,"unorderedlist")})}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
index 4ee39f03540..cd6916ee3bf 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
@@ -49,8 +49,7 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
} else {
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
index d04a38104c2..f882172caa6 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
@@ -47,8 +47,7 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
- var editable = M.editor_atto.get_editable_node(params.elementid);
- editable.on('atto:selectionchanged', function(e) {
+ M.editor_atto.on('atto:selectionchanged', function(e) {
if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
M.editor_atto.add_widget_highlight(e.elementid, 'unorderedlist');
} else {
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index 69288d5c6d2..e74821b9625 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -126,6 +126,38 @@ M.editor_atto = M.editor_atto || {
focusfromclick : false,
+ /**
+ * Set to true after events have been published for M.editor_atto.
+ * @param eventspublished
+ * @type boolean
+ */
+ eventspublished : false,
+
+ /**
+ * A unique identifier for the last selection recorded.
+ * @param lastselection
+ * @type string
+ */
+ lastselection : null,
+
+ /**
+ * The number of selection ranges when the selection was last recorded.
+ * @param lastselectioncount
+ * @type integer
+ */
+ lastselectioncount : 0,
+
+ /**
+ * Set to true after the first editor has been initialised.
+ *
+ * "The first transport is away. The first transport is away."
+ * ―Lieutenant Romas Navander, during the Battle of Hoth
+ *
+ * @param firstinit
+ * @type boolean
+ */
+ firstinit : true,
+
/**
* Toggle a menu.
* @param event e
@@ -597,6 +629,9 @@ M.editor_atto = M.editor_atto || {
* @param object params for this editor instance.
*/
init : function(params) {
+ // Some things we only want to do on the first init.
+ var firstinit = this.firstinit;
+ this.firstinit = false;
var wrapper = Y.Node.create('');
var atto = Y.Node.create('
0) {
this.selections[elementid] = sel;
@@ -959,7 +936,7 @@ M.editor_atto = M.editor_atto || {
/**
* Get the selection object that can be passed back to set_selection.
- * @return range (browser dependent)
+ * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[]
*/
get_selection : function() {
if (window.getSelection) {
@@ -972,7 +949,7 @@ M.editor_atto = M.editor_atto || {
} else if (document.selection) {
// IE < 9
if (document.selection.createRange) {
- return document.selection.createRange();
+ return [document.selection.createRange()];
}
}
return false;
@@ -984,10 +961,10 @@ M.editor_atto = M.editor_atto || {
* @return boolean
*/
selection_contains_node : function(node) {
+ // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed.
var range, sel;
if (window.getSelection) {
sel = window.getSelection();
-
if (sel.containsNode) {
return sel.containsNode(node.getDOMNode(), true);
}
@@ -1008,159 +985,390 @@ M.editor_atto = M.editor_atto || {
* @return {Boolean}
*/
selection_filter_matches : function(elementid, selector, selectednodes) {
- var result = true;
-
if (!selectednodes) {
// Find this because it was not passed as a param.
selectednodes = M.editor_atto.get_selected_nodes(elementid);
}
- selector = '.' + CSS.CONTENT + ' ' + selector;
-
- if (selectednodes.size() === 0) {
- return false;
- }
- selectednodes.each(function(node) {
- if (!node.ancestor(selector, true)) {
- result = false;
+ var allmatch = selectednodes.size() > 0;
+ selectednodes.each(function(node){
+ // Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing.
+ if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
+ allmatch = false;
}
- });
- return result;
- },
-
- /**
- * Returns a list of nodes that are ancestors of the selection nodes,
- * and match the specified css selector (and are contained within the editable div).
- *
- * @param {String} elementid
- * @param {String} selector
- * @return Y.NodeList
- */
- selection_filter : function(elementid, selector) {
- var selectednodes = M.editor_atto.get_selected_nodes(elementid);
- var result = new Y.NodeList();
- selector = '.' + CSS.CONTENT + ' ' + selector;
- selectednodes.each(function(node) {
- node.ancestors(selector, true).each(function(match) {
- if (result.indexOf(match) === -1) {
- result.push(match);
- }
- });
- });
- return result;
+ }, this);
+ return allmatch;
},
/**
* Get the deepest possible list of nodes in the current selection.
+ *
+ * Presently this method contains logic to work out what nodes are "selected" by the user.
+ * The user selection may take the form of:
+ * - text : selected text within a single node, such as double clicking a word.
+ * - A node : the user has clicked an image or media object that is selectable by clicking upon an element.
+ * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include
+ * both text and/or nodes.
+ * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node
+ * containing selected content.
+ * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording
+ * all nodes that appear after the start node and before the end node.
+ *
+ * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by
+ * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property
+ * before returning the nodes.
+ *
* @param {String} elementid
* @return Y.NodeList
*/
get_selected_nodes : function(elementid) {
+ var nodes = [],
+ results = new Y.NodeList(),
+ points = [],
+ range,
+ i,
+ sel,
+ start,
+ end,
+ parent,
+ values;
- var editable = M.editor_atto.get_editable_node(elementid);
- var startnode = M.editor_atto.get_selection_start_container();
- var endnode = M.editor_atto.get_selection_end_container();
-
- if (!startnode || !endnode) {
- return new Y.NodeList();
- }
-
- /**
- * Recursive function to walk the dom tree between 2 nodes and build
- * a list of leaf nodes.
- * @param {Y.Node} node - The current node.
- * @param {Y.Node} endnode - The node to stop at.
- * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
- * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
- * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
- * @return {Y.NodeList} The list of leaf nodes found.
- */
- var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
- var leafnodes = new Y.NodeList();
- if (endnode === node) {
- // If there are child nodes and we didn't come from a child node, then skip this end condition.
- if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
- // If the end node is a leaf - return it.
- if (!node.hasChildNodes()) {
- leafnodes.push(node);
+ // The window.getSelection method is available from IE9 onwards and for all other browsers.
+ if (window.getSelection) {
+ sel = window.getSelection();
+ for (i = 0; i < sel.rangeCount; i++) {
+ range = sel.getRangeAt(i);
+ start = new Y.Node(range.startContainer);
+ end = new Y.Node(range.endContainer);
+ if (start.compareTo(end)) {
+ // The start and end node are the same node. Only one node is selected. Easy!
+ nodes.push(end);
+ } else {
+ // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out.
+ // For the time being record them as a pair of points along with the common ancestor.
+ // First up we need to check the order of the nodes, we need them in the order they appear in the DOM
+ // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order.
+ values = this._fix_selected_nodes_from_range(
+ start,
+ range.startOffset,
+ end,
+ range.endOffset
+ );
+ // Ok start and end are in order. Yay!
+ start = values[0];
+ end = values[2];
+ // We calculate the common ancestor here as we should be able to get it from the range object.
+ if (range.commonAncestorContainer) {
+ parent = range.commonAncestorContainer;
+ } else if (range.parentElement) {
+ parent = range.parentElement();
+ } else {
+ parent = this._get_common_parent_from_nodes(start, end);
}
- // We reached the end of the selection.
- return leafnodes;
+ points.push([
+ start,
+ end,
+ parent
+ ]);
}
}
-
- // Node is not a leaf, look at it's children.
- if (node.hasChildNodes()) {
- var children = node.get('childNodes');
- // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
- var child = children.item(skipchildren);
- if (child) {
- // There was a child node, find all the leaf nodes in it.
- leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ } else if (document.selection && document.selection.createRange) {
+ // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11).
+ // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8.
+ // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx
+ range = document.selection.createRange();
+ // Create range will return either TextRange object from the current text selection, or a controlRange
+ // collection from the control selection.
+ if (document.selection.type === "Control") {
+ // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us.
+ for (i = 0; i < range.length; i++) {
+ nodes.push(new Y.Node(range(i)));
}
} else {
- // This node has no children - so save it.
- leafnodes.push(node);
+ // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the
+ // selection.
+ nodes.push(new Y.Node(range.parentElement()));
}
- // If we hit the bounding node - this is an end condition.
- if (node === boundingnode) {
- return leafnodes;
+ }
+ // Ok we have two array as this point.
+ // - Nodes is an array containing nodes that are currently highlighted by selection.
+ // - Points is an array containing start, end, and parent nodes.
+ // For each item in the points array we need to find all nodes containing selected content.
+ for (i in points) {
+ nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i]));
+ }
+ // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area.
+ for (i in nodes) {
+ if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) {
+ results.push(nodes[i]);
}
- // We have added all the nodes in this branch, look at the next child of the parent.
- var parent = node.ancestor();
- if (parent) {
- var currentindex = parent.get('childNodes').indexOf(node);
- leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
- }
- return leafnodes;
- };
-
- // Kick off the recursive function.
- return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ }
+ return results;
},
/**
- * Get the first node that contains the current selection.
- * @return DOMNode or false
+ * Returns true if the selection has changed since this method was last called.
+ *
+ * If the selection has changed we also fire the custom event atto:selectionchanged
+ *
+ * @method _has_selection_changed
+ * @private
+ * @param {EventFacade} e
+ * @returns {Boolean}
*/
- get_selection_start_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
+ _has_selection_changed : function(e) {
+ var sel,
+ i,
+ range,
+ bits = [],
+ selectedstr = '',
+ selectioncount = 0;
+ if (window.getSelection) {
+ // Hoorah - working with the alternative is horrid..
+ sel = window.getSelection();
+ selectioncount = sel.rangeCount;
+ for (i = 0; i < selectioncount; i++) {
+ range = sel.getRangeAt(i);
+ bits.push(range.startOffset);
+ bits.push(range.endOffset);
+ bits.push(this._get_node_identifier(range.startContainer));
+ bits.push(this._get_node_identifier(range.endContainer));
+ bits.push('&');
+ }
+ } else if (document.selection && document.selection.createRange) {
+ range = document.selection.createRange();
+ if (range !== null) {
+ if (document.selection.type === "Control") {
+ // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx
+ selectioncount = range.length;
+ for (i = 0; i < selectioncount; i++) {
+ bits.push(this._get_node_identifier(range(i)));
+ }
+ } else {
+ // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
+ selectioncount = 1;
+ bits.push(this._get_node_identifier(range.parentElement()));
+ }
+ }
}
-
- if (selection.startContainer) {
- return Y.one(selection.startContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(true);
- return Y.one(range.parentElement());
- }
- },
-
- /**
- * Get the last node that contains the current selection.
- * @return DOMNode or false
- */
- get_selection_end_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
- }
-
- if (selection.endContainer) {
- return Y.one(selection.endContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(false);
- return Y.one(range.parentElement());
+ selectedstr = bits.join('|');
+ if (this.lastselection !== selectedstr) {
+ this.lastselection = selectedstr;
+ this.lastselectioncount = selectioncount;
+ return this._fire_selectionchanged(e);
}
return false;
},
+ /**
+ * Fires the atto:selectionchanged event.
+ *
+ * When the selectionchanged event gets fired three arguments are given:
+ * - event : the original event that lead to this event being fired.
+ * - elementid : the editor elementid for which this event is being fired.
+ * - selectednodes : an array containing nodes that are entirely selected of contain partially selected content.
+ *
+ * @method _fire_selectionchanged
+ * @private
+ * @param {EventFacade} e
+ */
+ _fire_selectionchanged : function(e) {
+ var editableid = e.currentTarget.get('id'),
+ elementid = editableid.substring(0, editableid.length - 8),
+ selectednodes = this.get_selected_nodes(elementid);
+ this.fire('atto:selectionchanged', {
+ event : e,
+ elementid : elementid,
+ selectedNodes : selectednodes
+ });
+ },
+
+ /**
+ * Returns the best identifier for the given node that can be found. May not be unique.
+ *
+ * @method _get_node_identifier
+ * @private
+ * @param {Node} node
+ * @returns {string}
+ */
+ _get_node_identifier : function(node) {
+ var uid = (node.nodeType === 3) ? node.length : node.id;
+ return uid || node.className || node.nodeName;
+ },
+
+ /**
+ * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset
+ *
+ * Note about offsets:
+ * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the
+ * start of the container to the boundary point of the Range.
+ * For other Node types, the offset is the number of child nodes between the start of the container and the boundary
+ * point of the Range.
+ *
+ * @method _fix_selected_nodes_from_range
+ * @private
+ * @param {Node} a
+ * @param {integer} aoffset
+ * @param {Node} b
+ * @param {integer} boffset
+ * @returns {[Node, integer, Node, integer]}
+ */
+ _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) {
+ var first,
+ firstoffset,
+ second,
+ secondoffset,
+ searchfunc,
+ acheckend = true,
+ bcheckend = true,
+ checkend = true;
+
+ if (a.get('nodeType') === 1) {
+ a = a.get('childNodes').item(aoffset);
+ acheckend = false;
+ }
+ if (b.get('nodeType') === 1 && boffset > 0) {
+ b = b.get('childNodes').item(boffset - 1);
+ bcheckend = false;
+ }
+ if (a._yuid === b._yuid) {
+ if (aoffset < boffset) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ } else {
+ if (a.contains(b)) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else if (b.contains(a)) {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ } else {
+ searchfunc = function find_first(nodes, x, y) {
+ var i, result;
+ for (i in nodes) {
+ if (nodes[i] === x.getDOMNode()) {
+ return x;
+ } else if (nodes[i] === y.getDOMNode()) {
+ return y;
+ } else if (nodes[i].nodeType === 1) {
+ result = find_first(nodes[i].childNodes, x, y);
+ if (result !== false) {
+ return result;
+ }
+ }
+ }
+ return false;
+ };
+ first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a;
+ if (first === a) {
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ }
+ if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) {
+ second = second.previous();
+ }
+ }
+ return [first, firstoffset, second, secondoffset];
+ },
+
+ /**
+ * Returns an array containing all of the nodes in the Dom between the two reference nodes.
+ *
+ * @method _get_all_nodes_between
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @param {Node} parent The common parent of a and b
+ * @returns {Node[]}
+ */
+ _get_all_nodes_between : function(a, b, parent) {
+ var startfound = false,
+ endfound = false,
+ nodes = [],
+ parenthasselectedtext = false;
+ if (a === b || a.contains(b)) {
+ return [a];
+ }
+ if (b.contains(a)) {
+ return [b];
+ }
+ if (!parent) {
+ return [a, b];
+ }
+
+ nodes.push(a);
+ parent.get('childNodes').each(function(node) {
+ if (endfound) {
+ return;
+ }
+ if (!startfound && node === a) {
+ startfound = true;
+ } else if (startfound && !endfound) {
+ if (node === b) {
+ endfound = true;
+ } else {
+ if (node.get('nodeType') === 3) {
+ parenthasselectedtext = true;
+ } else {
+ nodes.push(node);
+ nodes = nodes.compact(node.all('*'));
+ }
+ }
+ }
+ });
+ nodes.push(b);
+
+ if (parenthasselectedtext) {
+ nodes.unshift(parent);
+ }
+
+ return nodes;
+ },
+
+ /**
+ * Returns the common parent of the two given nodes.
+ *
+ * @method _get_common_parent_from_nodes
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @returns {Node}
+ */
+ _get_common_parent_from_nodes : function(a, b) {
+ var aparents = a.ancestors('*', false, '.' + CSS.CONTENT),
+ bparents = b.ancestors('*', false, '.' + CSS.CONTENT),
+ node;
+ while ((node = aparents.pop())) {
+ if (bparents.indexOf(node) !== -1) {
+ return node;
+ }
+ }
+ return a.ancestor('.' + CSS.CONTENT);
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
@@ -1180,17 +1388,6 @@ M.editor_atto = M.editor_atto || {
return false;
},
- /**
- * Get the list of child nodes of the selection.
- * @return DOMNode[]
- */
- get_selection_text : function() {
- var selection = M.editor_atto.get_selection();
- if (selection.length > 0 && selection[0].cloneContents) {
- return selection[0].cloneContents();
- }
- },
-
/**
* Set the current selection. Used to restore a selection.
*/
@@ -1433,6 +1630,9 @@ M.editor_atto = M.editor_atto || {
}
}
};
+
+// The editor_atto is publishing custom events that can be subscribed to.
+Y.augment(M.editor_atto, Y.EventTarget);
var CONTROLMENU_NAME = "Controlmenu",
CONTROLMENU;
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
index c5495f95854..9d345e74c13 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -1,4 +1,4 @@
-YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n
")&&n.setHTML(""),n.getHTML()},keyboard_navigation:function(t,n){var r,i,s,o,u=M.editor_atto.get_toolbar_node(n);t.preventDefault(),r=u.all("empty"),u.all(".atto_group").each(function(e){e.hasAttribute("hidden")||(r=r.concat(e.all("button")))}),s=u.getAttribute("aria-activedescendant");if(!s)return;i=e.one("#"+s),i.setAttribute("tabindex","-1"),o=r.indexOf(i),t.keyCode===37?(o--,o<0&&(o=r.size()-1)):(o++,o>=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t,n,r){e.use("core_filepicker",function(e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?
+(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},save_selection:function(e){var t=e.elementid;if(t&&this.is_active(t)){var n=this.get_selection();n.length>0&&(this.selections[t]=n)}},restore_selection:function(e,t){this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0;return n.each(function(n){if(!r||!n.ancestor(t,!0,"#"+e+"editable"))r=!1},this),r},get_selected_nodes:function(t){var n=[],r=new e.NodeList,i=[],s,o,u,a,f,l,c;if(window.getSelection){u=window.getSelection();for(o=0;o0&&(n=n.get("childNodes").item(r-1),l=!1),e._yuid===n._yuid?t"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}},insert_html_at_focus_point:function(e){if(document.selection&&document.selection.createRange){var t=document.selection.createRange();t.pasteHTML&&t.pasteHTML(e)}else document
+.execCommand("insertHTML",!1,e)},toggle_inline_selection_class:function(t,n){var r=M.editor_atto.get_selection_parent_node(),i=e.one("body"),s,o=[],u,a,f,l=0;if(!r)return;document.execCommand("fontname",!1,M.editor_atto.PLACEHOLDER_FONTNAME),s=i.all(M.editor_atto.ALL_NODES_SELECTOR),s.each(function(t,n){t.getStyle(M.editor_atto.FONT_FAMILY)===M.editor_atto.PLACEHOLDER_FONTNAME&&(t.setStyle(M.editor_atto.FONT_FAMILY,""),t.compareTo(i)||o.push(e.Node.getDOMNode(s.item(n))))});for(l=0;l"),f.append(o[l].innerHTML),a.replace(f),a=f);for(var c=0;c"),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","event-custom","yui-throttle","moodle-core-notification-dialogue"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
index 69288d5c6d2..e74821b9625 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -126,6 +126,38 @@ M.editor_atto = M.editor_atto || {
focusfromclick : false,
+ /**
+ * Set to true after events have been published for M.editor_atto.
+ * @param eventspublished
+ * @type boolean
+ */
+ eventspublished : false,
+
+ /**
+ * A unique identifier for the last selection recorded.
+ * @param lastselection
+ * @type string
+ */
+ lastselection : null,
+
+ /**
+ * The number of selection ranges when the selection was last recorded.
+ * @param lastselectioncount
+ * @type integer
+ */
+ lastselectioncount : 0,
+
+ /**
+ * Set to true after the first editor has been initialised.
+ *
+ * "The first transport is away. The first transport is away."
+ * ―Lieutenant Romas Navander, during the Battle of Hoth
+ *
+ * @param firstinit
+ * @type boolean
+ */
+ firstinit : true,
+
/**
* Toggle a menu.
* @param event e
@@ -597,6 +629,9 @@ M.editor_atto = M.editor_atto || {
* @param object params for this editor instance.
*/
init : function(params) {
+ // Some things we only want to do on the first init.
+ var firstinit = this.firstinit;
+ this.firstinit = false;
var wrapper = Y.Node.create('');
var atto = Y.Node.create('
0) {
this.selections[elementid] = sel;
@@ -959,7 +936,7 @@ M.editor_atto = M.editor_atto || {
/**
* Get the selection object that can be passed back to set_selection.
- * @return range (browser dependent)
+ * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[]
*/
get_selection : function() {
if (window.getSelection) {
@@ -972,7 +949,7 @@ M.editor_atto = M.editor_atto || {
} else if (document.selection) {
// IE < 9
if (document.selection.createRange) {
- return document.selection.createRange();
+ return [document.selection.createRange()];
}
}
return false;
@@ -984,10 +961,10 @@ M.editor_atto = M.editor_atto || {
* @return boolean
*/
selection_contains_node : function(node) {
+ // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed.
var range, sel;
if (window.getSelection) {
sel = window.getSelection();
-
if (sel.containsNode) {
return sel.containsNode(node.getDOMNode(), true);
}
@@ -1008,159 +985,390 @@ M.editor_atto = M.editor_atto || {
* @return {Boolean}
*/
selection_filter_matches : function(elementid, selector, selectednodes) {
- var result = true;
-
if (!selectednodes) {
// Find this because it was not passed as a param.
selectednodes = M.editor_atto.get_selected_nodes(elementid);
}
- selector = '.' + CSS.CONTENT + ' ' + selector;
-
- if (selectednodes.size() === 0) {
- return false;
- }
- selectednodes.each(function(node) {
- if (!node.ancestor(selector, true)) {
- result = false;
+ var allmatch = selectednodes.size() > 0;
+ selectednodes.each(function(node){
+ // Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing.
+ if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
+ allmatch = false;
}
- });
- return result;
- },
-
- /**
- * Returns a list of nodes that are ancestors of the selection nodes,
- * and match the specified css selector (and are contained within the editable div).
- *
- * @param {String} elementid
- * @param {String} selector
- * @return Y.NodeList
- */
- selection_filter : function(elementid, selector) {
- var selectednodes = M.editor_atto.get_selected_nodes(elementid);
- var result = new Y.NodeList();
- selector = '.' + CSS.CONTENT + ' ' + selector;
- selectednodes.each(function(node) {
- node.ancestors(selector, true).each(function(match) {
- if (result.indexOf(match) === -1) {
- result.push(match);
- }
- });
- });
- return result;
+ }, this);
+ return allmatch;
},
/**
* Get the deepest possible list of nodes in the current selection.
+ *
+ * Presently this method contains logic to work out what nodes are "selected" by the user.
+ * The user selection may take the form of:
+ * - text : selected text within a single node, such as double clicking a word.
+ * - A node : the user has clicked an image or media object that is selectable by clicking upon an element.
+ * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include
+ * both text and/or nodes.
+ * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node
+ * containing selected content.
+ * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording
+ * all nodes that appear after the start node and before the end node.
+ *
+ * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by
+ * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property
+ * before returning the nodes.
+ *
* @param {String} elementid
* @return Y.NodeList
*/
get_selected_nodes : function(elementid) {
+ var nodes = [],
+ results = new Y.NodeList(),
+ points = [],
+ range,
+ i,
+ sel,
+ start,
+ end,
+ parent,
+ values;
- var editable = M.editor_atto.get_editable_node(elementid);
- var startnode = M.editor_atto.get_selection_start_container();
- var endnode = M.editor_atto.get_selection_end_container();
-
- if (!startnode || !endnode) {
- return new Y.NodeList();
- }
-
- /**
- * Recursive function to walk the dom tree between 2 nodes and build
- * a list of leaf nodes.
- * @param {Y.Node} node - The current node.
- * @param {Y.Node} endnode - The node to stop at.
- * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
- * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
- * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
- * @return {Y.NodeList} The list of leaf nodes found.
- */
- var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
- var leafnodes = new Y.NodeList();
- if (endnode === node) {
- // If there are child nodes and we didn't come from a child node, then skip this end condition.
- if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
- // If the end node is a leaf - return it.
- if (!node.hasChildNodes()) {
- leafnodes.push(node);
+ // The window.getSelection method is available from IE9 onwards and for all other browsers.
+ if (window.getSelection) {
+ sel = window.getSelection();
+ for (i = 0; i < sel.rangeCount; i++) {
+ range = sel.getRangeAt(i);
+ start = new Y.Node(range.startContainer);
+ end = new Y.Node(range.endContainer);
+ if (start.compareTo(end)) {
+ // The start and end node are the same node. Only one node is selected. Easy!
+ nodes.push(end);
+ } else {
+ // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out.
+ // For the time being record them as a pair of points along with the common ancestor.
+ // First up we need to check the order of the nodes, we need them in the order they appear in the DOM
+ // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order.
+ values = this._fix_selected_nodes_from_range(
+ start,
+ range.startOffset,
+ end,
+ range.endOffset
+ );
+ // Ok start and end are in order. Yay!
+ start = values[0];
+ end = values[2];
+ // We calculate the common ancestor here as we should be able to get it from the range object.
+ if (range.commonAncestorContainer) {
+ parent = range.commonAncestorContainer;
+ } else if (range.parentElement) {
+ parent = range.parentElement();
+ } else {
+ parent = this._get_common_parent_from_nodes(start, end);
}
- // We reached the end of the selection.
- return leafnodes;
+ points.push([
+ start,
+ end,
+ parent
+ ]);
}
}
-
- // Node is not a leaf, look at it's children.
- if (node.hasChildNodes()) {
- var children = node.get('childNodes');
- // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
- var child = children.item(skipchildren);
- if (child) {
- // There was a child node, find all the leaf nodes in it.
- leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ } else if (document.selection && document.selection.createRange) {
+ // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11).
+ // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8.
+ // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx
+ range = document.selection.createRange();
+ // Create range will return either TextRange object from the current text selection, or a controlRange
+ // collection from the control selection.
+ if (document.selection.type === "Control") {
+ // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us.
+ for (i = 0; i < range.length; i++) {
+ nodes.push(new Y.Node(range(i)));
}
} else {
- // This node has no children - so save it.
- leafnodes.push(node);
+ // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the
+ // selection.
+ nodes.push(new Y.Node(range.parentElement()));
}
- // If we hit the bounding node - this is an end condition.
- if (node === boundingnode) {
- return leafnodes;
+ }
+ // Ok we have two array as this point.
+ // - Nodes is an array containing nodes that are currently highlighted by selection.
+ // - Points is an array containing start, end, and parent nodes.
+ // For each item in the points array we need to find all nodes containing selected content.
+ for (i in points) {
+ nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i]));
+ }
+ // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area.
+ for (i in nodes) {
+ if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) {
+ results.push(nodes[i]);
}
- // We have added all the nodes in this branch, look at the next child of the parent.
- var parent = node.ancestor();
- if (parent) {
- var currentindex = parent.get('childNodes').indexOf(node);
- leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
- }
- return leafnodes;
- };
-
- // Kick off the recursive function.
- return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ }
+ return results;
},
/**
- * Get the first node that contains the current selection.
- * @return DOMNode or false
+ * Returns true if the selection has changed since this method was last called.
+ *
+ * If the selection has changed we also fire the custom event atto:selectionchanged
+ *
+ * @method _has_selection_changed
+ * @private
+ * @param {EventFacade} e
+ * @returns {Boolean}
*/
- get_selection_start_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
+ _has_selection_changed : function(e) {
+ var sel,
+ i,
+ range,
+ bits = [],
+ selectedstr = '',
+ selectioncount = 0;
+ if (window.getSelection) {
+ // Hoorah - working with the alternative is horrid..
+ sel = window.getSelection();
+ selectioncount = sel.rangeCount;
+ for (i = 0; i < selectioncount; i++) {
+ range = sel.getRangeAt(i);
+ bits.push(range.startOffset);
+ bits.push(range.endOffset);
+ bits.push(this._get_node_identifier(range.startContainer));
+ bits.push(this._get_node_identifier(range.endContainer));
+ bits.push('&');
+ }
+ } else if (document.selection && document.selection.createRange) {
+ range = document.selection.createRange();
+ if (range !== null) {
+ if (document.selection.type === "Control") {
+ // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx
+ selectioncount = range.length;
+ for (i = 0; i < selectioncount; i++) {
+ bits.push(this._get_node_identifier(range(i)));
+ }
+ } else {
+ // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
+ selectioncount = 1;
+ bits.push(this._get_node_identifier(range.parentElement()));
+ }
+ }
}
-
- if (selection.startContainer) {
- return Y.one(selection.startContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(true);
- return Y.one(range.parentElement());
- }
- },
-
- /**
- * Get the last node that contains the current selection.
- * @return DOMNode or false
- */
- get_selection_end_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
- }
-
- if (selection.endContainer) {
- return Y.one(selection.endContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(false);
- return Y.one(range.parentElement());
+ selectedstr = bits.join('|');
+ if (this.lastselection !== selectedstr) {
+ this.lastselection = selectedstr;
+ this.lastselectioncount = selectioncount;
+ return this._fire_selectionchanged(e);
}
return false;
},
+ /**
+ * Fires the atto:selectionchanged event.
+ *
+ * When the selectionchanged event gets fired three arguments are given:
+ * - event : the original event that lead to this event being fired.
+ * - elementid : the editor elementid for which this event is being fired.
+ * - selectednodes : an array containing nodes that are entirely selected of contain partially selected content.
+ *
+ * @method _fire_selectionchanged
+ * @private
+ * @param {EventFacade} e
+ */
+ _fire_selectionchanged : function(e) {
+ var editableid = e.currentTarget.get('id'),
+ elementid = editableid.substring(0, editableid.length - 8),
+ selectednodes = this.get_selected_nodes(elementid);
+ this.fire('atto:selectionchanged', {
+ event : e,
+ elementid : elementid,
+ selectedNodes : selectednodes
+ });
+ },
+
+ /**
+ * Returns the best identifier for the given node that can be found. May not be unique.
+ *
+ * @method _get_node_identifier
+ * @private
+ * @param {Node} node
+ * @returns {string}
+ */
+ _get_node_identifier : function(node) {
+ var uid = (node.nodeType === 3) ? node.length : node.id;
+ return uid || node.className || node.nodeName;
+ },
+
+ /**
+ * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset
+ *
+ * Note about offsets:
+ * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the
+ * start of the container to the boundary point of the Range.
+ * For other Node types, the offset is the number of child nodes between the start of the container and the boundary
+ * point of the Range.
+ *
+ * @method _fix_selected_nodes_from_range
+ * @private
+ * @param {Node} a
+ * @param {integer} aoffset
+ * @param {Node} b
+ * @param {integer} boffset
+ * @returns {[Node, integer, Node, integer]}
+ */
+ _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) {
+ var first,
+ firstoffset,
+ second,
+ secondoffset,
+ searchfunc,
+ acheckend = true,
+ bcheckend = true,
+ checkend = true;
+
+ if (a.get('nodeType') === 1) {
+ a = a.get('childNodes').item(aoffset);
+ acheckend = false;
+ }
+ if (b.get('nodeType') === 1 && boffset > 0) {
+ b = b.get('childNodes').item(boffset - 1);
+ bcheckend = false;
+ }
+ if (a._yuid === b._yuid) {
+ if (aoffset < boffset) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ } else {
+ if (a.contains(b)) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else if (b.contains(a)) {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ } else {
+ searchfunc = function find_first(nodes, x, y) {
+ var i, result;
+ for (i in nodes) {
+ if (nodes[i] === x.getDOMNode()) {
+ return x;
+ } else if (nodes[i] === y.getDOMNode()) {
+ return y;
+ } else if (nodes[i].nodeType === 1) {
+ result = find_first(nodes[i].childNodes, x, y);
+ if (result !== false) {
+ return result;
+ }
+ }
+ }
+ return false;
+ };
+ first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a;
+ if (first === a) {
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ }
+ if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) {
+ second = second.previous();
+ }
+ }
+ return [first, firstoffset, second, secondoffset];
+ },
+
+ /**
+ * Returns an array containing all of the nodes in the Dom between the two reference nodes.
+ *
+ * @method _get_all_nodes_between
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @param {Node} parent The common parent of a and b
+ * @returns {Node[]}
+ */
+ _get_all_nodes_between : function(a, b, parent) {
+ var startfound = false,
+ endfound = false,
+ nodes = [],
+ parenthasselectedtext = false;
+ if (a === b || a.contains(b)) {
+ return [a];
+ }
+ if (b.contains(a)) {
+ return [b];
+ }
+ if (!parent) {
+ return [a, b];
+ }
+
+ nodes.push(a);
+ parent.get('childNodes').each(function(node) {
+ if (endfound) {
+ return;
+ }
+ if (!startfound && node === a) {
+ startfound = true;
+ } else if (startfound && !endfound) {
+ if (node === b) {
+ endfound = true;
+ } else {
+ if (node.get('nodeType') === 3) {
+ parenthasselectedtext = true;
+ } else {
+ nodes.push(node);
+ nodes = nodes.compact(node.all('*'));
+ }
+ }
+ }
+ });
+ nodes.push(b);
+
+ if (parenthasselectedtext) {
+ nodes.unshift(parent);
+ }
+
+ return nodes;
+ },
+
+ /**
+ * Returns the common parent of the two given nodes.
+ *
+ * @method _get_common_parent_from_nodes
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @returns {Node}
+ */
+ _get_common_parent_from_nodes : function(a, b) {
+ var aparents = a.ancestors('*', false, '.' + CSS.CONTENT),
+ bparents = b.ancestors('*', false, '.' + CSS.CONTENT),
+ node;
+ while ((node = aparents.pop())) {
+ if (bparents.indexOf(node) !== -1) {
+ return node;
+ }
+ }
+ return a.ancestor('.' + CSS.CONTENT);
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
@@ -1180,17 +1388,6 @@ M.editor_atto = M.editor_atto || {
return false;
},
- /**
- * Get the list of child nodes of the selection.
- * @return DOMNode[]
- */
- get_selection_text : function() {
- var selection = M.editor_atto.get_selection();
- if (selection.length > 0 && selection[0].cloneContents) {
- return selection[0].cloneContents();
- }
- },
-
/**
* Set the current selection. Used to restore a selection.
*/
@@ -1433,6 +1630,9 @@ M.editor_atto = M.editor_atto || {
}
}
};
+
+// The editor_atto is publishing custom events that can be subscribed to.
+Y.augment(M.editor_atto, Y.EventTarget);
var CONTROLMENU_NAME = "Controlmenu",
CONTROLMENU;
diff --git a/lib/editor/atto/yui/src/editor/js/editor.js b/lib/editor/atto/yui/src/editor/js/editor.js
index cb5dbc39e8d..11f575543d7 100755
--- a/lib/editor/atto/yui/src/editor/js/editor.js
+++ b/lib/editor/atto/yui/src/editor/js/editor.js
@@ -124,6 +124,38 @@ M.editor_atto = M.editor_atto || {
focusfromclick : false,
+ /**
+ * Set to true after events have been published for M.editor_atto.
+ * @param eventspublished
+ * @type boolean
+ */
+ eventspublished : false,
+
+ /**
+ * A unique identifier for the last selection recorded.
+ * @param lastselection
+ * @type string
+ */
+ lastselection : null,
+
+ /**
+ * The number of selection ranges when the selection was last recorded.
+ * @param lastselectioncount
+ * @type integer
+ */
+ lastselectioncount : 0,
+
+ /**
+ * Set to true after the first editor has been initialised.
+ *
+ * "The first transport is away. The first transport is away."
+ * ―Lieutenant Romas Navander, during the Battle of Hoth
+ *
+ * @param firstinit
+ * @type boolean
+ */
+ firstinit : true,
+
/**
* Toggle a menu.
* @param event e
@@ -595,6 +627,9 @@ M.editor_atto = M.editor_atto || {
* @param object params for this editor instance.
*/
init : function(params) {
+ // Some things we only want to do on the first init.
+ var firstinit = this.firstinit;
+ this.firstinit = false;
var wrapper = Y.Node.create('');
var atto = Y.Node.create('
0) {
this.selections[elementid] = sel;
@@ -957,7 +934,7 @@ M.editor_atto = M.editor_atto || {
/**
* Get the selection object that can be passed back to set_selection.
- * @return range (browser dependent)
+ * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[]
*/
get_selection : function() {
if (window.getSelection) {
@@ -970,7 +947,7 @@ M.editor_atto = M.editor_atto || {
} else if (document.selection) {
// IE < 9
if (document.selection.createRange) {
- return document.selection.createRange();
+ return [document.selection.createRange()];
}
}
return false;
@@ -982,10 +959,10 @@ M.editor_atto = M.editor_atto || {
* @return boolean
*/
selection_contains_node : function(node) {
+ // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed.
var range, sel;
if (window.getSelection) {
sel = window.getSelection();
-
if (sel.containsNode) {
return sel.containsNode(node.getDOMNode(), true);
}
@@ -1006,159 +983,390 @@ M.editor_atto = M.editor_atto || {
* @return {Boolean}
*/
selection_filter_matches : function(elementid, selector, selectednodes) {
- var result = true;
-
if (!selectednodes) {
// Find this because it was not passed as a param.
selectednodes = M.editor_atto.get_selected_nodes(elementid);
}
- selector = '.' + CSS.CONTENT + ' ' + selector;
-
- if (selectednodes.size() === 0) {
- return false;
- }
- selectednodes.each(function(node) {
- if (!node.ancestor(selector, true)) {
- result = false;
+ var allmatch = selectednodes.size() > 0;
+ selectednodes.each(function(node){
+ // Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing.
+ if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
+ allmatch = false;
}
- });
- return result;
- },
-
- /**
- * Returns a list of nodes that are ancestors of the selection nodes,
- * and match the specified css selector (and are contained within the editable div).
- *
- * @param {String} elementid
- * @param {String} selector
- * @return Y.NodeList
- */
- selection_filter : function(elementid, selector) {
- var selectednodes = M.editor_atto.get_selected_nodes(elementid);
- var result = new Y.NodeList();
- selector = '.' + CSS.CONTENT + ' ' + selector;
- selectednodes.each(function(node) {
- node.ancestors(selector, true).each(function(match) {
- if (result.indexOf(match) === -1) {
- result.push(match);
- }
- });
- });
- return result;
+ }, this);
+ return allmatch;
},
/**
* Get the deepest possible list of nodes in the current selection.
+ *
+ * Presently this method contains logic to work out what nodes are "selected" by the user.
+ * The user selection may take the form of:
+ * - text : selected text within a single node, such as double clicking a word.
+ * - A node : the user has clicked an image or media object that is selectable by clicking upon an element.
+ * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include
+ * both text and/or nodes.
+ * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node
+ * containing selected content.
+ * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording
+ * all nodes that appear after the start node and before the end node.
+ *
+ * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by
+ * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property
+ * before returning the nodes.
+ *
* @param {String} elementid
* @return Y.NodeList
*/
get_selected_nodes : function(elementid) {
+ var nodes = [],
+ results = new Y.NodeList(),
+ points = [],
+ range,
+ i,
+ sel,
+ start,
+ end,
+ parent,
+ values;
- var editable = M.editor_atto.get_editable_node(elementid);
- var startnode = M.editor_atto.get_selection_start_container();
- var endnode = M.editor_atto.get_selection_end_container();
-
- if (!startnode || !endnode) {
- return new Y.NodeList();
- }
-
- /**
- * Recursive function to walk the dom tree between 2 nodes and build
- * a list of leaf nodes.
- * @param {Y.Node} node - The current node.
- * @param {Y.Node} endnode - The node to stop at.
- * @param {Y.Node} boundingnode - A node that will contain the selection - do not result results outside this node.
- * @param {Integer} skipchildren - When returning from a child to a parent, this is how many children to skip.
- * @param {Boolean} isstart - Only true for the starting condition. Used in the case when startnode == endnode.
- * @return {Y.NodeList} The list of leaf nodes found.
- */
- var find_leaf_nodes = function(node, endnode, boundingnode, skipchildren, isstart) {
- var leafnodes = new Y.NodeList();
- if (endnode === node) {
- // If there are child nodes and we didn't come from a child node, then skip this end condition.
- if (!(endnode === node && startnode === node && isstart && node.hasChildNodes())) {
- // If the end node is a leaf - return it.
- if (!node.hasChildNodes()) {
- leafnodes.push(node);
+ // The window.getSelection method is available from IE9 onwards and for all other browsers.
+ if (window.getSelection) {
+ sel = window.getSelection();
+ for (i = 0; i < sel.rangeCount; i++) {
+ range = sel.getRangeAt(i);
+ start = new Y.Node(range.startContainer);
+ end = new Y.Node(range.endContainer);
+ if (start.compareTo(end)) {
+ // The start and end node are the same node. Only one node is selected. Easy!
+ nodes.push(end);
+ } else {
+ // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out.
+ // For the time being record them as a pair of points along with the common ancestor.
+ // First up we need to check the order of the nodes, we need them in the order they appear in the DOM
+ // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order.
+ values = this._fix_selected_nodes_from_range(
+ start,
+ range.startOffset,
+ end,
+ range.endOffset
+ );
+ // Ok start and end are in order. Yay!
+ start = values[0];
+ end = values[2];
+ // We calculate the common ancestor here as we should be able to get it from the range object.
+ if (range.commonAncestorContainer) {
+ parent = range.commonAncestorContainer;
+ } else if (range.parentElement) {
+ parent = range.parentElement();
+ } else {
+ parent = this._get_common_parent_from_nodes(start, end);
}
- // We reached the end of the selection.
- return leafnodes;
+ points.push([
+ start,
+ end,
+ parent
+ ]);
}
}
-
- // Node is not a leaf, look at it's children.
- if (node.hasChildNodes()) {
- var children = node.get('childNodes');
- // Skipchildren is used when returning from a child node, to make the parent look at the next child node.
- var child = children.item(skipchildren);
- if (child) {
- // There was a child node, find all the leaf nodes in it.
- leafnodes.concat(find_leaf_nodes(child, endnode, boundingnode, 0, false));
+ } else if (document.selection && document.selection.createRange) {
+ // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11).
+ // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8.
+ // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx
+ range = document.selection.createRange();
+ // Create range will return either TextRange object from the current text selection, or a controlRange
+ // collection from the control selection.
+ if (document.selection.type === "Control") {
+ // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us.
+ for (i = 0; i < range.length; i++) {
+ nodes.push(new Y.Node(range(i)));
}
} else {
- // This node has no children - so save it.
- leafnodes.push(node);
+ // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the
+ // selection.
+ nodes.push(new Y.Node(range.parentElement()));
}
- // If we hit the bounding node - this is an end condition.
- if (node === boundingnode) {
- return leafnodes;
+ }
+ // Ok we have two array as this point.
+ // - Nodes is an array containing nodes that are currently highlighted by selection.
+ // - Points is an array containing start, end, and parent nodes.
+ // For each item in the points array we need to find all nodes containing selected content.
+ for (i in points) {
+ nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i]));
+ }
+ // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area.
+ for (i in nodes) {
+ if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) {
+ results.push(nodes[i]);
}
- // We have added all the nodes in this branch, look at the next child of the parent.
- var parent = node.ancestor();
- if (parent) {
- var currentindex = parent.get('childNodes').indexOf(node);
- leafnodes.concat(find_leaf_nodes(parent, endnode, boundingnode, currentindex + 1, false));
- }
- return leafnodes;
- };
-
- // Kick off the recursive function.
- return find_leaf_nodes(startnode, endnode, editable, 0, true);
+ }
+ return results;
},
/**
- * Get the first node that contains the current selection.
- * @return DOMNode or false
+ * Returns true if the selection has changed since this method was last called.
+ *
+ * If the selection has changed we also fire the custom event atto:selectionchanged
+ *
+ * @method _has_selection_changed
+ * @private
+ * @param {EventFacade} e
+ * @returns {Boolean}
*/
- get_selection_start_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
+ _has_selection_changed : function(e) {
+ var sel,
+ i,
+ range,
+ bits = [],
+ selectedstr = '',
+ selectioncount = 0;
+ if (window.getSelection) {
+ // Hoorah - working with the alternative is horrid..
+ sel = window.getSelection();
+ selectioncount = sel.rangeCount;
+ for (i = 0; i < selectioncount; i++) {
+ range = sel.getRangeAt(i);
+ bits.push(range.startOffset);
+ bits.push(range.endOffset);
+ bits.push(this._get_node_identifier(range.startContainer));
+ bits.push(this._get_node_identifier(range.endContainer));
+ bits.push('&');
+ }
+ } else if (document.selection && document.selection.createRange) {
+ range = document.selection.createRange();
+ if (range !== null) {
+ if (document.selection.type === "Control") {
+ // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx
+ selectioncount = range.length;
+ for (i = 0; i < selectioncount; i++) {
+ bits.push(this._get_node_identifier(range(i)));
+ }
+ } else {
+ // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
+ selectioncount = 1;
+ bits.push(this._get_node_identifier(range.parentElement()));
+ }
+ }
}
-
- if (selection.startContainer) {
- return Y.one(selection.startContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(true);
- return Y.one(range.parentElement());
- }
- },
-
- /**
- * Get the last node that contains the current selection.
- * @return DOMNode or false
- */
- get_selection_end_container : function() {
- var selection = M.editor_atto.get_selection();
-
- if (selection.length && selection.pop) {
- selection = selection.pop();
- }
-
- if (selection.endContainer) {
- return Y.one(selection.endContainer);
- } else if (selection.parentElement) {
- var range = selection.duplicate();
-
- range.collapse(false);
- return Y.one(range.parentElement());
+ selectedstr = bits.join('|');
+ if (this.lastselection !== selectedstr) {
+ this.lastselection = selectedstr;
+ this.lastselectioncount = selectioncount;
+ return this._fire_selectionchanged(e);
}
return false;
},
+ /**
+ * Fires the atto:selectionchanged event.
+ *
+ * When the selectionchanged event gets fired three arguments are given:
+ * - event : the original event that lead to this event being fired.
+ * - elementid : the editor elementid for which this event is being fired.
+ * - selectednodes : an array containing nodes that are entirely selected of contain partially selected content.
+ *
+ * @method _fire_selectionchanged
+ * @private
+ * @param {EventFacade} e
+ */
+ _fire_selectionchanged : function(e) {
+ var editableid = e.currentTarget.get('id'),
+ elementid = editableid.substring(0, editableid.length - 8),
+ selectednodes = this.get_selected_nodes(elementid);
+ this.fire('atto:selectionchanged', {
+ event : e,
+ elementid : elementid,
+ selectedNodes : selectednodes
+ });
+ },
+
+ /**
+ * Returns the best identifier for the given node that can be found. May not be unique.
+ *
+ * @method _get_node_identifier
+ * @private
+ * @param {Node} node
+ * @returns {string}
+ */
+ _get_node_identifier : function(node) {
+ var uid = (node.nodeType === 3) ? node.length : node.id;
+ return uid || node.className || node.nodeName;
+ },
+
+ /**
+ * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset
+ *
+ * Note about offsets:
+ * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the
+ * start of the container to the boundary point of the Range.
+ * For other Node types, the offset is the number of child nodes between the start of the container and the boundary
+ * point of the Range.
+ *
+ * @method _fix_selected_nodes_from_range
+ * @private
+ * @param {Node} a
+ * @param {integer} aoffset
+ * @param {Node} b
+ * @param {integer} boffset
+ * @returns {[Node, integer, Node, integer]}
+ */
+ _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) {
+ var first,
+ firstoffset,
+ second,
+ secondoffset,
+ searchfunc,
+ acheckend = true,
+ bcheckend = true,
+ checkend = true;
+
+ if (a.get('nodeType') === 1) {
+ a = a.get('childNodes').item(aoffset);
+ acheckend = false;
+ }
+ if (b.get('nodeType') === 1 && boffset > 0) {
+ b = b.get('childNodes').item(boffset - 1);
+ bcheckend = false;
+ }
+ if (a._yuid === b._yuid) {
+ if (aoffset < boffset) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ } else {
+ if (a.contains(b)) {
+ first = a;
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else if (b.contains(a)) {
+ first = b;
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ } else {
+ searchfunc = function find_first(nodes, x, y) {
+ var i, result;
+ for (i in nodes) {
+ if (nodes[i] === x.getDOMNode()) {
+ return x;
+ } else if (nodes[i] === y.getDOMNode()) {
+ return y;
+ } else if (nodes[i].nodeType === 1) {
+ result = find_first(nodes[i].childNodes, x, y);
+ if (result !== false) {
+ return result;
+ }
+ }
+ }
+ return false;
+ };
+ first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a;
+ if (first === a) {
+ firstoffset = aoffset;
+ second = b;
+ secondoffset = boffset;
+ checkend = bcheckend;
+ } else {
+ firstoffset = boffset;
+ second = a;
+ secondoffset = aoffset;
+ checkend = acheckend;
+ }
+ }
+ if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) {
+ second = second.previous();
+ }
+ }
+ return [first, firstoffset, second, secondoffset];
+ },
+
+ /**
+ * Returns an array containing all of the nodes in the Dom between the two reference nodes.
+ *
+ * @method _get_all_nodes_between
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @param {Node} parent The common parent of a and b
+ * @returns {Node[]}
+ */
+ _get_all_nodes_between : function(a, b, parent) {
+ var startfound = false,
+ endfound = false,
+ nodes = [],
+ parenthasselectedtext = false;
+ if (a === b || a.contains(b)) {
+ return [a];
+ }
+ if (b.contains(a)) {
+ return [b];
+ }
+ if (!parent) {
+ return [a, b];
+ }
+
+ nodes.push(a);
+ parent.get('childNodes').each(function(node) {
+ if (endfound) {
+ return;
+ }
+ if (!startfound && node === a) {
+ startfound = true;
+ } else if (startfound && !endfound) {
+ if (node === b) {
+ endfound = true;
+ } else {
+ if (node.get('nodeType') === 3) {
+ parenthasselectedtext = true;
+ } else {
+ nodes.push(node);
+ nodes = nodes.compact(node.all('*'));
+ }
+ }
+ }
+ });
+ nodes.push(b);
+
+ if (parenthasselectedtext) {
+ nodes.unshift(parent);
+ }
+
+ return nodes;
+ },
+
+ /**
+ * Returns the common parent of the two given nodes.
+ *
+ * @method _get_common_parent_from_nodes
+ * @private
+ * @param {Node} a
+ * @param {Node} b
+ * @returns {Node}
+ */
+ _get_common_parent_from_nodes : function(a, b) {
+ var aparents = a.ancestors('*', false, '.' + CSS.CONTENT),
+ bparents = b.ancestors('*', false, '.' + CSS.CONTENT),
+ node;
+ while ((node = aparents.pop())) {
+ if (bparents.indexOf(node) !== -1) {
+ return node;
+ }
+ }
+ return a.ancestor('.' + CSS.CONTENT);
+ },
+
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
@@ -1178,17 +1386,6 @@ M.editor_atto = M.editor_atto || {
return false;
},
- /**
- * Get the list of child nodes of the selection.
- * @return DOMNode[]
- */
- get_selection_text : function() {
- var selection = M.editor_atto.get_selection();
- if (selection.length > 0 && selection[0].cloneContents) {
- return selection[0].cloneContents();
- }
- },
-
/**
* Set the current selection. Used to restore a selection.
*/
@@ -1431,3 +1628,6 @@ M.editor_atto = M.editor_atto || {
}
}
};
+
+// The editor_atto is publishing custom events that can be subscribed to.
+Y.augment(M.editor_atto, Y.EventTarget);
From d321f68b3dd3d4cb77e9037d89b7fe2bae77b657 Mon Sep 17 00:00:00 2001
From: Damyon Wiese
Date: Mon, 10 Mar 2014 16:39:21 +0800
Subject: [PATCH 55/63] MDL-44219 Atto: Convert selection logic to use Rangy JS
Library
This removes alot of complex, hard to test, hard to debug code from the editor so
we can focus on the things we want to spend time on. Rangy seems a good choice for
browser support, stability and features.
---
.../moodle-atto_equation-button-debug.js | 2 +-
.../moodle-atto_equation-button-min.js | 2 +-
.../moodle-atto_equation-button.js | 2 +-
.../equation/yui/src/button/js/button.js | 2 +-
.../moodle-atto_image-button-debug.js | 24 +-
.../moodle-atto_image-button-min.js | 4 +-
.../moodle-atto_image-button.js | 24 +-
.../plugins/image/yui/src/button/js/button.js | 24 +-
lib/editor/atto/thirdpartylibs.xml | 10 +
.../moodle-editor_atto-editor-debug.js | 515 +-
.../moodle-editor_atto-editor-min.js | 7 +-
.../moodle-editor_atto-editor.js | 515 +-
.../moodle-editor_atto-rangy-debug.js | 4458 +++++++++++++++++
.../moodle-editor_atto-rangy-min.js | 10 +
.../moodle-editor_atto-rangy.js | 4458 +++++++++++++++++
lib/editor/atto/yui/src/editor/js/editor.js | 512 +-
.../atto/yui/src/editor/meta/editor.json | 3 +-
lib/editor/atto/yui/src/rangy/build.json | 19 +
lib/editor/atto/yui/src/rangy/js/init.js | 18 +
lib/editor/atto/yui/src/rangy/js/license.txt | 21 +
.../atto/yui/src/rangy/js/rangy-core.js | 3224 ++++++++++++
.../yui/src/rangy/js/rangy-cssclassapplier.js | 713 +++
.../rangy/js/rangy-selectionsaverestore.js | 195 +
.../atto/yui/src/rangy/js/rangy-serializer.js | 300 ++
lib/editor/atto/yui/src/rangy/meta/rangy.json | 5 +
25 files changed, 13767 insertions(+), 1300 deletions(-)
create mode 100644 lib/editor/atto/thirdpartylibs.xml
create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-min.js
create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy.js
create mode 100644 lib/editor/atto/yui/src/rangy/build.json
create mode 100644 lib/editor/atto/yui/src/rangy/js/init.js
create mode 100644 lib/editor/atto/yui/src/rangy/js/license.txt
create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-core.js
create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-cssclassapplier.js
create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-selectionsaverestore.js
create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-serializer.js
create mode 100644 lib/editor/atto/yui/src/rangy/meta/rangy.json
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
index 4fee53bca80..c0c14aede45 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
@@ -81,7 +81,7 @@ M.atto_equation = M.atto_equation || {
M.editor_atto.focus(elementid);
}
M.atto_equation.selection = M.editor_atto.get_selection();
- if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) {
+ if (M.atto_equation.selection) {
var dialogue;
if (!M.atto_equation.dialogue) {
dialogue = new M.core.dialogue({
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
index 6c40ed061fa..0f416d5eab3 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_equation-button",function(e,t){M.atto_equation=M.atto_equation||{dialogue:null,selection:null,contextids:{},library:{},lastcursor:0,display_chooser:function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_equation.selection=M.editor_atto.get_selection();if(M.atto_equation.selection!==!1&&!M.atto_equation.selection.collapsed){var r;M.atto_equation.dialogue?r=M.atto_equation.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.render(),r.set("bodyContent",M.atto_equation.get_form_content(n)),r.set("headerContent",M.util.get_string("pluginname","atto_equation"));var i=new e.TabView({srcNode:"#atto_equation_library"});i.render(),r.show();var s=M.atto_equation.resolve_equation();s&&e.one("#atto_equation_equation").set("text",s),M.atto_equation.update_preview(!1,n),M.atto_equation.dialogue=r}},init:function(e){var t=M.util.image_url("e/math","core");e.texfilteractive&&(this.contextids[e.elementid]=e.contextid,this.library=e.library,M.editor_atto.add_toolbar_button(e.elementid,"equation",t,e.group,this.display_chooser),M.editor_atto.on("atto:selectionchanged",function(e){M.atto_equation.resolve_equation()?M.editor_atto.add_widget_highlight(e.elementid,"equation"):M.editor_atto.remove_widget_highlight(e.elementid,"equation")}))},resolve_equation:function(){var t=M.editor_atto.get_selection_parent_node(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},set_equation:function(t,n){var r,i,s,o,u,a;t.preventDefault(),M.atto_equation.dialogue.hide(),M.editor_atto.set_selection(M.atto_equation.selection),r=t.currentTarget.ancestor(".atto_form").one("textarea"),a=r.get("value"),a!==""&&(a="$$ "+a.trim()+" $$",i=e.one(M.editor_atto.get_selection_parent_node()),s=i.get("text"),o=/\$\$[\S\s]*\$\$/,u=o.exec(s),u&&u.length?(u=u.pop(),s=s.replace(u,"$$"+a+"$$"),i.set("text",s)):M.editor_atto.insert_html_at_focus_point(a),M.editor_atto.text_updated(n))},update_preview:function(t,n){var r=e.one("#atto_equation_equation"),i=r.get("value"),s,o,u="",a="\\square ",f=r.get("selectionStart");f||(f=0);while(i.charAt(f)==="\\"&&f>0)f-=1;var l=/[\w\{\}]/;while(l.test(i.charAt(f))&&f'+this.get_library_html(t)+'"+' '+"
";var o=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",u={sesskey:M.cfg.sesskey,contextid:this.contextids[t],action:"filtertext",text:n};return preview=e.io(o,{sync:!0,data:u,method:"POST"}),preview.status===200&&(n=preview.responseText),n}}},"@VERSION@",{requires:["node","escape","io","event-valuechange","tabview"]});
diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
index 4fee53bca80..c0c14aede45 100644
--- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
+++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
@@ -81,7 +81,7 @@ M.atto_equation = M.atto_equation || {
M.editor_atto.focus(elementid);
}
M.atto_equation.selection = M.editor_atto.get_selection();
- if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) {
+ if (M.atto_equation.selection) {
var dialogue;
if (!M.atto_equation.dialogue) {
dialogue = new M.core.dialogue({
diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
index 9253c0b5a3c..8fde5344f23 100644
--- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js
@@ -79,7 +79,7 @@ M.atto_equation = M.atto_equation || {
M.editor_atto.focus(elementid);
}
M.atto_equation.selection = M.editor_atto.get_selection();
- if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) {
+ if (M.atto_equation.selection) {
var dialogue;
if (!M.atto_equation.dialogue) {
dialogue = new M.core.dialogue({
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
index 566b652527d..0dd4d4aee30 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
@@ -145,6 +145,7 @@ M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
currentlyselected : {},
+ lastselectedimage : null,
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -183,7 +184,7 @@ M.atto_image = M.atto_image || {
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
M.editor_atto.on('atto:selectionchanged', function(e) {
- if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
@@ -263,8 +264,13 @@ M.atto_image = M.atto_image || {
M.atto_image.dialogue.hide();
+ M.editor_atto.focus(elementid);
if (url !== '') {
- M.editor_atto.set_selection(M.atto_image.selection);
+ if (this.lastselectedimage) {
+ M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage));
+ } else {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ }
imagehtml = ' 0) {
+ if (images) {
+ images = images.filter('img');
+ }
+
+ if (images && images.size()) {
image = images.item(0);
- for (i in images) {
- if (images[i].test('img')) {
- image = images[i];
- break;
- }
- }
+ this.lastselectedimage = image;
+
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
index 074c97fd6eb..5aec326d218 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,currentlyselected:{},init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t),M.editor_atto.currentlyselected=M.editor_atto.currentlyselected||{},M.editor_atto.currentlyselected[e.elementid]=null,M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?(M.editor_atto.add_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=e.selectedNodes):(M.editor_atto.remove_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=null)})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),s!==""&&(M.editor_atto.set_selection(M.atto_image.selection),c='",M.editor_atto.insert_html_at_focus_point(c),M.editor_atto.text_updated(n))},_get_selected_image_properties:function(e){var t={src:null,alt:null,width:null,height:null,align:null,display:"inline",presentation:!1},n=M.editor_atto.currentlyselected[e],r,s,o,u,a;if(n!==null&&n.size()>0){s=n.item(0);for(r in n)if(n[r].test("img")){s=n[r];break}a=s.getAttribute("style"),o=parseInt(s.getAttribute("width"),10),u=parseInt(s.getAttribute("height"),10),o>0&&(t.width=o),u>0&&(t.height=u);for(r in i)if(i[r].test(a)){t.align=i[r];break}return t.src=s.getAttribute("src"),t.alt=s.getAttribute("alt")||"",t.presentation=s.get("role")==="presentation",t}return!1},get_form_content:function(t){var n={alignment:M.util.get_string("alignment","atto_image"),alt:M.util.get_string("enteralt","atto_image"),browse:M.util.get_string("browserepositories","atto_image"),create:M.util.get_string("createimage","atto_image"),height:M.util.get_string("height","atto_image"),presentation:M.util.get_string("presentation","atto_image"),presentationrequired:M.util.get_string("presentationoraltrequired","atto_image"),preview:M.util.get_string("preview","atto_image"),width:M.util.get_string("width","atto_image"),url:M.util.get_string("enterurl","atto_image")},s,o;s='";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]});
+YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,currentlyselected:{},lastselectedimage:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t),M.editor_atto.currentlyselected=M.editor_atto.currentlyselected||{},M.editor_atto.currentlyselected[e.elementid]=null,M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes,!1)?(M.editor_atto.add_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=e.selectedNodes):(M.editor_atto.remove_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=null)})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),M.editor_atto.focus(n),s!==""&&(this.lastselectedimage?M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage)):M.editor_atto.set_selection(M.atto_image.selection),c='",M.editor_atto.insert_html_at_focus_point(c),M.editor_atto.text_updated(n))},_get_selected_image_properties:function(e){var t={src:null,alt:null,width:null,height:null,align:null,display:"inline",presentation:!1},n=M.editor_atto.currentlyselected[e],r,s,o,u,a;n&&(n=n.filter("img"));if(n&&n.size()){s=n.item(0),this.lastselectedimage=s,a=s.getAttribute("style"),o=parseInt(s.getAttribute("width"),10),u=parseInt(s.getAttribute("height"),10),o>0&&(t.width=o),u>0&&(t.height=u);for(r in i)if(i[r].test(a)){t.align=i[r];break}return t.src=s.getAttribute("src"),t.alt=s.getAttribute("alt")||"",t.presentation=s.get("role")==="presentation",t}return!1},get_form_content:function(t){var n={alignment:M.util.get_string("alignment","atto_image"),alt:M.util.get_string("enteralt","atto_image"),browse:M.util.get_string("browserepositories","atto_image"),create:M.util.get_string("createimage","atto_image"),height:M.util.get_string("height","atto_image"),presentation:M.util.get_string("presentation","atto_image"),presentationrequired:M.util.get_string("presentationoraltrequired","atto_image"),preview:M.util.get_string("preview","atto_image"),width:M.util.get_string("width","atto_image"),url:M.util.get_string("enterurl","atto_image")},s,o;s='";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]});
diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
index 566b652527d..0dd4d4aee30 100644
--- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
+++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
@@ -145,6 +145,7 @@ M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
currentlyselected : {},
+ lastselectedimage : null,
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -183,7 +184,7 @@ M.atto_image = M.atto_image || {
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
M.editor_atto.on('atto:selectionchanged', function(e) {
- if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
@@ -263,8 +264,13 @@ M.atto_image = M.atto_image || {
M.atto_image.dialogue.hide();
+ M.editor_atto.focus(elementid);
if (url !== '') {
- M.editor_atto.set_selection(M.atto_image.selection);
+ if (this.lastselectedimage) {
+ M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage));
+ } else {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ }
imagehtml = ' 0) {
+ if (images) {
+ images = images.filter('img');
+ }
+
+ if (images && images.size()) {
image = images.item(0);
- for (i in images) {
- if (images[i].test('img')) {
- image = images[i];
- break;
- }
- }
+ this.lastselectedimage = image;
+
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
index 2b77cc803ce..f0b0e9fb8cd 100644
--- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js
@@ -143,6 +143,7 @@ M.atto_image = M.atto_image || {
dialogue: null,
selection: null,
currentlyselected : {},
+ lastselectedimage : null,
init: function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
@@ -181,7 +182,7 @@ M.atto_image = M.atto_image || {
// This includes cursor changes, we check if the button should be active or not, based
// on the text selection.
M.editor_atto.on('atto:selectionchanged', function(e) {
- if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) {
+ if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) {
M.editor_atto.add_widget_highlight(e.elementid, 'image');
M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes;
} else {
@@ -261,8 +262,13 @@ M.atto_image = M.atto_image || {
M.atto_image.dialogue.hide();
+ M.editor_atto.focus(elementid);
if (url !== '') {
- M.editor_atto.set_selection(M.atto_image.selection);
+ if (this.lastselectedimage) {
+ M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage));
+ } else {
+ M.editor_atto.set_selection(M.atto_image.selection);
+ }
imagehtml = ' 0) {
+ if (images) {
+ images = images.filter('img');
+ }
+
+ if (images && images.size()) {
image = images.item(0);
- for (i in images) {
- if (images[i].test('img')) {
- image = images[i];
- break;
- }
- }
+ this.lastselectedimage = image;
+
style = image.getAttribute('style');
width = parseInt(image.getAttribute('width'), 10);
height = parseInt(image.getAttribute('height'), 10);
diff --git a/lib/editor/atto/thirdpartylibs.xml b/lib/editor/atto/thirdpartylibs.xml
new file mode 100644
index 00000000000..06b6014f949
--- /dev/null
+++ b/lib/editor/atto/thirdpartylibs.xml
@@ -0,0 +1,10 @@
+
+
+
+ yui/src/rangy/js/*.*
+ Rangy
+ MIT
+ 1.2.3
+
+
+
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index e74821b9625..0da12917fc3 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -84,7 +84,7 @@ M.editor_atto = M.editor_atto || {
'tfoot',
'ul',
'video'],
-
+
PLACEHOLDER_FONTNAME: 'yui-tmp',
ALL_NODES_SELECTOR: '[style],font[face]',
FONT_FAMILY: 'fontFamily',
@@ -140,13 +140,6 @@ M.editor_atto = M.editor_atto || {
*/
lastselection : null,
- /**
- * The number of selection ranges when the selection was last recorded.
- * @param lastselectioncount
- * @type integer
- */
- lastselectioncount : 0,
-
/**
* Set to true after the first editor has been initialised.
*
@@ -569,22 +562,17 @@ M.editor_atto = M.editor_atto || {
* @return bool
*/
is_active : function(elementid) {
- var selection = M.editor_atto.get_selection();
+ var range = rangy.createRange(),
+ editable = M.editor_atto.get_editable_node(elementid),
+ selection = rangy.getSelection();
- if (selection.length && selection.pop) {
- selection = selection.pop();
+ if (!selection.rangeCount) {
+ return false;
}
- var node = null;
- if (selection.parentElement) {
- node = Y.one(selection.parentElement());
- } else {
- node = Y.one(selection.startContainer);
- }
+ range.selectNode(editable.getDOMNode());
- var editable = M.editor_atto.get_editable_node(elementid);
-
- return node && editable.contains(node);
+ return range.intersectsRange(selection.getRangeAt(0));
},
/**
@@ -886,23 +874,12 @@ M.editor_atto = M.editor_atto || {
/**
* Create a cross browser selection object that represents a yui node.
* @param Node yui node for the selection
- * @return range (browser dependent)
+ * @return rangy.Range[]
*/
get_selection_from_node: function(node) {
- var range;
-
- if (window.getSelection) {
- range = document.createRange();
-
- range.setStartBefore(node.getDOMNode());
- range.setEndAfter(node.getDOMNode());
- return [range];
- } else if (document.selection) {
- range = document.body.createTextRange();
- range.moveToElementText(node.getDOMNode());
- return range;
- }
- return false;
+ var range = rangy.createRange();
+ range.selectNode(node.getDOMNode());
+ return [range];
},
/**
@@ -913,10 +890,7 @@ M.editor_atto = M.editor_atto || {
save_selection : function(event) {
var elementid = event.elementid;
if (elementid && this.is_active(elementid)) {
- var sel = this.get_selection();
- if (sel.length > 0) {
- this.selections[elementid] = sel;
- }
+ this.selections[elementid] = this.get_selection();
}
},
@@ -936,23 +910,10 @@ M.editor_atto = M.editor_atto || {
/**
* Get the selection object that can be passed back to set_selection.
- * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[]
+ * @return rangy.Range[]
*/
get_selection : function() {
- if (window.getSelection) {
- var sel = window.getSelection();
- var ranges = [], i = 0;
- for (i = 0; i < sel.rangeCount; i++) {
- ranges.push(sel.getRangeAt(i));
- }
- return ranges;
- } else if (document.selection) {
- // IE < 9
- if (document.selection.createRange) {
- return [document.selection.createRange()];
- }
- }
- return false;
+ return rangy.getSelection().getAllRanges();
},
/**
@@ -961,18 +922,7 @@ M.editor_atto = M.editor_atto || {
* @return boolean
*/
selection_contains_node : function(node) {
- // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed.
- var range, sel;
- if (window.getSelection) {
- sel = window.getSelection();
- if (sel.containsNode) {
- return sel.containsNode(node.getDOMNode(), true);
- }
- }
- sel = document.selection.createRange();
- range = sel.duplicate();
- range.moveToElementText(node.getDOMNode());
- return sel.inRange(range);
+ return rangy.getSelection().containsNode(node.getDOMNode(), true);
},
/**
@@ -982,124 +932,72 @@ M.editor_atto = M.editor_atto || {
* @param {String} elementid
* @param {String} selector
* @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up.
+ * @param {Boolean} requireall (Optional) - used to specify that any "any" match is good enough. Defaults to true.
* @return {Boolean}
*/
- selection_filter_matches : function(elementid, selector, selectednodes) {
+ selection_filter_matches : function(elementid, selector, selectednodes, requireall) {
+ if ((typeof requireall) === 'undefined') {
+ requireall = true;
+ }
if (!selectednodes) {
// Find this because it was not passed as a param.
selectednodes = M.editor_atto.get_selected_nodes(elementid);
}
- var allmatch = selectednodes.size() > 0;
+ var allmatch = selectednodes.size() > 0, anymatch = false;
selectednodes.each(function(node){
// Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing.
- if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
- allmatch = false;
+ if (requireall) {
+ // Check for at least one failure.
+ if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
+ allmatch = false;
+ }
+ } else {
+ // Check for at least one match.
+ if (!anymatch && node.ancestor(selector, true, '#' + elementid + "editable")) {
+ anymatch = true;
+ }
}
}, this);
- return allmatch;
+ if (requireall) {
+ return allmatch;
+ } else {
+ return anymatch;
+ }
},
/**
* Get the deepest possible list of nodes in the current selection.
*
- * Presently this method contains logic to work out what nodes are "selected" by the user.
- * The user selection may take the form of:
- * - text : selected text within a single node, such as double clicking a word.
- * - A node : the user has clicked an image or media object that is selectable by clicking upon an element.
- * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include
- * both text and/or nodes.
- * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node
- * containing selected content.
- * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording
- * all nodes that appear after the start node and before the end node.
- *
- * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by
- * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property
- * before returning the nodes.
- *
* @param {String} elementid
* @return Y.NodeList
*/
get_selected_nodes : function(elementid) {
- var nodes = [],
- results = new Y.NodeList(),
- points = [],
+ var results = new Y.NodeList(),
+ nodes,
+ selection,
range,
- i,
- sel,
- start,
- end,
- parent,
- values;
+ node,
+ i;
- // The window.getSelection method is available from IE9 onwards and for all other browsers.
- if (window.getSelection) {
- sel = window.getSelection();
- for (i = 0; i < sel.rangeCount; i++) {
- range = sel.getRangeAt(i);
- start = new Y.Node(range.startContainer);
- end = new Y.Node(range.endContainer);
- if (start.compareTo(end)) {
- // The start and end node are the same node. Only one node is selected. Easy!
- nodes.push(end);
- } else {
- // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out.
- // For the time being record them as a pair of points along with the common ancestor.
- // First up we need to check the order of the nodes, we need them in the order they appear in the DOM
- // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order.
- values = this._fix_selected_nodes_from_range(
- start,
- range.startOffset,
- end,
- range.endOffset
- );
- // Ok start and end are in order. Yay!
- start = values[0];
- end = values[2];
- // We calculate the common ancestor here as we should be able to get it from the range object.
- if (range.commonAncestorContainer) {
- parent = range.commonAncestorContainer;
- } else if (range.parentElement) {
- parent = range.parentElement();
- } else {
- parent = this._get_common_parent_from_nodes(start, end);
- }
- points.push([
- start,
- end,
- parent
- ]);
- }
- }
- } else if (document.selection && document.selection.createRange) {
- // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11).
- // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8.
- // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx
- range = document.selection.createRange();
- // Create range will return either TextRange object from the current text selection, or a controlRange
- // collection from the control selection.
- if (document.selection.type === "Control") {
- // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us.
- for (i = 0; i < range.length; i++) {
- nodes.push(new Y.Node(range(i)));
- }
- } else {
- // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the
- // selection.
- nodes.push(new Y.Node(range.parentElement()));
- }
+ selection = rangy.getSelection();
+
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ } else {
+ // Empty range.
+ range = rangy.createRange();
}
- // Ok we have two array as this point.
- // - Nodes is an array containing nodes that are currently highlighted by selection.
- // - Points is an array containing start, end, and parent nodes.
- // For each item in the points array we need to find all nodes containing selected content.
- for (i in points) {
- nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i]));
+
+ if (range.collapsed) {
+ range.selectNode(range.commonAncestorContainer);
}
- // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area.
- for (i in nodes) {
- if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) {
- results.push(nodes[i]);
+
+ nodes = range.getNodes();
+
+ for (i = 0; i < nodes.length; i++) {
+ node = Y.one(nodes[i]);
+ if (node.ancestor('#' + elementid + "editable")) {
+ results.push(node);
}
}
return results;
@@ -1116,47 +1014,25 @@ M.editor_atto = M.editor_atto || {
* @returns {Boolean}
*/
_has_selection_changed : function(e) {
- var sel,
- i,
+ var selection = rangy.getSelection(),
range,
- bits = [],
- selectedstr = '',
- selectioncount = 0;
- if (window.getSelection) {
- // Hoorah - working with the alternative is horrid..
- sel = window.getSelection();
- selectioncount = sel.rangeCount;
- for (i = 0; i < selectioncount; i++) {
- range = sel.getRangeAt(i);
- bits.push(range.startOffset);
- bits.push(range.endOffset);
- bits.push(this._get_node_identifier(range.startContainer));
- bits.push(this._get_node_identifier(range.endContainer));
- bits.push('&');
- }
- } else if (document.selection && document.selection.createRange) {
- range = document.selection.createRange();
- if (range !== null) {
- if (document.selection.type === "Control") {
- // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx
- selectioncount = range.length;
- for (i = 0; i < selectioncount; i++) {
- bits.push(this._get_node_identifier(range(i)));
- }
- } else {
- // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
- selectioncount = 1;
- bits.push(this._get_node_identifier(range.parentElement()));
- }
+ changed = false;
+
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ } else {
+ // Empty range.
+ range = rangy.createRange();
+ }
+
+ if (this.lastselection) {
+ if (!this.lastselection.equals(range)) {
+ changed = true;
+ return this._fire_selectionchanged(e);
}
}
- selectedstr = bits.join('|');
- if (this.lastselection !== selectedstr) {
- this.lastselection = selectedstr;
- this.lastselectioncount = selectioncount;
- return this._fire_selectionchanged(e);
- }
- return false;
+ this.lastselection = range;
+ return changed;
},
/**
@@ -1182,230 +1058,25 @@ M.editor_atto = M.editor_atto || {
});
},
- /**
- * Returns the best identifier for the given node that can be found. May not be unique.
- *
- * @method _get_node_identifier
- * @private
- * @param {Node} node
- * @returns {string}
- */
- _get_node_identifier : function(node) {
- var uid = (node.nodeType === 3) ? node.length : node.id;
- return uid || node.className || node.nodeName;
- },
-
- /**
- * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset
- *
- * Note about offsets:
- * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the
- * start of the container to the boundary point of the Range.
- * For other Node types, the offset is the number of child nodes between the start of the container and the boundary
- * point of the Range.
- *
- * @method _fix_selected_nodes_from_range
- * @private
- * @param {Node} a
- * @param {integer} aoffset
- * @param {Node} b
- * @param {integer} boffset
- * @returns {[Node, integer, Node, integer]}
- */
- _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) {
- var first,
- firstoffset,
- second,
- secondoffset,
- searchfunc,
- acheckend = true,
- bcheckend = true,
- checkend = true;
-
- if (a.get('nodeType') === 1) {
- a = a.get('childNodes').item(aoffset);
- acheckend = false;
- }
- if (b.get('nodeType') === 1 && boffset > 0) {
- b = b.get('childNodes').item(boffset - 1);
- bcheckend = false;
- }
- if (a._yuid === b._yuid) {
- if (aoffset < boffset) {
- first = a;
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else {
- first = b;
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- }
- } else {
- if (a.contains(b)) {
- first = a;
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else if (b.contains(a)) {
- first = b;
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- } else {
- searchfunc = function find_first(nodes, x, y) {
- var i, result;
- for (i in nodes) {
- if (nodes[i] === x.getDOMNode()) {
- return x;
- } else if (nodes[i] === y.getDOMNode()) {
- return y;
- } else if (nodes[i].nodeType === 1) {
- result = find_first(nodes[i].childNodes, x, y);
- if (result !== false) {
- return result;
- }
- }
- }
- return false;
- };
- first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a;
- if (first === a) {
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else {
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- }
- }
- if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) {
- second = second.previous();
- }
- }
- return [first, firstoffset, second, secondoffset];
- },
-
- /**
- * Returns an array containing all of the nodes in the Dom between the two reference nodes.
- *
- * @method _get_all_nodes_between
- * @private
- * @param {Node} a
- * @param {Node} b
- * @param {Node} parent The common parent of a and b
- * @returns {Node[]}
- */
- _get_all_nodes_between : function(a, b, parent) {
- var startfound = false,
- endfound = false,
- nodes = [],
- parenthasselectedtext = false;
- if (a === b || a.contains(b)) {
- return [a];
- }
- if (b.contains(a)) {
- return [b];
- }
- if (!parent) {
- return [a, b];
- }
-
- nodes.push(a);
- parent.get('childNodes').each(function(node) {
- if (endfound) {
- return;
- }
- if (!startfound && node === a) {
- startfound = true;
- } else if (startfound && !endfound) {
- if (node === b) {
- endfound = true;
- } else {
- if (node.get('nodeType') === 3) {
- parenthasselectedtext = true;
- } else {
- nodes.push(node);
- nodes = nodes.compact(node.all('*'));
- }
- }
- }
- });
- nodes.push(b);
-
- if (parenthasselectedtext) {
- nodes.unshift(parent);
- }
-
- return nodes;
- },
-
- /**
- * Returns the common parent of the two given nodes.
- *
- * @method _get_common_parent_from_nodes
- * @private
- * @param {Node} a
- * @param {Node} b
- * @returns {Node}
- */
- _get_common_parent_from_nodes : function(a, b) {
- var aparents = a.ancestors('*', false, '.' + CSS.CONTENT),
- bparents = b.ancestors('*', false, '.' + CSS.CONTENT),
- node;
- while ((node = aparents.pop())) {
- if (bparents.indexOf(node) !== -1) {
- return node;
- }
- }
- return a.ancestor('.' + CSS.CONTENT);
- },
-
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
*/
get_selection_parent_node : function() {
- var selection = M.editor_atto.get_selection();
- if (selection.length) {
- selection = selection.pop();
+ var selection = rangy.getSelection();
+ if (selection.rangeCount) {
+ return selection.getRangeAt(0).commonAncestorContainer;
}
-
- if (selection.commonAncestorContainer) {
- return selection.commonAncestorContainer;
- } else if (selection.parentElement) {
- return selection.parentElement();
- }
- // No selection
return false;
},
/**
* Set the current selection. Used to restore a selection.
+ * @param rangy.Range[] ranges - List of ranges in selection.
*/
- set_selection : function(selection) {
- var sel, i;
-
- if (window.getSelection) {
- sel = window.getSelection();
- sel.removeAllRanges();
- for (i = 0; i < selection.length; i++) {
- sel.addRange(selection[i]);
- }
- } else if (document.selection) {
- // IE < 9
- if (selection.select) {
- selection.select();
- }
- }
+ set_selection : function(ranges) {
+ var selection = rangy.getSelection();
+ selection.setRanges(ranges);
},
/**
@@ -1552,19 +1223,20 @@ M.editor_atto = M.editor_atto || {
/**
* Inserts the given HTML into the editable content at the currently focused point.
- * @param {String} html
+ * @param String html
*/
insert_html_at_focus_point: function(html) {
// We check document.selection here as the insertHTML exec command wan't available in IE until version 11.
// In IE document.selection was removed at from version 11. Making it an easy affordable check.
- if (document.selection && document.selection.createRange) {
- var range = document.selection.createRange();
- if (range.pasteHTML) {
- range.pasteHTML(html);
- }
- } else {
- // All other browsers are great!
- document.execCommand('insertHTML', false, html);
+ var selection = rangy.getSelection(),
+ range,
+ node = Y.Node.create(html);
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ }
+ if (range) {
+ range.deleteContents();
+ range.insertNode(node.getDOMNode());
}
},
@@ -1775,6 +1447,7 @@ Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML");
"event-simulate",
"event-custom",
"yui-throttle",
- "moodle-core-notification-dialogue"
+ "moodle-core-notification-dialogue",
+ "moodle-editor_atto-rangy"
]
});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
index 9d345e74c13..669e8d0afb6 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -1,4 +1,3 @@
-YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n
")&&n.setHTML(""),n.getHTML()},keyboard_navigation:function(t,n){var r,i,s,o,u=M.editor_atto.get_toolbar_node(n);t.preventDefault(),r=u.all("empty"),u.all(".atto_group").each(function(e){e.hasAttribute("hidden")||(r=r.concat(e.all("button")))}),s=u.getAttribute("aria-activedescendant");if(!s)return;i=e.one("#"+s),i.setAttribute("tabindex","-1"),o=r.indexOf(i),t.keyCode===37?(o--,o<0&&(o=r.size()-1)):(o++,o>=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t,n,r){e.use("core_filepicker",function(e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t=rangy.createRange();return t.selectNode(e.getDOMNode()),[t]},save_selection:function(
+e){var t=e.elementid;t&&this.is_active(t)&&(this.selections[t]=this.get_selection())},restore_selection:function(e,t){this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){return rangy.getSelection().getAllRanges()},selection_contains_node:function(e){return rangy.getSelection().containsNode(e.getDOMNode(),!0)},selection_filter_matches:function(e,t,n,r){typeof r=="undefined"&&(r=!0),n||(n=M.editor_atto.get_selected_nodes(e));var i=n.size()>0,s=!1;return n.each(function(n){if(r){if(!i||!n.ancestor(t,!0,"#"+e+"editable"))i=!1}else!s&&n.ancestor(t,!0,"#"+e+"editable")&&(s=!0)},this),r?i:s},get_selected_nodes:function(t){var n=new e.NodeList,r,i,s,o,u;i=rangy.getSelection(),i.rangeCount?s=i.getRangeAt(0):s=rangy.createRange(),s.collapsed&&s.selectNode(s.commonAncestorContainer),r=s.getNodes();for(u=0;u"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}},insert_html_at_focus_point:function(t){var n=rangy.getSelection(),r,i=e.Node.create(t);n.rangeCount&&(r=n.getRangeAt(0)),r&&(r.deleteContents(),r.insertNode(i.getDOMNode()))},toggle_inline_selection_class:function(t,n){var r=M.editor_atto.get_selection_parent_node(),i=e.one("body"),s,o=[],u,a,f,l=0;if(!r)return;document.execCommand("fontname",!1,M.editor_atto.PLACEHOLDER_FONTNAME),s=i.all(M.editor_atto.ALL_NODES_SELECTOR),s.each(function(t,n){t.getStyle(M.editor_atto.FONT_FAMILY)===M.editor_atto.PLACEHOLDER_FONTNAME&&(t.setStyle(M.editor_atto.FONT_FAMILY,""),t.compareTo(i)||o.push(e.Node.getDOMNode(s.item(n))))});for(l=0;l"),f.append(o[l].innerHTML),a.replace(f),a=f);for(var c=0;c"),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","event-custom","yui-throttle","moodle-core-notification-dialogue","moodle-editor_atto-rangy"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
index e74821b9625..0da12917fc3 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -84,7 +84,7 @@ M.editor_atto = M.editor_atto || {
'tfoot',
'ul',
'video'],
-
+
PLACEHOLDER_FONTNAME: 'yui-tmp',
ALL_NODES_SELECTOR: '[style],font[face]',
FONT_FAMILY: 'fontFamily',
@@ -140,13 +140,6 @@ M.editor_atto = M.editor_atto || {
*/
lastselection : null,
- /**
- * The number of selection ranges when the selection was last recorded.
- * @param lastselectioncount
- * @type integer
- */
- lastselectioncount : 0,
-
/**
* Set to true after the first editor has been initialised.
*
@@ -569,22 +562,17 @@ M.editor_atto = M.editor_atto || {
* @return bool
*/
is_active : function(elementid) {
- var selection = M.editor_atto.get_selection();
+ var range = rangy.createRange(),
+ editable = M.editor_atto.get_editable_node(elementid),
+ selection = rangy.getSelection();
- if (selection.length && selection.pop) {
- selection = selection.pop();
+ if (!selection.rangeCount) {
+ return false;
}
- var node = null;
- if (selection.parentElement) {
- node = Y.one(selection.parentElement());
- } else {
- node = Y.one(selection.startContainer);
- }
+ range.selectNode(editable.getDOMNode());
- var editable = M.editor_atto.get_editable_node(elementid);
-
- return node && editable.contains(node);
+ return range.intersectsRange(selection.getRangeAt(0));
},
/**
@@ -886,23 +874,12 @@ M.editor_atto = M.editor_atto || {
/**
* Create a cross browser selection object that represents a yui node.
* @param Node yui node for the selection
- * @return range (browser dependent)
+ * @return rangy.Range[]
*/
get_selection_from_node: function(node) {
- var range;
-
- if (window.getSelection) {
- range = document.createRange();
-
- range.setStartBefore(node.getDOMNode());
- range.setEndAfter(node.getDOMNode());
- return [range];
- } else if (document.selection) {
- range = document.body.createTextRange();
- range.moveToElementText(node.getDOMNode());
- return range;
- }
- return false;
+ var range = rangy.createRange();
+ range.selectNode(node.getDOMNode());
+ return [range];
},
/**
@@ -913,10 +890,7 @@ M.editor_atto = M.editor_atto || {
save_selection : function(event) {
var elementid = event.elementid;
if (elementid && this.is_active(elementid)) {
- var sel = this.get_selection();
- if (sel.length > 0) {
- this.selections[elementid] = sel;
- }
+ this.selections[elementid] = this.get_selection();
}
},
@@ -936,23 +910,10 @@ M.editor_atto = M.editor_atto || {
/**
* Get the selection object that can be passed back to set_selection.
- * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[]
+ * @return rangy.Range[]
*/
get_selection : function() {
- if (window.getSelection) {
- var sel = window.getSelection();
- var ranges = [], i = 0;
- for (i = 0; i < sel.rangeCount; i++) {
- ranges.push(sel.getRangeAt(i));
- }
- return ranges;
- } else if (document.selection) {
- // IE < 9
- if (document.selection.createRange) {
- return [document.selection.createRange()];
- }
- }
- return false;
+ return rangy.getSelection().getAllRanges();
},
/**
@@ -961,18 +922,7 @@ M.editor_atto = M.editor_atto || {
* @return boolean
*/
selection_contains_node : function(node) {
- // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed.
- var range, sel;
- if (window.getSelection) {
- sel = window.getSelection();
- if (sel.containsNode) {
- return sel.containsNode(node.getDOMNode(), true);
- }
- }
- sel = document.selection.createRange();
- range = sel.duplicate();
- range.moveToElementText(node.getDOMNode());
- return sel.inRange(range);
+ return rangy.getSelection().containsNode(node.getDOMNode(), true);
},
/**
@@ -982,124 +932,72 @@ M.editor_atto = M.editor_atto || {
* @param {String} elementid
* @param {String} selector
* @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up.
+ * @param {Boolean} requireall (Optional) - used to specify that any "any" match is good enough. Defaults to true.
* @return {Boolean}
*/
- selection_filter_matches : function(elementid, selector, selectednodes) {
+ selection_filter_matches : function(elementid, selector, selectednodes, requireall) {
+ if ((typeof requireall) === 'undefined') {
+ requireall = true;
+ }
if (!selectednodes) {
// Find this because it was not passed as a param.
selectednodes = M.editor_atto.get_selected_nodes(elementid);
}
- var allmatch = selectednodes.size() > 0;
+ var allmatch = selectednodes.size() > 0, anymatch = false;
selectednodes.each(function(node){
// Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing.
- if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
- allmatch = false;
+ if (requireall) {
+ // Check for at least one failure.
+ if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) {
+ allmatch = false;
+ }
+ } else {
+ // Check for at least one match.
+ if (!anymatch && node.ancestor(selector, true, '#' + elementid + "editable")) {
+ anymatch = true;
+ }
}
}, this);
- return allmatch;
+ if (requireall) {
+ return allmatch;
+ } else {
+ return anymatch;
+ }
},
/**
* Get the deepest possible list of nodes in the current selection.
*
- * Presently this method contains logic to work out what nodes are "selected" by the user.
- * The user selection may take the form of:
- * - text : selected text within a single node, such as double clicking a word.
- * - A node : the user has clicked an image or media object that is selectable by clicking upon an element.
- * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include
- * both text and/or nodes.
- * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node
- * containing selected content.
- * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording
- * all nodes that appear after the start node and before the end node.
- *
- * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by
- * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property
- * before returning the nodes.
- *
* @param {String} elementid
* @return Y.NodeList
*/
get_selected_nodes : function(elementid) {
- var nodes = [],
- results = new Y.NodeList(),
- points = [],
+ var results = new Y.NodeList(),
+ nodes,
+ selection,
range,
- i,
- sel,
- start,
- end,
- parent,
- values;
+ node,
+ i;
- // The window.getSelection method is available from IE9 onwards and for all other browsers.
- if (window.getSelection) {
- sel = window.getSelection();
- for (i = 0; i < sel.rangeCount; i++) {
- range = sel.getRangeAt(i);
- start = new Y.Node(range.startContainer);
- end = new Y.Node(range.endContainer);
- if (start.compareTo(end)) {
- // The start and end node are the same node. Only one node is selected. Easy!
- nodes.push(end);
- } else {
- // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out.
- // For the time being record them as a pair of points along with the common ancestor.
- // First up we need to check the order of the nodes, we need them in the order they appear in the DOM
- // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order.
- values = this._fix_selected_nodes_from_range(
- start,
- range.startOffset,
- end,
- range.endOffset
- );
- // Ok start and end are in order. Yay!
- start = values[0];
- end = values[2];
- // We calculate the common ancestor here as we should be able to get it from the range object.
- if (range.commonAncestorContainer) {
- parent = range.commonAncestorContainer;
- } else if (range.parentElement) {
- parent = range.parentElement();
- } else {
- parent = this._get_common_parent_from_nodes(start, end);
- }
- points.push([
- start,
- end,
- parent
- ]);
- }
- }
- } else if (document.selection && document.selection.createRange) {
- // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11).
- // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8.
- // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx
- range = document.selection.createRange();
- // Create range will return either TextRange object from the current text selection, or a controlRange
- // collection from the control selection.
- if (document.selection.type === "Control") {
- // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us.
- for (i = 0; i < range.length; i++) {
- nodes.push(new Y.Node(range(i)));
- }
- } else {
- // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the
- // selection.
- nodes.push(new Y.Node(range.parentElement()));
- }
+ selection = rangy.getSelection();
+
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ } else {
+ // Empty range.
+ range = rangy.createRange();
}
- // Ok we have two array as this point.
- // - Nodes is an array containing nodes that are currently highlighted by selection.
- // - Points is an array containing start, end, and parent nodes.
- // For each item in the points array we need to find all nodes containing selected content.
- for (i in points) {
- nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i]));
+
+ if (range.collapsed) {
+ range.selectNode(range.commonAncestorContainer);
}
- // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area.
- for (i in nodes) {
- if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) {
- results.push(nodes[i]);
+
+ nodes = range.getNodes();
+
+ for (i = 0; i < nodes.length; i++) {
+ node = Y.one(nodes[i]);
+ if (node.ancestor('#' + elementid + "editable")) {
+ results.push(node);
}
}
return results;
@@ -1116,47 +1014,25 @@ M.editor_atto = M.editor_atto || {
* @returns {Boolean}
*/
_has_selection_changed : function(e) {
- var sel,
- i,
+ var selection = rangy.getSelection(),
range,
- bits = [],
- selectedstr = '',
- selectioncount = 0;
- if (window.getSelection) {
- // Hoorah - working with the alternative is horrid..
- sel = window.getSelection();
- selectioncount = sel.rangeCount;
- for (i = 0; i < selectioncount; i++) {
- range = sel.getRangeAt(i);
- bits.push(range.startOffset);
- bits.push(range.endOffset);
- bits.push(this._get_node_identifier(range.startContainer));
- bits.push(this._get_node_identifier(range.endContainer));
- bits.push('&');
- }
- } else if (document.selection && document.selection.createRange) {
- range = document.selection.createRange();
- if (range !== null) {
- if (document.selection.type === "Control") {
- // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx
- selectioncount = range.length;
- for (i = 0; i < selectioncount; i++) {
- bits.push(this._get_node_identifier(range(i)));
- }
- } else {
- // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
- selectioncount = 1;
- bits.push(this._get_node_identifier(range.parentElement()));
- }
+ changed = false;
+
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ } else {
+ // Empty range.
+ range = rangy.createRange();
+ }
+
+ if (this.lastselection) {
+ if (!this.lastselection.equals(range)) {
+ changed = true;
+ return this._fire_selectionchanged(e);
}
}
- selectedstr = bits.join('|');
- if (this.lastselection !== selectedstr) {
- this.lastselection = selectedstr;
- this.lastselectioncount = selectioncount;
- return this._fire_selectionchanged(e);
- }
- return false;
+ this.lastselection = range;
+ return changed;
},
/**
@@ -1182,230 +1058,25 @@ M.editor_atto = M.editor_atto || {
});
},
- /**
- * Returns the best identifier for the given node that can be found. May not be unique.
- *
- * @method _get_node_identifier
- * @private
- * @param {Node} node
- * @returns {string}
- */
- _get_node_identifier : function(node) {
- var uid = (node.nodeType === 3) ? node.length : node.id;
- return uid || node.className || node.nodeName;
- },
-
- /**
- * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset
- *
- * Note about offsets:
- * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the
- * start of the container to the boundary point of the Range.
- * For other Node types, the offset is the number of child nodes between the start of the container and the boundary
- * point of the Range.
- *
- * @method _fix_selected_nodes_from_range
- * @private
- * @param {Node} a
- * @param {integer} aoffset
- * @param {Node} b
- * @param {integer} boffset
- * @returns {[Node, integer, Node, integer]}
- */
- _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) {
- var first,
- firstoffset,
- second,
- secondoffset,
- searchfunc,
- acheckend = true,
- bcheckend = true,
- checkend = true;
-
- if (a.get('nodeType') === 1) {
- a = a.get('childNodes').item(aoffset);
- acheckend = false;
- }
- if (b.get('nodeType') === 1 && boffset > 0) {
- b = b.get('childNodes').item(boffset - 1);
- bcheckend = false;
- }
- if (a._yuid === b._yuid) {
- if (aoffset < boffset) {
- first = a;
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else {
- first = b;
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- }
- } else {
- if (a.contains(b)) {
- first = a;
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else if (b.contains(a)) {
- first = b;
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- } else {
- searchfunc = function find_first(nodes, x, y) {
- var i, result;
- for (i in nodes) {
- if (nodes[i] === x.getDOMNode()) {
- return x;
- } else if (nodes[i] === y.getDOMNode()) {
- return y;
- } else if (nodes[i].nodeType === 1) {
- result = find_first(nodes[i].childNodes, x, y);
- if (result !== false) {
- return result;
- }
- }
- }
- return false;
- };
- first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a;
- if (first === a) {
- firstoffset = aoffset;
- second = b;
- secondoffset = boffset;
- checkend = bcheckend;
- } else {
- firstoffset = boffset;
- second = a;
- secondoffset = aoffset;
- checkend = acheckend;
- }
- }
- if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) {
- second = second.previous();
- }
- }
- return [first, firstoffset, second, secondoffset];
- },
-
- /**
- * Returns an array containing all of the nodes in the Dom between the two reference nodes.
- *
- * @method _get_all_nodes_between
- * @private
- * @param {Node} a
- * @param {Node} b
- * @param {Node} parent The common parent of a and b
- * @returns {Node[]}
- */
- _get_all_nodes_between : function(a, b, parent) {
- var startfound = false,
- endfound = false,
- nodes = [],
- parenthasselectedtext = false;
- if (a === b || a.contains(b)) {
- return [a];
- }
- if (b.contains(a)) {
- return [b];
- }
- if (!parent) {
- return [a, b];
- }
-
- nodes.push(a);
- parent.get('childNodes').each(function(node) {
- if (endfound) {
- return;
- }
- if (!startfound && node === a) {
- startfound = true;
- } else if (startfound && !endfound) {
- if (node === b) {
- endfound = true;
- } else {
- if (node.get('nodeType') === 3) {
- parenthasselectedtext = true;
- } else {
- nodes.push(node);
- nodes = nodes.compact(node.all('*'));
- }
- }
- }
- });
- nodes.push(b);
-
- if (parenthasselectedtext) {
- nodes.unshift(parent);
- }
-
- return nodes;
- },
-
- /**
- * Returns the common parent of the two given nodes.
- *
- * @method _get_common_parent_from_nodes
- * @private
- * @param {Node} a
- * @param {Node} b
- * @returns {Node}
- */
- _get_common_parent_from_nodes : function(a, b) {
- var aparents = a.ancestors('*', false, '.' + CSS.CONTENT),
- bparents = b.ancestors('*', false, '.' + CSS.CONTENT),
- node;
- while ((node = aparents.pop())) {
- if (bparents.indexOf(node) !== -1) {
- return node;
- }
- }
- return a.ancestor('.' + CSS.CONTENT);
- },
-
/**
* Get the dom node representing the common anscestor of the selection nodes.
* @return DOMNode or false
*/
get_selection_parent_node : function() {
- var selection = M.editor_atto.get_selection();
- if (selection.length) {
- selection = selection.pop();
+ var selection = rangy.getSelection();
+ if (selection.rangeCount) {
+ return selection.getRangeAt(0).commonAncestorContainer;
}
-
- if (selection.commonAncestorContainer) {
- return selection.commonAncestorContainer;
- } else if (selection.parentElement) {
- return selection.parentElement();
- }
- // No selection
return false;
},
/**
* Set the current selection. Used to restore a selection.
+ * @param rangy.Range[] ranges - List of ranges in selection.
*/
- set_selection : function(selection) {
- var sel, i;
-
- if (window.getSelection) {
- sel = window.getSelection();
- sel.removeAllRanges();
- for (i = 0; i < selection.length; i++) {
- sel.addRange(selection[i]);
- }
- } else if (document.selection) {
- // IE < 9
- if (selection.select) {
- selection.select();
- }
- }
+ set_selection : function(ranges) {
+ var selection = rangy.getSelection();
+ selection.setRanges(ranges);
},
/**
@@ -1552,19 +1223,20 @@ M.editor_atto = M.editor_atto || {
/**
* Inserts the given HTML into the editable content at the currently focused point.
- * @param {String} html
+ * @param String html
*/
insert_html_at_focus_point: function(html) {
// We check document.selection here as the insertHTML exec command wan't available in IE until version 11.
// In IE document.selection was removed at from version 11. Making it an easy affordable check.
- if (document.selection && document.selection.createRange) {
- var range = document.selection.createRange();
- if (range.pasteHTML) {
- range.pasteHTML(html);
- }
- } else {
- // All other browsers are great!
- document.execCommand('insertHTML', false, html);
+ var selection = rangy.getSelection(),
+ range,
+ node = Y.Node.create(html);
+ if (selection.rangeCount) {
+ range = selection.getRangeAt(0);
+ }
+ if (range) {
+ range.deleteContents();
+ range.insertNode(node.getDOMNode());
}
},
@@ -1775,6 +1447,7 @@ Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML");
"event-simulate",
"event-custom",
"yui-throttle",
- "moodle-core-notification-dialogue"
+ "moodle-core-notification-dialogue",
+ "moodle-editor_atto-rangy"
]
});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
new file mode 100644
index 00000000000..05c06c82890
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js
@@ -0,0 +1,4458 @@
+/**
+ * @license Rangy, a cross-browser JavaScript range and selection library
+ * http://code.google.com/p/rangy/
+ *
+ * Copyright 2012, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.2.3
+ * Build date: 26 February 2012
+ */
+window['rangy'] = (function() {
+
+
+ var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
+
+ var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
+ "commonAncestorContainer", "START_TO_START", "START_TO_END", "END_TO_START", "END_TO_END"];
+
+ var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
+ "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
+ "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
+
+ var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
+
+ // Subset of TextRange's full set of methods that we're interested in
+ var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "getBookmark", "moveToBookmark",
+ "moveToElementText", "parentElement", "pasteHTML", "select", "setEndPoint", "getBoundingClientRect"];
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Trio of functions taken from Peter Michaux's article:
+ // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
+ function isHostMethod(o, p) {
+ var t = typeof o[p];
+ return t == FUNCTION || (!!(t == OBJECT && o[p])) || t == "unknown";
+ }
+
+ function isHostObject(o, p) {
+ return !!(typeof o[p] == OBJECT && o[p]);
+ }
+
+ function isHostProperty(o, p) {
+ return typeof o[p] != UNDEFINED;
+ }
+
+ // Creates a convenience function to save verbose repeated calls to tests functions
+ function createMultiplePropertyTest(testFunc) {
+ return function(o, props) {
+ var i = props.length;
+ while (i--) {
+ if (!testFunc(o, props[i])) {
+ return false;
+ }
+ }
+ return true;
+ };
+ }
+
+ // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
+ var areHostMethods = createMultiplePropertyTest(isHostMethod);
+ var areHostObjects = createMultiplePropertyTest(isHostObject);
+ var areHostProperties = createMultiplePropertyTest(isHostProperty);
+
+ function isTextRange(range) {
+ return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
+ }
+
+ var api = {
+ version: "1.2.3",
+ initialized: false,
+ supported: true,
+
+ util: {
+ isHostMethod: isHostMethod,
+ isHostObject: isHostObject,
+ isHostProperty: isHostProperty,
+ areHostMethods: areHostMethods,
+ areHostObjects: areHostObjects,
+ areHostProperties: areHostProperties,
+ isTextRange: isTextRange
+ },
+
+ features: {},
+
+ modules: {},
+ config: {
+ alertOnWarn: false,
+ preferTextRange: false
+ }
+ };
+
+ function fail(reason) {
+ window.alert("Rangy not supported in your browser. Reason: " + reason);
+ api.initialized = true;
+ api.supported = false;
+ }
+
+ api.fail = fail;
+
+ function warn(msg) {
+ var warningMessage = "Rangy warning: " + msg;
+ if (api.config.alertOnWarn) {
+ window.alert(warningMessage);
+ } else if (typeof window.console != UNDEFINED && typeof window.console.log != UNDEFINED) {
+ window.console.log(warningMessage);
+ }
+ }
+
+ api.warn = warn;
+
+ if ({}.hasOwnProperty) {
+ api.util.extend = function(o, props) {
+ for (var i in props) {
+ if (props.hasOwnProperty(i)) {
+ o[i] = props[i];
+ }
+ }
+ };
+ } else {
+ fail("hasOwnProperty not supported");
+ }
+
+ var initListeners = [];
+ var moduleInitializers = [];
+
+ // Initialization
+ function init() {
+ if (api.initialized) {
+ return;
+ }
+ var testRange;
+ var implementsDomRange = false, implementsTextRange = false;
+
+ // First, perform basic feature tests
+
+ if (isHostMethod(document, "createRange")) {
+ testRange = document.createRange();
+ if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
+ implementsDomRange = true;
+ }
+ testRange.detach();
+ }
+
+ var body = isHostObject(document, "body") ? document.body : document.getElementsByTagName("body")[0];
+
+ if (body && isHostMethod(body, "createTextRange")) {
+ testRange = body.createTextRange();
+ if (isTextRange(testRange)) {
+ implementsTextRange = true;
+ }
+ }
+
+ if (!implementsDomRange && !implementsTextRange) {
+ fail("Neither Range nor TextRange are implemented");
+ }
+
+ api.initialized = true;
+ api.features = {
+ implementsDomRange: implementsDomRange,
+ implementsTextRange: implementsTextRange
+ };
+
+ // Initialize modules and call init listeners
+ var allListeners = moduleInitializers.concat(initListeners);
+ for (var i = 0, len = allListeners.length; i < len; ++i) {
+ try {
+ allListeners[i](api);
+ } catch (ex) {
+ if (isHostObject(window, "console") && isHostMethod(window.console, "log")) {
+ window.console.log("Init listener threw an exception. Continuing.", ex);
+ }
+
+ }
+ }
+ }
+
+ // Allow external scripts to initialize this library in case it's loaded after the document has loaded
+ api.init = init;
+
+ // Execute listener immediately if already initialized
+ api.addInitListener = function(listener) {
+ if (api.initialized) {
+ listener(api);
+ } else {
+ initListeners.push(listener);
+ }
+ };
+
+ var createMissingNativeApiListeners = [];
+
+ api.addCreateMissingNativeApiListener = function(listener) {
+ createMissingNativeApiListeners.push(listener);
+ };
+
+ function createMissingNativeApi(win) {
+ win = win || window;
+ init();
+
+ // Notify listeners
+ for (var i = 0, len = createMissingNativeApiListeners.length; i < len; ++i) {
+ createMissingNativeApiListeners[i](win);
+ }
+ }
+
+ api.createMissingNativeApi = createMissingNativeApi;
+
+ /**
+ * @constructor
+ */
+ function Module(name) {
+ this.name = name;
+ this.initialized = false;
+ this.supported = false;
+ }
+
+ Module.prototype.fail = function(reason) {
+ this.initialized = true;
+ this.supported = false;
+
+ throw new Error("Module '" + this.name + "' failed to load: " + reason);
+ };
+
+ Module.prototype.warn = function(msg) {
+ api.warn("Module " + this.name + ": " + msg);
+ };
+
+ Module.prototype.createError = function(msg) {
+ return new Error("Error in Rangy " + this.name + " module: " + msg);
+ };
+
+ api.createModule = function(name, initFunc) {
+ var module = new Module(name);
+ api.modules[name] = module;
+
+ moduleInitializers.push(function(api) {
+ initFunc(api, module);
+ module.initialized = true;
+ module.supported = true;
+ });
+ };
+
+ api.requireModules = function(modules) {
+ for (var i = 0, len = modules.length, module, moduleName; i < len; ++i) {
+ moduleName = modules[i];
+ module = api.modules[moduleName];
+ if (!module || !(module instanceof Module)) {
+ throw new Error("Module '" + moduleName + "' not found");
+ }
+ if (!module.supported) {
+ throw new Error("Module '" + moduleName + "' not supported");
+ }
+ }
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Wait for document to load before running tests
+
+ var docReady = false;
+
+ var loadHandler = function(e) {
+
+ if (!docReady) {
+ docReady = true;
+ if (!api.initialized) {
+ init();
+ }
+ }
+ };
+
+ // Test whether we have window and document objects that we will need
+ if (typeof window == UNDEFINED) {
+ fail("No window found");
+ return;
+ }
+ if (typeof document == UNDEFINED) {
+ fail("No document found");
+ return;
+ }
+
+ if (isHostMethod(document, "addEventListener")) {
+ document.addEventListener("DOMContentLoaded", loadHandler, false);
+ }
+
+ // Add a fallback in case the DOMContentLoaded event isn't supported
+ if (isHostMethod(window, "addEventListener")) {
+ window.addEventListener("load", loadHandler, false);
+ } else if (isHostMethod(window, "attachEvent")) {
+ window.attachEvent("onload", loadHandler);
+ } else {
+ fail("Window does not have required addEventListener or attachEvent method");
+ }
+
+ return api;
+})();
+rangy.createModule("DomUtil", function(api, module) {
+
+ var UNDEF = "undefined";
+ var util = api.util;
+
+ // Perform feature tests
+ if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
+ module.fail("document missing a Node creation method");
+ }
+
+ if (!util.isHostMethod(document, "getElementsByTagName")) {
+ module.fail("document missing getElementsByTagName method");
+ }
+
+ var el = document.createElement("div");
+ if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
+ !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
+ module.fail("Incomplete Element implementation");
+ }
+
+ // innerHTML is required for Range's createContextualFragment method
+ if (!util.isHostProperty(el, "innerHTML")) {
+ module.fail("Element is missing innerHTML property");
+ }
+
+ var textNode = document.createTextNode("test");
+ if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
+ !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
+ !util.areHostProperties(textNode, ["data"]))) {
+ module.fail("Incomplete Text Node implementation");
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
+ // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
+ // contains just the document as a single element and the value searched for is the document.
+ var arrayContains = /*Array.prototype.indexOf ?
+ function(arr, val) {
+ return arr.indexOf(val) > -1;
+ }:*/
+
+ function(arr, val) {
+ var i = arr.length;
+ while (i--) {
+ if (arr[i] === val) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
+ function isHtmlNamespace(node) {
+ var ns;
+ return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
+ }
+
+ function parentElement(node) {
+ var parent = node.parentNode;
+ return (parent.nodeType == 1) ? parent : null;
+ }
+
+ function getNodeIndex(node) {
+ var i = 0;
+ while( (node = node.previousSibling) ) {
+ i++;
+ }
+ return i;
+ }
+
+ function getNodeLength(node) {
+ var childNodes;
+ return isCharacterDataNode(node) ? node.length : ((childNodes = node.childNodes) ? childNodes.length : 0);
+ }
+
+ function getCommonAncestor(node1, node2) {
+ var ancestors = [], n;
+ for (n = node1; n; n = n.parentNode) {
+ ancestors.push(n);
+ }
+
+ for (n = node2; n; n = n.parentNode) {
+ if (arrayContains(ancestors, n)) {
+ return n;
+ }
+ }
+
+ return null;
+ }
+
+ function isAncestorOf(ancestor, descendant, selfIsAncestor) {
+ var n = selfIsAncestor ? descendant : descendant.parentNode;
+ while (n) {
+ if (n === ancestor) {
+ return true;
+ } else {
+ n = n.parentNode;
+ }
+ }
+ return false;
+ }
+
+ function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
+ var p, n = selfIsAncestor ? node : node.parentNode;
+ while (n) {
+ p = n.parentNode;
+ if (p === ancestor) {
+ return n;
+ }
+ n = p;
+ }
+ return null;
+ }
+
+ function isCharacterDataNode(node) {
+ var t = node.nodeType;
+ return t == 3 || t == 4 || t == 8 ; // Text, CDataSection or Comment
+ }
+
+ function insertAfter(node, precedingNode) {
+ var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode;
+ if (nextNode) {
+ parent.insertBefore(node, nextNode);
+ } else {
+ parent.appendChild(node);
+ }
+ return node;
+ }
+
+ // Note that we cannot use splitText() because it is bugridden in IE 9.
+ function splitDataNode(node, index) {
+ var newNode = node.cloneNode(false);
+ newNode.deleteData(0, index);
+ node.deleteData(index, node.length - index);
+ insertAfter(newNode, node);
+ return newNode;
+ }
+
+ function getDocument(node) {
+ if (node.nodeType == 9) {
+ return node;
+ } else if (typeof node.ownerDocument != UNDEF) {
+ return node.ownerDocument;
+ } else if (typeof node.document != UNDEF) {
+ return node.document;
+ } else if (node.parentNode) {
+ return getDocument(node.parentNode);
+ } else {
+ throw new Error("getDocument: no document found for node");
+ }
+ }
+
+ function getWindow(node) {
+ var doc = getDocument(node);
+ if (typeof doc.defaultView != UNDEF) {
+ return doc.defaultView;
+ } else if (typeof doc.parentWindow != UNDEF) {
+ return doc.parentWindow;
+ } else {
+ throw new Error("Cannot get a window object for node");
+ }
+ }
+
+ function getIframeDocument(iframeEl) {
+ if (typeof iframeEl.contentDocument != UNDEF) {
+ return iframeEl.contentDocument;
+ } else if (typeof iframeEl.contentWindow != UNDEF) {
+ return iframeEl.contentWindow.document;
+ } else {
+ throw new Error("getIframeWindow: No Document object found for iframe element");
+ }
+ }
+
+ function getIframeWindow(iframeEl) {
+ if (typeof iframeEl.contentWindow != UNDEF) {
+ return iframeEl.contentWindow;
+ } else if (typeof iframeEl.contentDocument != UNDEF) {
+ return iframeEl.contentDocument.defaultView;
+ } else {
+ throw new Error("getIframeWindow: No Window object found for iframe element");
+ }
+ }
+
+ function getBody(doc) {
+ return util.isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
+ }
+
+ function getRootContainer(node) {
+ var parent;
+ while ( (parent = node.parentNode) ) {
+ node = parent;
+ }
+ return node;
+ }
+
+ function comparePoints(nodeA, offsetA, nodeB, offsetB) {
+ // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
+ var nodeC, root, childA, childB, n;
+ if (nodeA == nodeB) {
+
+ // Case 1: nodes are the same
+ return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
+ } else if ( (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) ) {
+
+ // Case 2: node C (container B or an ancestor) is a child node of A
+ return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
+ } else if ( (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) ) {
+
+ // Case 3: node C (container A or an ancestor) is a child node of B
+ return getNodeIndex(nodeC) < offsetB ? -1 : 1;
+ } else {
+
+ // Case 4: containers are siblings or descendants of siblings
+ root = getCommonAncestor(nodeA, nodeB);
+ childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
+ childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);
+
+ if (childA === childB) {
+ // This shouldn't be possible
+
+ throw new Error("comparePoints got to case 4 and childA and childB are the same!");
+ } else {
+ n = root.firstChild;
+ while (n) {
+ if (n === childA) {
+ return -1;
+ } else if (n === childB) {
+ return 1;
+ }
+ n = n.nextSibling;
+ }
+ throw new Error("Should not be here!");
+ }
+ }
+ }
+
+ function fragmentFromNodeChildren(node) {
+ var fragment = getDocument(node).createDocumentFragment(), child;
+ while ( (child = node.firstChild) ) {
+ fragment.appendChild(child);
+ }
+ return fragment;
+ }
+
+ function inspectNode(node) {
+ if (!node) {
+ return "[No node]";
+ }
+ if (isCharacterDataNode(node)) {
+ return '"' + node.data + '"';
+ } else if (node.nodeType == 1) {
+ var idAttr = node.id ? ' id="' + node.id + '"' : "";
+ return "<" + node.nodeName + idAttr + ">[" + node.childNodes.length + "]";
+ } else {
+ return node.nodeName;
+ }
+ }
+
+ /**
+ * @constructor
+ */
+ function NodeIterator(root) {
+ this.root = root;
+ this._next = root;
+ }
+
+ NodeIterator.prototype = {
+ _current: null,
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ var n = this._current = this._next;
+ var child, next;
+ if (this._current) {
+ child = n.firstChild;
+ if (child) {
+ this._next = child;
+ } else {
+ next = null;
+ while ((n !== this.root) && !(next = n.nextSibling)) {
+ n = n.parentNode;
+ }
+ this._next = next;
+ }
+ }
+ return this._current;
+ },
+
+ detach: function() {
+ this._current = this._next = this.root = null;
+ }
+ };
+
+ function createIterator(root) {
+ return new NodeIterator(root);
+ }
+
+ /**
+ * @constructor
+ */
+ function DomPosition(node, offset) {
+ this.node = node;
+ this.offset = offset;
+ }
+
+ DomPosition.prototype = {
+ equals: function(pos) {
+ return this.node === pos.node & this.offset == pos.offset;
+ },
+
+ inspect: function() {
+ return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
+ }
+ };
+
+ /**
+ * @constructor
+ */
+ function DOMException(codeName) {
+ this.code = this[codeName];
+ this.codeName = codeName;
+ this.message = "DOMException: " + this.codeName;
+ }
+
+ DOMException.prototype = {
+ INDEX_SIZE_ERR: 1,
+ HIERARCHY_REQUEST_ERR: 3,
+ WRONG_DOCUMENT_ERR: 4,
+ NO_MODIFICATION_ALLOWED_ERR: 7,
+ NOT_FOUND_ERR: 8,
+ NOT_SUPPORTED_ERR: 9,
+ INVALID_STATE_ERR: 11
+ };
+
+ DOMException.prototype.toString = function() {
+ return this.message;
+ };
+
+ api.dom = {
+ arrayContains: arrayContains,
+ isHtmlNamespace: isHtmlNamespace,
+ parentElement: parentElement,
+ getNodeIndex: getNodeIndex,
+ getNodeLength: getNodeLength,
+ getCommonAncestor: getCommonAncestor,
+ isAncestorOf: isAncestorOf,
+ getClosestAncestorIn: getClosestAncestorIn,
+ isCharacterDataNode: isCharacterDataNode,
+ insertAfter: insertAfter,
+ splitDataNode: splitDataNode,
+ getDocument: getDocument,
+ getWindow: getWindow,
+ getIframeWindow: getIframeWindow,
+ getIframeDocument: getIframeDocument,
+ getBody: getBody,
+ getRootContainer: getRootContainer,
+ comparePoints: comparePoints,
+ inspectNode: inspectNode,
+ fragmentFromNodeChildren: fragmentFromNodeChildren,
+ createIterator: createIterator,
+ DomPosition: DomPosition
+ };
+
+ api.DOMException = DOMException;
+});rangy.createModule("DomRange", function(api, module) {
+ api.requireModules( ["DomUtil"] );
+
+
+ var dom = api.dom;
+ var DomPosition = dom.DomPosition;
+ var DOMException = api.DOMException;
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Utility functions
+
+ function isNonTextPartiallySelected(node, range) {
+ return (node.nodeType != 3) &&
+ (dom.isAncestorOf(node, range.startContainer, true) || dom.isAncestorOf(node, range.endContainer, true));
+ }
+
+ function getRangeDocument(range) {
+ return dom.getDocument(range.startContainer);
+ }
+
+ function dispatchEvent(range, type, args) {
+ var listeners = range._listeners[type];
+ if (listeners) {
+ for (var i = 0, len = listeners.length; i < len; ++i) {
+ listeners[i].call(range, {target: range, args: args});
+ }
+ }
+ }
+
+ function getBoundaryBeforeNode(node) {
+ return new DomPosition(node.parentNode, dom.getNodeIndex(node));
+ }
+
+ function getBoundaryAfterNode(node) {
+ return new DomPosition(node.parentNode, dom.getNodeIndex(node) + 1);
+ }
+
+ function insertNodeAtPosition(node, n, o) {
+ var firstNodeInserted = node.nodeType == 11 ? node.firstChild : node;
+ if (dom.isCharacterDataNode(n)) {
+ if (o == n.length) {
+ dom.insertAfter(node, n);
+ } else {
+ n.parentNode.insertBefore(node, o == 0 ? n : dom.splitDataNode(n, o));
+ }
+ } else if (o >= n.childNodes.length) {
+ n.appendChild(node);
+ } else {
+ n.insertBefore(node, n.childNodes[o]);
+ }
+ return firstNodeInserted;
+ }
+
+ function cloneSubtree(iterator) {
+ var partiallySelected;
+ for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
+ partiallySelected = iterator.isPartiallySelectedSubtree();
+
+ node = node.cloneNode(!partiallySelected);
+ if (partiallySelected) {
+ subIterator = iterator.getSubtreeIterator();
+ node.appendChild(cloneSubtree(subIterator));
+ subIterator.detach(true);
+ }
+
+ if (node.nodeType == 10) { // DocumentType
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+ frag.appendChild(node);
+ }
+ return frag;
+ }
+
+ function iterateSubtree(rangeIterator, func, iteratorState) {
+ var it, n;
+ iteratorState = iteratorState || { stop: false };
+ for (var node, subRangeIterator; node = rangeIterator.next(); ) {
+ //log.debug("iterateSubtree, partially selected: " + rangeIterator.isPartiallySelectedSubtree(), nodeToString(node));
+ if (rangeIterator.isPartiallySelectedSubtree()) {
+ // The node is partially selected by the Range, so we can use a new RangeIterator on the portion of the
+ // node selected by the Range.
+ if (func(node) === false) {
+ iteratorState.stop = true;
+ return;
+ } else {
+ subRangeIterator = rangeIterator.getSubtreeIterator();
+ iterateSubtree(subRangeIterator, func, iteratorState);
+ subRangeIterator.detach(true);
+ if (iteratorState.stop) {
+ return;
+ }
+ }
+ } else {
+ // The whole node is selected, so we can use efficient DOM iteration to iterate over the node and its
+ // descendant
+ it = dom.createIterator(node);
+ while ( (n = it.next()) ) {
+ if (func(n) === false) {
+ iteratorState.stop = true;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ function deleteSubtree(iterator) {
+ var subIterator;
+ while (iterator.next()) {
+ if (iterator.isPartiallySelectedSubtree()) {
+ subIterator = iterator.getSubtreeIterator();
+ deleteSubtree(subIterator);
+ subIterator.detach(true);
+ } else {
+ iterator.remove();
+ }
+ }
+ }
+
+ function extractSubtree(iterator) {
+
+ for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
+
+
+ if (iterator.isPartiallySelectedSubtree()) {
+ node = node.cloneNode(false);
+ subIterator = iterator.getSubtreeIterator();
+ node.appendChild(extractSubtree(subIterator));
+ subIterator.detach(true);
+ } else {
+ iterator.remove();
+ }
+ if (node.nodeType == 10) { // DocumentType
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+ frag.appendChild(node);
+ }
+ return frag;
+ }
+
+ function getNodesInRange(range, nodeTypes, filter) {
+ //log.info("getNodesInRange, " + nodeTypes.join(","));
+ var filterNodeTypes = !!(nodeTypes && nodeTypes.length), regex;
+ var filterExists = !!filter;
+ if (filterNodeTypes) {
+ regex = new RegExp("^(" + nodeTypes.join("|") + ")$");
+ }
+
+ var nodes = [];
+ iterateSubtree(new RangeIterator(range, false), function(node) {
+ if ((!filterNodeTypes || regex.test(node.nodeType)) && (!filterExists || filter(node))) {
+ nodes.push(node);
+ }
+ });
+ return nodes;
+ }
+
+ function inspect(range) {
+ var name = (typeof range.getName == "undefined") ? "Range" : range.getName();
+ return "[" + name + "(" + dom.inspectNode(range.startContainer) + ":" + range.startOffset + ", " +
+ dom.inspectNode(range.endContainer) + ":" + range.endOffset + ")]";
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // RangeIterator code partially borrows from IERange by Tim Ryan (http://github.com/timcameronryan/IERange)
+
+ /**
+ * @constructor
+ */
+ function RangeIterator(range, clonePartiallySelectedTextNodes) {
+ this.range = range;
+ this.clonePartiallySelectedTextNodes = clonePartiallySelectedTextNodes;
+
+
+
+ if (!range.collapsed) {
+ this.sc = range.startContainer;
+ this.so = range.startOffset;
+ this.ec = range.endContainer;
+ this.eo = range.endOffset;
+ var root = range.commonAncestorContainer;
+
+ if (this.sc === this.ec && dom.isCharacterDataNode(this.sc)) {
+ this.isSingleCharacterDataNode = true;
+ this._first = this._last = this._next = this.sc;
+ } else {
+ this._first = this._next = (this.sc === root && !dom.isCharacterDataNode(this.sc)) ?
+ this.sc.childNodes[this.so] : dom.getClosestAncestorIn(this.sc, root, true);
+ this._last = (this.ec === root && !dom.isCharacterDataNode(this.ec)) ?
+ this.ec.childNodes[this.eo - 1] : dom.getClosestAncestorIn(this.ec, root, true);
+ }
+
+ }
+ }
+
+ RangeIterator.prototype = {
+ _current: null,
+ _next: null,
+ _first: null,
+ _last: null,
+ isSingleCharacterDataNode: false,
+
+ reset: function() {
+ this._current = null;
+ this._next = this._first;
+ },
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ // Move to next node
+ var current = this._current = this._next;
+ if (current) {
+ this._next = (current !== this._last) ? current.nextSibling : null;
+
+ // Check for partially selected text nodes
+ if (dom.isCharacterDataNode(current) && this.clonePartiallySelectedTextNodes) {
+ if (current === this.ec) {
+
+ (current = current.cloneNode(true)).deleteData(this.eo, current.length - this.eo);
+ }
+ if (this._current === this.sc) {
+
+ (current = current.cloneNode(true)).deleteData(0, this.so);
+ }
+ }
+ }
+
+ return current;
+ },
+
+ remove: function() {
+ var current = this._current, start, end;
+
+ if (dom.isCharacterDataNode(current) && (current === this.sc || current === this.ec)) {
+ start = (current === this.sc) ? this.so : 0;
+ end = (current === this.ec) ? this.eo : current.length;
+ if (start != end) {
+ current.deleteData(start, end - start);
+ }
+ } else {
+ if (current.parentNode) {
+ current.parentNode.removeChild(current);
+ } else {
+
+ }
+ }
+ },
+
+ // Checks if the current node is partially selected
+ isPartiallySelectedSubtree: function() {
+ var current = this._current;
+ return isNonTextPartiallySelected(current, this.range);
+ },
+
+ getSubtreeIterator: function() {
+ var subRange;
+ if (this.isSingleCharacterDataNode) {
+ subRange = this.range.cloneRange();
+ subRange.collapse();
+ } else {
+ subRange = new Range(getRangeDocument(this.range));
+ var current = this._current;
+ var startContainer = current, startOffset = 0, endContainer = current, endOffset = dom.getNodeLength(current);
+
+ if (dom.isAncestorOf(current, this.sc, true)) {
+ startContainer = this.sc;
+ startOffset = this.so;
+ }
+ if (dom.isAncestorOf(current, this.ec, true)) {
+ endContainer = this.ec;
+ endOffset = this.eo;
+ }
+
+ updateBoundaries(subRange, startContainer, startOffset, endContainer, endOffset);
+ }
+ return new RangeIterator(subRange, this.clonePartiallySelectedTextNodes);
+ },
+
+ detach: function(detachRange) {
+ if (detachRange) {
+ this.range.detach();
+ }
+ this.range = this._current = this._next = this._first = this._last = this.sc = this.so = this.ec = this.eo = null;
+ }
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Exceptions
+
+ /**
+ * @constructor
+ */
+ function RangeException(codeName) {
+ this.code = this[codeName];
+ this.codeName = codeName;
+ this.message = "RangeException: " + this.codeName;
+ }
+
+ RangeException.prototype = {
+ BAD_BOUNDARYPOINTS_ERR: 1,
+ INVALID_NODE_TYPE_ERR: 2
+ };
+
+ RangeException.prototype.toString = function() {
+ return this.message;
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ /**
+ * Currently iterates through all nodes in the range on creation until I think of a decent way to do it
+ * TODO: Look into making this a proper iterator, not requiring preloading everything first
+ * @constructor
+ */
+ function RangeNodeIterator(range, nodeTypes, filter) {
+ this.nodes = getNodesInRange(range, nodeTypes, filter);
+ this._next = this.nodes[0];
+ this._position = 0;
+ }
+
+ RangeNodeIterator.prototype = {
+ _current: null,
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ this._current = this._next;
+ this._next = this.nodes[ ++this._position ];
+ return this._current;
+ },
+
+ detach: function() {
+ this._current = this._next = this.nodes = null;
+ }
+ };
+
+ var beforeAfterNodeTypes = [1, 3, 4, 5, 7, 8, 10];
+ var rootContainerNodeTypes = [2, 9, 11];
+ var readonlyNodeTypes = [5, 6, 10, 12];
+ var insertableNodeTypes = [1, 3, 4, 5, 7, 8, 10, 11];
+ var surroundNodeTypes = [1, 3, 4, 5, 7, 8];
+
+ function createAncestorFinder(nodeTypes) {
+ return function(node, selfIsAncestor) {
+ var t, n = selfIsAncestor ? node : node.parentNode;
+ while (n) {
+ t = n.nodeType;
+ if (dom.arrayContains(nodeTypes, t)) {
+ return n;
+ }
+ n = n.parentNode;
+ }
+ return null;
+ };
+ }
+
+ var getRootContainer = dom.getRootContainer;
+ var getDocumentOrFragmentContainer = createAncestorFinder( [9, 11] );
+ var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
+ var getDocTypeNotationEntityAncestor = createAncestorFinder( [6, 10, 12] );
+
+ function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
+ if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
+ throw new RangeException("INVALID_NODE_TYPE_ERR");
+ }
+ }
+
+ function assertNotDetached(range) {
+ if (!range.startContainer) {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ }
+
+ function assertValidNodeType(node, invalidTypes) {
+ if (!dom.arrayContains(invalidTypes, node.nodeType)) {
+ throw new RangeException("INVALID_NODE_TYPE_ERR");
+ }
+ }
+
+ function assertValidOffset(node, offset) {
+ if (offset < 0 || offset > (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length)) {
+ throw new DOMException("INDEX_SIZE_ERR");
+ }
+ }
+
+ function assertSameDocumentOrFragment(node1, node2) {
+ if (getDocumentOrFragmentContainer(node1, true) !== getDocumentOrFragmentContainer(node2, true)) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+ }
+
+ function assertNodeNotReadOnly(node) {
+ if (getReadonlyAncestor(node, true)) {
+ throw new DOMException("NO_MODIFICATION_ALLOWED_ERR");
+ }
+ }
+
+ function assertNode(node, codeName) {
+ if (!node) {
+ throw new DOMException(codeName);
+ }
+ }
+
+ function isOrphan(node) {
+ return !dom.arrayContains(rootContainerNodeTypes, node.nodeType) && !getDocumentOrFragmentContainer(node, true);
+ }
+
+ function isValidOffset(node, offset) {
+ return offset <= (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length);
+ }
+
+ function isRangeValid(range) {
+ return (!!range.startContainer && !!range.endContainer
+ && !isOrphan(range.startContainer)
+ && !isOrphan(range.endContainer)
+ && isValidOffset(range.startContainer, range.startOffset)
+ && isValidOffset(range.endContainer, range.endOffset));
+ }
+
+ function assertRangeValid(range) {
+ assertNotDetached(range);
+ if (!isRangeValid(range)) {
+ throw new Error("Range error: Range is no longer valid after DOM mutation (" + range.inspect() + ")");
+ }
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Test the browser's innerHTML support to decide how to implement createContextualFragment
+ var styleEl = document.createElement("style");
+ var htmlParsingConforms = false;
+ try {
+ styleEl.innerHTML = "x";
+ htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
+ } catch (e) {
+ // IE 6 and 7 throw
+ }
+
+ api.features.htmlParsingConforms = htmlParsingConforms;
+
+ var createContextualFragment = htmlParsingConforms ?
+
+ // Implementation as per HTML parsing spec, trusting in the browser's implementation of innerHTML. See
+ // discussion and base code for this implementation at issue 67.
+ // Spec: http://html5.org/specs/dom-parsing.html#extensions-to-the-range-interface
+ // Thanks to Aleks Williams.
+ function(fragmentStr) {
+ // "Let node the context object's start's node."
+ var node = this.startContainer;
+ var doc = dom.getDocument(node);
+
+ // "If the context object's start's node is null, raise an INVALID_STATE_ERR
+ // exception and abort these steps."
+ if (!node) {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+
+ // "Let element be as follows, depending on node's interface:"
+ // Document, Document Fragment: null
+ var el = null;
+
+ // "Element: node"
+ if (node.nodeType == 1) {
+ el = node;
+
+ // "Text, Comment: node's parentElement"
+ } else if (dom.isCharacterDataNode(node)) {
+ el = dom.parentElement(node);
+ }
+
+ // "If either element is null or element's ownerDocument is an HTML document
+ // and element's local name is "html" and element's namespace is the HTML
+ // namespace"
+ if (el === null || (
+ el.nodeName == "HTML"
+ && dom.isHtmlNamespace(dom.getDocument(el).documentElement)
+ && dom.isHtmlNamespace(el)
+ )) {
+
+ // "let element be a new Element with "body" as its local name and the HTML
+ // namespace as its namespace.""
+ el = doc.createElement("body");
+ } else {
+ el = el.cloneNode(false);
+ }
+
+ // "If the node's document is an HTML document: Invoke the HTML fragment parsing algorithm."
+ // "If the node's document is an XML document: Invoke the XML fragment parsing algorithm."
+ // "In either case, the algorithm must be invoked with fragment as the input
+ // and element as the context element."
+ el.innerHTML = fragmentStr;
+
+ // "If this raises an exception, then abort these steps. Otherwise, let new
+ // children be the nodes returned."
+
+ // "Let fragment be a new DocumentFragment."
+ // "Append all new children to fragment."
+ // "Return fragment."
+ return dom.fragmentFromNodeChildren(el);
+ } :
+
+ // In this case, innerHTML cannot be trusted, so fall back to a simpler, non-conformant implementation that
+ // previous versions of Rangy used (with the exception of using a body element rather than a div)
+ function(fragmentStr) {
+ assertNotDetached(this);
+ var doc = getRangeDocument(this);
+ var el = doc.createElement("body");
+ el.innerHTML = fragmentStr;
+
+ return dom.fragmentFromNodeChildren(el);
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ var rangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
+ "commonAncestorContainer"];
+
+ var s2s = 0, s2e = 1, e2e = 2, e2s = 3;
+ var n_b = 0, n_a = 1, n_b_a = 2, n_i = 3;
+
+ function RangePrototype() {}
+
+ RangePrototype.prototype = {
+ attachListener: function(type, listener) {
+ this._listeners[type].push(listener);
+ },
+
+ compareBoundaryPoints: function(how, range) {
+ assertRangeValid(this);
+ assertSameDocumentOrFragment(this.startContainer, range.startContainer);
+
+ var nodeA, offsetA, nodeB, offsetB;
+ var prefixA = (how == e2s || how == s2s) ? "start" : "end";
+ var prefixB = (how == s2e || how == s2s) ? "start" : "end";
+ nodeA = this[prefixA + "Container"];
+ offsetA = this[prefixA + "Offset"];
+ nodeB = range[prefixB + "Container"];
+ offsetB = range[prefixB + "Offset"];
+ return dom.comparePoints(nodeA, offsetA, nodeB, offsetB);
+ },
+
+ insertNode: function(node) {
+ assertRangeValid(this);
+ assertValidNodeType(node, insertableNodeTypes);
+ assertNodeNotReadOnly(this.startContainer);
+
+ if (dom.isAncestorOf(node, this.startContainer, true)) {
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+
+ // No check for whether the container of the start of the Range is of a type that does not allow
+ // children of the type of node: the browser's DOM implementation should do this for us when we attempt
+ // to add the node
+
+ var firstNodeInserted = insertNodeAtPosition(node, this.startContainer, this.startOffset);
+ this.setStartBefore(firstNodeInserted);
+ },
+
+ cloneContents: function() {
+ assertRangeValid(this);
+
+ var clone, frag;
+ if (this.collapsed) {
+ return getRangeDocument(this).createDocumentFragment();
+ } else {
+ if (this.startContainer === this.endContainer && dom.isCharacterDataNode(this.startContainer)) {
+ clone = this.startContainer.cloneNode(true);
+ clone.data = clone.data.slice(this.startOffset, this.endOffset);
+ frag = getRangeDocument(this).createDocumentFragment();
+ frag.appendChild(clone);
+ return frag;
+ } else {
+ var iterator = new RangeIterator(this, true);
+ clone = cloneSubtree(iterator);
+ iterator.detach();
+ }
+ return clone;
+ }
+ },
+
+ canSurroundContents: function() {
+ assertRangeValid(this);
+ assertNodeNotReadOnly(this.startContainer);
+ assertNodeNotReadOnly(this.endContainer);
+
+ // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
+ // no non-text nodes.
+ var iterator = new RangeIterator(this, true);
+ var boundariesInvalid = (iterator._first && (isNonTextPartiallySelected(iterator._first, this)) ||
+ (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
+ iterator.detach();
+ return !boundariesInvalid;
+ },
+
+ surroundContents: function(node) {
+ assertValidNodeType(node, surroundNodeTypes);
+
+ if (!this.canSurroundContents()) {
+ throw new RangeException("BAD_BOUNDARYPOINTS_ERR");
+ }
+
+ // Extract the contents
+ var content = this.extractContents();
+
+ // Clear the children of the node
+ if (node.hasChildNodes()) {
+ while (node.lastChild) {
+ node.removeChild(node.lastChild);
+ }
+ }
+
+ // Insert the new node and add the extracted contents
+ insertNodeAtPosition(node, this.startContainer, this.startOffset);
+ node.appendChild(content);
+
+ this.selectNode(node);
+ },
+
+ cloneRange: function() {
+ assertRangeValid(this);
+ var range = new Range(getRangeDocument(this));
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = this[prop];
+ }
+ return range;
+ },
+
+ toString: function() {
+ assertRangeValid(this);
+ var sc = this.startContainer;
+ if (sc === this.endContainer && dom.isCharacterDataNode(sc)) {
+ return (sc.nodeType == 3 || sc.nodeType == 4) ? sc.data.slice(this.startOffset, this.endOffset) : "";
+ } else {
+ var textBits = [], iterator = new RangeIterator(this, true);
+
+ iterateSubtree(iterator, function(node) {
+ // Accept only text or CDATA nodes, not comments
+
+ if (node.nodeType == 3 || node.nodeType == 4) {
+ textBits.push(node.data);
+ }
+ });
+ iterator.detach();
+ return textBits.join("");
+ }
+ },
+
+ // The methods below are all non-standard. The following batch were introduced by Mozilla but have since
+ // been removed from Mozilla.
+
+ compareNode: function(node) {
+ assertRangeValid(this);
+
+ var parent = node.parentNode;
+ var nodeIndex = dom.getNodeIndex(node);
+
+ if (!parent) {
+ throw new DOMException("NOT_FOUND_ERR");
+ }
+
+ var startComparison = this.comparePoint(parent, nodeIndex),
+ endComparison = this.comparePoint(parent, nodeIndex + 1);
+
+ if (startComparison < 0) { // Node starts before
+ return (endComparison > 0) ? n_b_a : n_b;
+ } else {
+ return (endComparison > 0) ? n_a : n_i;
+ }
+ },
+
+ comparePoint: function(node, offset) {
+ assertRangeValid(this);
+ assertNode(node, "HIERARCHY_REQUEST_ERR");
+ assertSameDocumentOrFragment(node, this.startContainer);
+
+ if (dom.comparePoints(node, offset, this.startContainer, this.startOffset) < 0) {
+ return -1;
+ } else if (dom.comparePoints(node, offset, this.endContainer, this.endOffset) > 0) {
+ return 1;
+ }
+ return 0;
+ },
+
+ createContextualFragment: createContextualFragment,
+
+ toHtml: function() {
+ assertRangeValid(this);
+ var container = getRangeDocument(this).createElement("div");
+ container.appendChild(this.cloneContents());
+ return container.innerHTML;
+ },
+
+ // touchingIsIntersecting determines whether this method considers a node that borders a range intersects
+ // with it (as in WebKit) or not (as in Gecko pre-1.9, and the default)
+ intersectsNode: function(node, touchingIsIntersecting) {
+ assertRangeValid(this);
+ assertNode(node, "NOT_FOUND_ERR");
+ if (dom.getDocument(node) !== getRangeDocument(this)) {
+ return false;
+ }
+
+ var parent = node.parentNode, offset = dom.getNodeIndex(node);
+ assertNode(parent, "NOT_FOUND_ERR");
+
+ var startComparison = dom.comparePoints(parent, offset, this.endContainer, this.endOffset),
+ endComparison = dom.comparePoints(parent, offset + 1, this.startContainer, this.startOffset);
+
+ return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
+ },
+
+
+ isPointInRange: function(node, offset) {
+ assertRangeValid(this);
+ assertNode(node, "HIERARCHY_REQUEST_ERR");
+ assertSameDocumentOrFragment(node, this.startContainer);
+
+ return (dom.comparePoints(node, offset, this.startContainer, this.startOffset) >= 0) &&
+ (dom.comparePoints(node, offset, this.endContainer, this.endOffset) <= 0);
+ },
+
+ // The methods below are non-standard and invented by me.
+
+ // Sharing a boundary start-to-end or end-to-start does not count as intersection.
+ intersectsRange: function(range, touchingIsIntersecting) {
+ assertRangeValid(this);
+
+ if (getRangeDocument(range) != getRangeDocument(this)) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+
+ var startComparison = dom.comparePoints(this.startContainer, this.startOffset, range.endContainer, range.endOffset),
+ endComparison = dom.comparePoints(this.endContainer, this.endOffset, range.startContainer, range.startOffset);
+
+ return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
+ },
+
+ intersection: function(range) {
+ if (this.intersectsRange(range)) {
+ var startComparison = dom.comparePoints(this.startContainer, this.startOffset, range.startContainer, range.startOffset),
+ endComparison = dom.comparePoints(this.endContainer, this.endOffset, range.endContainer, range.endOffset);
+
+ var intersectionRange = this.cloneRange();
+
+ if (startComparison == -1) {
+ intersectionRange.setStart(range.startContainer, range.startOffset);
+ }
+ if (endComparison == 1) {
+ intersectionRange.setEnd(range.endContainer, range.endOffset);
+ }
+ return intersectionRange;
+ }
+ return null;
+ },
+
+ union: function(range) {
+ if (this.intersectsRange(range, true)) {
+ var unionRange = this.cloneRange();
+ if (dom.comparePoints(range.startContainer, range.startOffset, this.startContainer, this.startOffset) == -1) {
+ unionRange.setStart(range.startContainer, range.startOffset);
+ }
+ if (dom.comparePoints(range.endContainer, range.endOffset, this.endContainer, this.endOffset) == 1) {
+ unionRange.setEnd(range.endContainer, range.endOffset);
+ }
+ return unionRange;
+ } else {
+ throw new RangeException("Ranges do not intersect");
+ }
+ },
+
+ containsNode: function(node, allowPartial) {
+ if (allowPartial) {
+ return this.intersectsNode(node, false);
+ } else {
+ return this.compareNode(node) == n_i;
+ }
+ },
+
+ containsNodeContents: function(node) {
+ return this.comparePoint(node, 0) >= 0 && this.comparePoint(node, dom.getNodeLength(node)) <= 0;
+ },
+
+ containsRange: function(range) {
+ return this.intersection(range).equals(range);
+ },
+
+ containsNodeText: function(node) {
+ var nodeRange = this.cloneRange();
+ nodeRange.selectNode(node);
+ var textNodes = nodeRange.getNodes([3]);
+ if (textNodes.length > 0) {
+ nodeRange.setStart(textNodes[0], 0);
+ var lastTextNode = textNodes.pop();
+ nodeRange.setEnd(lastTextNode, lastTextNode.length);
+ var contains = this.containsRange(nodeRange);
+ nodeRange.detach();
+ return contains;
+ } else {
+ return this.containsNodeContents(node);
+ }
+ },
+
+ createNodeIterator: function(nodeTypes, filter) {
+ assertRangeValid(this);
+ return new RangeNodeIterator(this, nodeTypes, filter);
+ },
+
+ getNodes: function(nodeTypes, filter) {
+ assertRangeValid(this);
+ return getNodesInRange(this, nodeTypes, filter);
+ },
+
+ getDocument: function() {
+ return getRangeDocument(this);
+ },
+
+ collapseBefore: function(node) {
+ assertNotDetached(this);
+
+ this.setEndBefore(node);
+ this.collapse(false);
+ },
+
+ collapseAfter: function(node) {
+ assertNotDetached(this);
+
+ this.setStartAfter(node);
+ this.collapse(true);
+ },
+
+ getName: function() {
+ return "DomRange";
+ },
+
+ equals: function(range) {
+ return Range.rangesEqual(this, range);
+ },
+
+ isValid: function() {
+ return isRangeValid(this);
+ },
+
+ inspect: function() {
+ return inspect(this);
+ }
+ };
+
+ function copyComparisonConstantsToObject(obj) {
+ obj.START_TO_START = s2s;
+ obj.START_TO_END = s2e;
+ obj.END_TO_END = e2e;
+ obj.END_TO_START = e2s;
+
+ obj.NODE_BEFORE = n_b;
+ obj.NODE_AFTER = n_a;
+ obj.NODE_BEFORE_AND_AFTER = n_b_a;
+ obj.NODE_INSIDE = n_i;
+ }
+
+ function copyComparisonConstants(constructor) {
+ copyComparisonConstantsToObject(constructor);
+ copyComparisonConstantsToObject(constructor.prototype);
+ }
+
+ function createRangeContentRemover(remover, boundaryUpdater) {
+ return function() {
+ assertRangeValid(this);
+
+ var sc = this.startContainer, so = this.startOffset, root = this.commonAncestorContainer;
+
+ var iterator = new RangeIterator(this, true);
+
+ // Work out where to position the range after content removal
+ var node, boundary;
+ if (sc !== root) {
+ node = dom.getClosestAncestorIn(sc, root, true);
+ boundary = getBoundaryAfterNode(node);
+ sc = boundary.node;
+ so = boundary.offset;
+ }
+
+ // Check none of the range is read-only
+ iterateSubtree(iterator, assertNodeNotReadOnly);
+
+ iterator.reset();
+
+ // Remove the content
+ var returnValue = remover(iterator);
+ iterator.detach();
+
+ // Move to the new position
+ boundaryUpdater(this, sc, so, sc, so);
+
+ return returnValue;
+ };
+ }
+
+ function createPrototypeRange(constructor, boundaryUpdater, detacher) {
+ function createBeforeAfterNodeSetter(isBefore, isStart) {
+ return function(node) {
+ assertNotDetached(this);
+ assertValidNodeType(node, beforeAfterNodeTypes);
+ assertValidNodeType(getRootContainer(node), rootContainerNodeTypes);
+
+ var boundary = (isBefore ? getBoundaryBeforeNode : getBoundaryAfterNode)(node);
+ (isStart ? setRangeStart : setRangeEnd)(this, boundary.node, boundary.offset);
+ };
+ }
+
+ function setRangeStart(range, node, offset) {
+ var ec = range.endContainer, eo = range.endOffset;
+ if (node !== range.startContainer || offset !== range.startOffset) {
+ // Check the root containers of the range and the new boundary, and also check whether the new boundary
+ // is after the current end. In either case, collapse the range to the new position
+ if (getRootContainer(node) != getRootContainer(ec) || dom.comparePoints(node, offset, ec, eo) == 1) {
+ ec = node;
+ eo = offset;
+ }
+ boundaryUpdater(range, node, offset, ec, eo);
+ }
+ }
+
+ function setRangeEnd(range, node, offset) {
+ var sc = range.startContainer, so = range.startOffset;
+ if (node !== range.endContainer || offset !== range.endOffset) {
+ // Check the root containers of the range and the new boundary, and also check whether the new boundary
+ // is after the current end. In either case, collapse the range to the new position
+ if (getRootContainer(node) != getRootContainer(sc) || dom.comparePoints(node, offset, sc, so) == -1) {
+ sc = node;
+ so = offset;
+ }
+ boundaryUpdater(range, sc, so, node, offset);
+ }
+ }
+
+ function setRangeStartAndEnd(range, node, offset) {
+ if (node !== range.startContainer || offset !== range.startOffset || node !== range.endContainer || offset !== range.endOffset) {
+ boundaryUpdater(range, node, offset, node, offset);
+ }
+ }
+
+ constructor.prototype = new RangePrototype();
+
+ api.util.extend(constructor.prototype, {
+ setStart: function(node, offset) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeStart(this, node, offset);
+ },
+
+ setEnd: function(node, offset) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeEnd(this, node, offset);
+ },
+
+ setStartBefore: createBeforeAfterNodeSetter(true, true),
+ setStartAfter: createBeforeAfterNodeSetter(false, true),
+ setEndBefore: createBeforeAfterNodeSetter(true, false),
+ setEndAfter: createBeforeAfterNodeSetter(false, false),
+
+ collapse: function(isStart) {
+ assertRangeValid(this);
+ if (isStart) {
+ boundaryUpdater(this, this.startContainer, this.startOffset, this.startContainer, this.startOffset);
+ } else {
+ boundaryUpdater(this, this.endContainer, this.endOffset, this.endContainer, this.endOffset);
+ }
+ },
+
+ selectNodeContents: function(node) {
+ // This doesn't seem well specified: the spec talks only about selecting the node's contents, which
+ // could be taken to mean only its children. However, browsers implement this the same as selectNode for
+ // text nodes, so I shall do likewise
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+
+ boundaryUpdater(this, node, 0, node, dom.getNodeLength(node));
+ },
+
+ selectNode: function(node) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, false);
+ assertValidNodeType(node, beforeAfterNodeTypes);
+
+ var start = getBoundaryBeforeNode(node), end = getBoundaryAfterNode(node);
+ boundaryUpdater(this, start.node, start.offset, end.node, end.offset);
+ },
+
+ extractContents: createRangeContentRemover(extractSubtree, boundaryUpdater),
+
+ deleteContents: createRangeContentRemover(deleteSubtree, boundaryUpdater),
+
+ canSurroundContents: function() {
+ assertRangeValid(this);
+ assertNodeNotReadOnly(this.startContainer);
+ assertNodeNotReadOnly(this.endContainer);
+
+ // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
+ // no non-text nodes.
+ var iterator = new RangeIterator(this, true);
+ var boundariesInvalid = (iterator._first && (isNonTextPartiallySelected(iterator._first, this)) ||
+ (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
+ iterator.detach();
+ return !boundariesInvalid;
+ },
+
+ detach: function() {
+ detacher(this);
+ },
+
+ splitBoundaries: function() {
+ assertRangeValid(this);
+
+
+ var sc = this.startContainer, so = this.startOffset, ec = this.endContainer, eo = this.endOffset;
+ var startEndSame = (sc === ec);
+
+ if (dom.isCharacterDataNode(ec) && eo > 0 && eo < ec.length) {
+ dom.splitDataNode(ec, eo);
+
+ }
+
+ if (dom.isCharacterDataNode(sc) && so > 0 && so < sc.length) {
+
+ sc = dom.splitDataNode(sc, so);
+ if (startEndSame) {
+ eo -= so;
+ ec = sc;
+ } else if (ec == sc.parentNode && eo >= dom.getNodeIndex(sc)) {
+ eo++;
+ }
+ so = 0;
+
+ }
+ boundaryUpdater(this, sc, so, ec, eo);
+ },
+
+ normalizeBoundaries: function() {
+ assertRangeValid(this);
+
+ var sc = this.startContainer, so = this.startOffset, ec = this.endContainer, eo = this.endOffset;
+
+ var mergeForward = function(node) {
+ var sibling = node.nextSibling;
+ if (sibling && sibling.nodeType == node.nodeType) {
+ ec = node;
+ eo = node.length;
+ node.appendData(sibling.data);
+ sibling.parentNode.removeChild(sibling);
+ }
+ };
+
+ var mergeBackward = function(node) {
+ var sibling = node.previousSibling;
+ if (sibling && sibling.nodeType == node.nodeType) {
+ sc = node;
+ var nodeLength = node.length;
+ so = sibling.length;
+ node.insertData(0, sibling.data);
+ sibling.parentNode.removeChild(sibling);
+ if (sc == ec) {
+ eo += so;
+ ec = sc;
+ } else if (ec == node.parentNode) {
+ var nodeIndex = dom.getNodeIndex(node);
+ if (eo == nodeIndex) {
+ ec = node;
+ eo = nodeLength;
+ } else if (eo > nodeIndex) {
+ eo--;
+ }
+ }
+ }
+ };
+
+ var normalizeStart = true;
+
+ if (dom.isCharacterDataNode(ec)) {
+ if (ec.length == eo) {
+ mergeForward(ec);
+ }
+ } else {
+ if (eo > 0) {
+ var endNode = ec.childNodes[eo - 1];
+ if (endNode && dom.isCharacterDataNode(endNode)) {
+ mergeForward(endNode);
+ }
+ }
+ normalizeStart = !this.collapsed;
+ }
+
+ if (normalizeStart) {
+ if (dom.isCharacterDataNode(sc)) {
+ if (so == 0) {
+ mergeBackward(sc);
+ }
+ } else {
+ if (so < sc.childNodes.length) {
+ var startNode = sc.childNodes[so];
+ if (startNode && dom.isCharacterDataNode(startNode)) {
+ mergeBackward(startNode);
+ }
+ }
+ }
+ } else {
+ sc = ec;
+ so = eo;
+ }
+
+ boundaryUpdater(this, sc, so, ec, eo);
+ },
+
+ collapseToPoint: function(node, offset) {
+ assertNotDetached(this);
+
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeStartAndEnd(this, node, offset);
+ }
+ });
+
+ copyComparisonConstants(constructor);
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Updates commonAncestorContainer and collapsed after boundary change
+ function updateCollapsedAndCommonAncestor(range) {
+ range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
+ range.commonAncestorContainer = range.collapsed ?
+ range.startContainer : dom.getCommonAncestor(range.startContainer, range.endContainer);
+ }
+
+ function updateBoundaries(range, startContainer, startOffset, endContainer, endOffset) {
+ var startMoved = (range.startContainer !== startContainer || range.startOffset !== startOffset);
+ var endMoved = (range.endContainer !== endContainer || range.endOffset !== endOffset);
+
+ range.startContainer = startContainer;
+ range.startOffset = startOffset;
+ range.endContainer = endContainer;
+ range.endOffset = endOffset;
+
+ updateCollapsedAndCommonAncestor(range);
+ dispatchEvent(range, "boundarychange", {startMoved: startMoved, endMoved: endMoved});
+ }
+
+ function detach(range) {
+ assertNotDetached(range);
+ range.startContainer = range.startOffset = range.endContainer = range.endOffset = null;
+ range.collapsed = range.commonAncestorContainer = null;
+ dispatchEvent(range, "detach", null);
+ range._listeners = null;
+ }
+
+ /**
+ * @constructor
+ */
+ function Range(doc) {
+ this.startContainer = doc;
+ this.startOffset = 0;
+ this.endContainer = doc;
+ this.endOffset = 0;
+ this._listeners = {
+ boundarychange: [],
+ detach: []
+ };
+ updateCollapsedAndCommonAncestor(this);
+ }
+
+ createPrototypeRange(Range, updateBoundaries, detach);
+
+ api.rangePrototype = RangePrototype.prototype;
+
+ Range.rangeProperties = rangeProperties;
+ Range.RangeIterator = RangeIterator;
+ Range.copyComparisonConstants = copyComparisonConstants;
+ Range.createPrototypeRange = createPrototypeRange;
+ Range.inspect = inspect;
+ Range.getRangeDocument = getRangeDocument;
+ Range.rangesEqual = function(r1, r2) {
+ return r1.startContainer === r2.startContainer &&
+ r1.startOffset === r2.startOffset &&
+ r1.endContainer === r2.endContainer &&
+ r1.endOffset === r2.endOffset;
+ };
+
+ api.DomRange = Range;
+ api.RangeException = RangeException;
+});rangy.createModule("WrappedRange", function(api, module) {
+ api.requireModules( ["DomUtil", "DomRange"] );
+
+ /**
+ * @constructor
+ */
+ var WrappedRange;
+ var dom = api.dom;
+ var DomPosition = dom.DomPosition;
+ var DomRange = api.DomRange;
+
+
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ /*
+ This is a workaround for a bug where IE returns the wrong container element from the TextRange's parentElement()
+ method. For example, in the following (where pipes denote the selection boundaries):
+
+
| a
b |
+
+ var range = document.selection.createRange();
+ alert(range.parentElement().id); // Should alert "ul" but alerts "b"
+
+ This method returns the common ancestor node of the following:
+ - the parentElement() of the textRange
+ - the parentElement() of the textRange after calling collapse(true)
+ - the parentElement() of the textRange after calling collapse(false)
+ */
+ function getTextRangeContainerElement(textRange) {
+ var parentEl = textRange.parentElement();
+
+ var range = textRange.duplicate();
+ range.collapse(true);
+ var startEl = range.parentElement();
+ range = textRange.duplicate();
+ range.collapse(false);
+ var endEl = range.parentElement();
+ var startEndContainer = (startEl == endEl) ? startEl : dom.getCommonAncestor(startEl, endEl);
+
+ return startEndContainer == parentEl ? startEndContainer : dom.getCommonAncestor(parentEl, startEndContainer);
+ }
+
+ function textRangeIsCollapsed(textRange) {
+ return textRange.compareEndPoints("StartToEnd", textRange) == 0;
+ }
+
+ // Gets the boundary of a TextRange expressed as a node and an offset within that node. This function started out as
+ // an improved version of code found in Tim Cameron Ryan's IERange (http://code.google.com/p/ierange/) but has
+ // grown, fixing problems with line breaks in preformatted text, adding workaround for IE TextRange bugs, handling
+ // for inputs and images, plus optimizations.
+ function getTextRangeBoundaryPosition(textRange, wholeRangeContainerElement, isStart, isCollapsed) {
+ var workingRange = textRange.duplicate();
+
+ workingRange.collapse(isStart);
+ var containerElement = workingRange.parentElement();
+
+ // Sometimes collapsing a TextRange that's at the start of a text node can move it into the previous node, so
+ // check for that
+ // TODO: Find out when. Workaround for wholeRangeContainerElement may break this
+ if (!dom.isAncestorOf(wholeRangeContainerElement, containerElement, true)) {
+ containerElement = wholeRangeContainerElement;
+
+ }
+
+
+
+ // Deal with nodes that cannot "contain rich HTML markup". In practice, this means form inputs, images and
+ // similar. See http://msdn.microsoft.com/en-us/library/aa703950%28VS.85%29.aspx
+ if (!containerElement.canHaveHTML) {
+ return new DomPosition(containerElement.parentNode, dom.getNodeIndex(containerElement));
+ }
+
+ var workingNode = dom.getDocument(containerElement).createElement("span");
+ var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd";
+ var previousNode, nextNode, boundaryPosition, boundaryNode;
+
+ // Move the working range through the container's children, starting at the end and working backwards, until the
+ // working range reaches or goes past the boundary we're interested in
+ do {
+ containerElement.insertBefore(workingNode, workingNode.previousSibling);
+ workingRange.moveToElementText(workingNode);
+ } while ( (comparison = workingRange.compareEndPoints(workingComparisonType, textRange)) > 0 &&
+ workingNode.previousSibling);
+
+ // We've now reached or gone past the boundary of the text range we're interested in
+ // so have identified the node we want
+ boundaryNode = workingNode.nextSibling;
+
+ if (comparison == -1 && boundaryNode && dom.isCharacterDataNode(boundaryNode)) {
+ // This is a character data node (text, comment, cdata). The working range is collapsed at the start of the
+ // node containing the text range's boundary, so we move the end of the working range to the boundary point
+ // and measure the length of its text to get the boundary's offset within the node.
+ workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange);
+
+
+ var offset;
+
+ if (/[\r\n]/.test(boundaryNode.data)) {
+ /*
+ For the particular case of a boundary within a text node containing line breaks (within a
element,
+ for example), we need a slightly complicated approach to get the boundary's offset in IE. The facts:
+
+ - Each line break is represented as \r in the text node's data/nodeValue properties
+ - Each line break is represented as \r\n in the TextRange's 'text' property
+ - The 'text' property of the TextRange does not contain trailing line breaks
+
+ To get round the problem presented by the final fact above, we can use the fact that TextRange's
+ moveStart() and moveEnd() methods return the actual number of characters moved, which is not necessarily
+ the same as the number of characters it was instructed to move. The simplest approach is to use this to
+ store the characters moved when moving both the start and end of the range to the start of the document
+ body and subtracting the start offset from the end offset (the "move-negative-gazillion" method).
+ However, this is extremely slow when the document is large and the range is near the end of it. Clearly
+ doing the mirror image (i.e. moving the range boundaries to the end of the document) has the same
+ problem.
+
+ Another approach that works is to use moveStart() to move the start boundary of the range up to the end
+ boundary one character at a time and incrementing a counter with the value returned by the moveStart()
+ call. However, the check for whether the start boundary has reached the end boundary is expensive, so
+ this method is slow (although unlike "move-negative-gazillion" is largely unaffected by the location of
+ the range within the document).
+
+ The method below is a hybrid of the two methods above. It uses the fact that a string containing the
+ TextRange's 'text' property with each \r\n converted to a single \r character cannot be longer than the
+ text of the TextRange, so the start of the range is moved that length initially and then a character at
+ a time to make up for any trailing line breaks not contained in the 'text' property. This has good
+ performance in most situations compared to the previous two methods.
+ */
+ var tempRange = workingRange.duplicate();
+ var rangeLength = tempRange.text.replace(/\r\n/g, "\r").length;
+
+ offset = tempRange.moveStart("character", rangeLength);
+ while ( (comparison = tempRange.compareEndPoints("StartToEnd", tempRange)) == -1) {
+ offset++;
+ tempRange.moveStart("character", 1);
+ }
+ } else {
+ offset = workingRange.text.length;
+ }
+ boundaryPosition = new DomPosition(boundaryNode, offset);
+ } else {
+
+
+ // If the boundary immediately follows a character data node and this is the end boundary, we should favour
+ // a position within that, and likewise for a start boundary preceding a character data node
+ previousNode = (isCollapsed || !isStart) && workingNode.previousSibling;
+ nextNode = (isCollapsed || isStart) && workingNode.nextSibling;
+
+
+
+ if (nextNode && dom.isCharacterDataNode(nextNode)) {
+ boundaryPosition = new DomPosition(nextNode, 0);
+ } else if (previousNode && dom.isCharacterDataNode(previousNode)) {
+ boundaryPosition = new DomPosition(previousNode, previousNode.length);
+ } else {
+ boundaryPosition = new DomPosition(containerElement, dom.getNodeIndex(workingNode));
+ }
+ }
+
+ // Clean up
+ workingNode.parentNode.removeChild(workingNode);
+
+ return boundaryPosition;
+ }
+
+ // Returns a TextRange representing the boundary of a TextRange expressed as a node and an offset within that node.
+ // This function started out as an optimized version of code found in Tim Cameron Ryan's IERange
+ // (http://code.google.com/p/ierange/)
+ function createBoundaryTextRange(boundaryPosition, isStart) {
+ var boundaryNode, boundaryParent, boundaryOffset = boundaryPosition.offset;
+ var doc = dom.getDocument(boundaryPosition.node);
+ var workingNode, childNodes, workingRange = doc.body.createTextRange();
+ var nodeIsDataNode = dom.isCharacterDataNode(boundaryPosition.node);
+
+ if (nodeIsDataNode) {
+ boundaryNode = boundaryPosition.node;
+ boundaryParent = boundaryNode.parentNode;
+ } else {
+ childNodes = boundaryPosition.node.childNodes;
+ boundaryNode = (boundaryOffset < childNodes.length) ? childNodes[boundaryOffset] : null;
+ boundaryParent = boundaryPosition.node;
+ }
+
+ // Position the range immediately before the node containing the boundary
+ workingNode = doc.createElement("span");
+
+ // Making the working element non-empty element persuades IE to consider the TextRange boundary to be within the
+ // element rather than immediately before or after it, which is what we want
+ workingNode.innerHTML = "feff;";
+
+ // insertBefore is supposed to work like appendChild if the second parameter is null. However, a bug report
+ // for IERange suggests that it can crash the browser: http://code.google.com/p/ierange/issues/detail?id=12
+ if (boundaryNode) {
+ boundaryParent.insertBefore(workingNode, boundaryNode);
+ } else {
+ boundaryParent.appendChild(workingNode);
+ }
+
+ workingRange.moveToElementText(workingNode);
+ workingRange.collapse(!isStart);
+
+ // Clean up
+ boundaryParent.removeChild(workingNode);
+
+ // Move the working range to the text offset, if required
+ if (nodeIsDataNode) {
+ workingRange[isStart ? "moveStart" : "moveEnd"]("character", boundaryOffset);
+ }
+
+ return workingRange;
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ if (api.features.implementsDomRange && (!api.features.implementsTextRange || !api.config.preferTextRange)) {
+ // This is a wrapper around the browser's native DOM Range. It has two aims:
+ // - Provide workarounds for specific browser bugs
+ // - provide convenient extensions, which are inherited from Rangy's DomRange
+
+ (function() {
+ var rangeProto;
+ var rangeProperties = DomRange.rangeProperties;
+ var canSetRangeStartAfterEnd;
+
+ function updateRangeProperties(range) {
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = range.nativeRange[prop];
+ }
+ }
+
+ function updateNativeRange(range, startContainer, startOffset, endContainer,endOffset) {
+ var startMoved = (range.startContainer !== startContainer || range.startOffset != startOffset);
+ var endMoved = (range.endContainer !== endContainer || range.endOffset != endOffset);
+
+ // Always set both boundaries for the benefit of IE9 (see issue 35)
+ if (startMoved || endMoved) {
+ range.setEnd(endContainer, endOffset);
+ range.setStart(startContainer, startOffset);
+ }
+ }
+
+ function detach(range) {
+ range.nativeRange.detach();
+ range.detached = true;
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = null;
+ }
+ }
+
+ var createBeforeAfterNodeSetter;
+
+ WrappedRange = function(range) {
+ if (!range) {
+ throw new Error("Range must be specified");
+ }
+ this.nativeRange = range;
+ updateRangeProperties(this);
+ };
+
+ DomRange.createPrototypeRange(WrappedRange, updateNativeRange, detach);
+
+ rangeProto = WrappedRange.prototype;
+
+ rangeProto.selectNode = function(node) {
+ this.nativeRange.selectNode(node);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.deleteContents = function() {
+ this.nativeRange.deleteContents();
+ updateRangeProperties(this);
+ };
+
+ rangeProto.extractContents = function() {
+ var frag = this.nativeRange.extractContents();
+ updateRangeProperties(this);
+ return frag;
+ };
+
+ rangeProto.cloneContents = function() {
+ return this.nativeRange.cloneContents();
+ };
+
+ // TODO: Until I can find a way to programmatically trigger the Firefox bug (apparently long-standing, still
+ // present in 3.6.8) that throws "Index or size is negative or greater than the allowed amount" for
+ // insertNode in some circumstances, all browsers will have to use the Rangy's own implementation of
+ // insertNode, which works but is almost certainly slower than the native implementation.
+/*
+ rangeProto.insertNode = function(node) {
+ this.nativeRange.insertNode(node);
+ updateRangeProperties(this);
+ };
+*/
+
+ rangeProto.surroundContents = function(node) {
+ this.nativeRange.surroundContents(node);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.collapse = function(isStart) {
+ this.nativeRange.collapse(isStart);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.cloneRange = function() {
+ return new WrappedRange(this.nativeRange.cloneRange());
+ };
+
+ rangeProto.refresh = function() {
+ updateRangeProperties(this);
+ };
+
+ rangeProto.toString = function() {
+ return this.nativeRange.toString();
+ };
+
+ // Create test range and node for feature detection
+
+ var testTextNode = document.createTextNode("test");
+ dom.getBody(document).appendChild(testTextNode);
+ var range = document.createRange();
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for Firefox 2 bug that prevents moving the start of a Range to a point after its current end and
+ // correct for it
+
+ range.setStart(testTextNode, 0);
+ range.setEnd(testTextNode, 0);
+
+ try {
+ range.setStart(testTextNode, 1);
+ canSetRangeStartAfterEnd = true;
+
+ rangeProto.setStart = function(node, offset) {
+ this.nativeRange.setStart(node, offset);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.setEnd = function(node, offset) {
+ this.nativeRange.setEnd(node, offset);
+ updateRangeProperties(this);
+ };
+
+ createBeforeAfterNodeSetter = function(name) {
+ return function(node) {
+ this.nativeRange[name](node);
+ updateRangeProperties(this);
+ };
+ };
+
+ } catch(ex) {
+
+
+ canSetRangeStartAfterEnd = false;
+
+ rangeProto.setStart = function(node, offset) {
+ try {
+ this.nativeRange.setStart(node, offset);
+ } catch (ex) {
+ this.nativeRange.setEnd(node, offset);
+ this.nativeRange.setStart(node, offset);
+ }
+ updateRangeProperties(this);
+ };
+
+ rangeProto.setEnd = function(node, offset) {
+ try {
+ this.nativeRange.setEnd(node, offset);
+ } catch (ex) {
+ this.nativeRange.setStart(node, offset);
+ this.nativeRange.setEnd(node, offset);
+ }
+ updateRangeProperties(this);
+ };
+
+ createBeforeAfterNodeSetter = function(name, oppositeName) {
+ return function(node) {
+ try {
+ this.nativeRange[name](node);
+ } catch (ex) {
+ this.nativeRange[oppositeName](node);
+ this.nativeRange[name](node);
+ }
+ updateRangeProperties(this);
+ };
+ };
+ }
+
+ rangeProto.setStartBefore = createBeforeAfterNodeSetter("setStartBefore", "setEndBefore");
+ rangeProto.setStartAfter = createBeforeAfterNodeSetter("setStartAfter", "setEndAfter");
+ rangeProto.setEndBefore = createBeforeAfterNodeSetter("setEndBefore", "setStartBefore");
+ rangeProto.setEndAfter = createBeforeAfterNodeSetter("setEndAfter", "setStartAfter");
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for and correct Firefox 2 behaviour with selectNodeContents on text nodes: it collapses the range to
+ // the 0th character of the text node
+ range.selectNodeContents(testTextNode);
+ if (range.startContainer == testTextNode && range.endContainer == testTextNode &&
+ range.startOffset == 0 && range.endOffset == testTextNode.length) {
+ rangeProto.selectNodeContents = function(node) {
+ this.nativeRange.selectNodeContents(node);
+ updateRangeProperties(this);
+ };
+ } else {
+ rangeProto.selectNodeContents = function(node) {
+ this.setStart(node, 0);
+ this.setEnd(node, DomRange.getEndOffset(node));
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for WebKit bug that has the beahviour of compareBoundaryPoints round the wrong way for constants
+ // START_TO_END and END_TO_START: https://bugs.webkit.org/show_bug.cgi?id=20738
+
+ range.selectNodeContents(testTextNode);
+ range.setEnd(testTextNode, 3);
+
+ var range2 = document.createRange();
+ range2.selectNodeContents(testTextNode);
+ range2.setEnd(testTextNode, 4);
+ range2.setStart(testTextNode, 2);
+
+ if (range.compareBoundaryPoints(range.START_TO_END, range2) == -1 &
+ range.compareBoundaryPoints(range.END_TO_START, range2) == 1) {
+ // This is the wrong way round, so correct for it
+
+
+ rangeProto.compareBoundaryPoints = function(type, range) {
+ range = range.nativeRange || range;
+ if (type == range.START_TO_END) {
+ type = range.END_TO_START;
+ } else if (type == range.END_TO_START) {
+ type = range.START_TO_END;
+ }
+ return this.nativeRange.compareBoundaryPoints(type, range);
+ };
+ } else {
+ rangeProto.compareBoundaryPoints = function(type, range) {
+ return this.nativeRange.compareBoundaryPoints(type, range.nativeRange || range);
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for existence of createContextualFragment and delegate to it if it exists
+ if (api.util.isHostMethod(range, "createContextualFragment")) {
+ rangeProto.createContextualFragment = function(fragmentStr) {
+ return this.nativeRange.createContextualFragment(fragmentStr);
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Clean up
+ dom.getBody(document).removeChild(testTextNode);
+ range.detach();
+ range2.detach();
+ })();
+
+ api.createNativeRange = function(doc) {
+ doc = doc || document;
+ return doc.createRange();
+ };
+ } else if (api.features.implementsTextRange) {
+ // This is a wrapper around a TextRange, providing full DOM Range functionality using rangy's DomRange as a
+ // prototype
+
+ WrappedRange = function(textRange) {
+ this.textRange = textRange;
+ this.refresh();
+ };
+
+ WrappedRange.prototype = new DomRange(document);
+
+ WrappedRange.prototype.refresh = function() {
+ var start, end;
+
+ // TextRange's parentElement() method cannot be trusted. getTextRangeContainerElement() works around that.
+ var rangeContainerElement = getTextRangeContainerElement(this.textRange);
+
+ if (textRangeIsCollapsed(this.textRange)) {
+ end = start = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true, true);
+ } else {
+
+ start = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true, false);
+ end = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, false, false);
+ }
+
+ this.setStart(start.node, start.offset);
+ this.setEnd(end.node, end.offset);
+ };
+
+ DomRange.copyComparisonConstants(WrappedRange);
+
+ // Add WrappedRange as the Range property of the global object to allow expression like Range.END_TO_END to work
+ var globalObj = (function() { return this; })();
+ if (typeof globalObj.Range == "undefined") {
+ globalObj.Range = WrappedRange;
+ }
+
+ api.createNativeRange = function(doc) {
+ doc = doc || document;
+ return doc.body.createTextRange();
+ };
+ }
+
+ if (api.features.implementsTextRange) {
+ WrappedRange.rangeToTextRange = function(range) {
+ if (range.collapsed) {
+ var tr = createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+
+
+
+ return tr;
+
+ //return createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+ } else {
+ var startRange = createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+ var endRange = createBoundaryTextRange(new DomPosition(range.endContainer, range.endOffset), false);
+ var textRange = dom.getDocument(range.startContainer).body.createTextRange();
+ textRange.setEndPoint("StartToStart", startRange);
+ textRange.setEndPoint("EndToEnd", endRange);
+ return textRange;
+ }
+ };
+ }
+
+ WrappedRange.prototype.getName = function() {
+ return "WrappedRange";
+ };
+
+ api.WrappedRange = WrappedRange;
+
+ api.createRange = function(doc) {
+ doc = doc || document;
+ return new WrappedRange(api.createNativeRange(doc));
+ };
+
+ api.createRangyRange = function(doc) {
+ doc = doc || document;
+ return new DomRange(doc);
+ };
+
+ api.createIframeRange = function(iframeEl) {
+ return api.createRange(dom.getIframeDocument(iframeEl));
+ };
+
+ api.createIframeRangyRange = function(iframeEl) {
+ return api.createRangyRange(dom.getIframeDocument(iframeEl));
+ };
+
+ api.addCreateMissingNativeApiListener(function(win) {
+ var doc = win.document;
+ if (typeof doc.createRange == "undefined") {
+ doc.createRange = function() {
+ return api.createRange(this);
+ };
+ }
+ doc = win = null;
+ });
+});rangy.createModule("WrappedSelection", function(api, module) {
+ // This will create a selection object wrapper that follows the Selection object found in the WHATWG draft DOM Range
+ // spec (http://html5.org/specs/dom-range.html)
+
+ api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
+
+ api.config.checkSelectionRanges = true;
+
+ var BOOLEAN = "boolean",
+ windowPropertyName = "_rangySelection",
+ dom = api.dom,
+ util = api.util,
+ DomRange = api.DomRange,
+ WrappedRange = api.WrappedRange,
+ DOMException = api.DOMException,
+ DomPosition = dom.DomPosition,
+ getSelection,
+ selectionIsCollapsed,
+ CONTROL = "Control";
+
+
+
+ function getWinSelection(winParam) {
+ return (winParam || window).getSelection();
+ }
+
+ function getDocSelection(winParam) {
+ return (winParam || window).document.selection;
+ }
+
+ // Test for the Range/TextRange and Selection features required
+ // Test for ability to retrieve selection
+ var implementsWinGetSelection = api.util.isHostMethod(window, "getSelection"),
+ implementsDocSelection = api.util.isHostObject(document, "selection");
+
+ var useDocumentSelection = implementsDocSelection && (!implementsWinGetSelection || api.config.preferTextRange);
+
+ if (useDocumentSelection) {
+ getSelection = getDocSelection;
+ api.isSelectionValid = function(winParam) {
+ var doc = (winParam || window).document, nativeSel = doc.selection;
+
+ // Check whether the selection TextRange is actually contained within the correct document
+ return (nativeSel.type != "None" || dom.getDocument(nativeSel.createRange().parentElement()) == doc);
+ };
+ } else if (implementsWinGetSelection) {
+ getSelection = getWinSelection;
+ api.isSelectionValid = function() {
+ return true;
+ };
+ } else {
+ module.fail("Neither document.selection or window.getSelection() detected.");
+ }
+
+ api.getNativeSelection = getSelection;
+
+ var testSelection = getSelection();
+ var testRange = api.createNativeRange(document);
+ var body = dom.getBody(document);
+
+ // Obtaining a range from a selection
+ var selectionHasAnchorAndFocus = util.areHostObjects(testSelection, ["anchorNode", "focusNode"] &&
+ util.areHostProperties(testSelection, ["anchorOffset", "focusOffset"]));
+ api.features.selectionHasAnchorAndFocus = selectionHasAnchorAndFocus;
+
+ // Test for existence of native selection extend() method
+ var selectionHasExtend = util.isHostMethod(testSelection, "extend");
+ api.features.selectionHasExtend = selectionHasExtend;
+
+ // Test if rangeCount exists
+ var selectionHasRangeCount = (typeof testSelection.rangeCount == "number");
+ api.features.selectionHasRangeCount = selectionHasRangeCount;
+
+ var selectionSupportsMultipleRanges = false;
+ var collapsedNonEditableSelectionsSupported = true;
+
+ if (util.areHostMethods(testSelection, ["addRange", "getRangeAt", "removeAllRanges"]) &&
+ typeof testSelection.rangeCount == "number" && api.features.implementsDomRange) {
+
+ (function() {
+ var iframe = document.createElement("iframe");
+ iframe.frameBorder = 0;
+ iframe.style.position = "absolute";
+ iframe.style.left = "-10000px";
+ body.appendChild(iframe);
+
+ var iframeDoc = dom.getIframeDocument(iframe);
+ iframeDoc.open();
+ iframeDoc.write("12");
+ iframeDoc.close();
+
+ var sel = dom.getIframeWindow(iframe).getSelection();
+ var docEl = iframeDoc.documentElement;
+ var iframeBody = docEl.lastChild, textNode = iframeBody.firstChild;
+
+ // Test whether the native selection will allow a collapsed selection within a non-editable element
+ var r1 = iframeDoc.createRange();
+ r1.setStart(textNode, 1);
+ r1.collapse(true);
+ sel.addRange(r1);
+ collapsedNonEditableSelectionsSupported = (sel.rangeCount == 1);
+ sel.removeAllRanges();
+
+ // Test whether the native selection is capable of supporting multiple ranges
+ var r2 = r1.cloneRange();
+ r1.setStart(textNode, 0);
+ r2.setEnd(textNode, 2);
+ sel.addRange(r1);
+ sel.addRange(r2);
+
+ selectionSupportsMultipleRanges = (sel.rangeCount == 2);
+
+ // Clean up
+ r1.detach();
+ r2.detach();
+
+ body.removeChild(iframe);
+ })();
+ }
+
+ api.features.selectionSupportsMultipleRanges = selectionSupportsMultipleRanges;
+ api.features.collapsedNonEditableSelectionsSupported = collapsedNonEditableSelectionsSupported;
+
+ // ControlRanges
+ var implementsControlRange = false, testControlRange;
+
+ if (body && util.isHostMethod(body, "createControlRange")) {
+ testControlRange = body.createControlRange();
+ if (util.areHostProperties(testControlRange, ["item", "add"])) {
+ implementsControlRange = true;
+ }
+ }
+ api.features.implementsControlRange = implementsControlRange;
+
+ // Selection collapsedness
+ if (selectionHasAnchorAndFocus) {
+ selectionIsCollapsed = function(sel) {
+ return sel.anchorNode === sel.focusNode && sel.anchorOffset === sel.focusOffset;
+ };
+ } else {
+ selectionIsCollapsed = function(sel) {
+ return sel.rangeCount ? sel.getRangeAt(sel.rangeCount - 1).collapsed : false;
+ };
+ }
+
+ function updateAnchorAndFocusFromRange(sel, range, backwards) {
+ var anchorPrefix = backwards ? "end" : "start", focusPrefix = backwards ? "start" : "end";
+ sel.anchorNode = range[anchorPrefix + "Container"];
+ sel.anchorOffset = range[anchorPrefix + "Offset"];
+ sel.focusNode = range[focusPrefix + "Container"];
+ sel.focusOffset = range[focusPrefix + "Offset"];
+ }
+
+ function updateAnchorAndFocusFromNativeSelection(sel) {
+ var nativeSel = sel.nativeSelection;
+ sel.anchorNode = nativeSel.anchorNode;
+ sel.anchorOffset = nativeSel.anchorOffset;
+ sel.focusNode = nativeSel.focusNode;
+ sel.focusOffset = nativeSel.focusOffset;
+ }
+
+ function updateEmptySelection(sel) {
+ sel.anchorNode = sel.focusNode = null;
+ sel.anchorOffset = sel.focusOffset = 0;
+ sel.rangeCount = 0;
+ sel.isCollapsed = true;
+ sel._ranges.length = 0;
+ }
+
+ function getNativeRange(range) {
+ var nativeRange;
+ if (range instanceof DomRange) {
+ nativeRange = range._selectionNativeRange;
+ if (!nativeRange) {
+ nativeRange = api.createNativeRange(dom.getDocument(range.startContainer));
+ nativeRange.setEnd(range.endContainer, range.endOffset);
+ nativeRange.setStart(range.startContainer, range.startOffset);
+ range._selectionNativeRange = nativeRange;
+ range.attachListener("detach", function() {
+
+ this._selectionNativeRange = null;
+ });
+ }
+ } else if (range instanceof WrappedRange) {
+ nativeRange = range.nativeRange;
+ } else if (api.features.implementsDomRange && (range instanceof dom.getWindow(range.startContainer).Range)) {
+ nativeRange = range;
+ }
+ return nativeRange;
+ }
+
+ function rangeContainsSingleElement(rangeNodes) {
+ if (!rangeNodes.length || rangeNodes[0].nodeType != 1) {
+ return false;
+ }
+ for (var i = 1, len = rangeNodes.length; i < len; ++i) {
+ if (!dom.isAncestorOf(rangeNodes[0], rangeNodes[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function getSingleElementFromRange(range) {
+ var nodes = range.getNodes();
+ if (!rangeContainsSingleElement(nodes)) {
+ throw new Error("getSingleElementFromRange: range " + range.inspect() + " did not consist of a single element");
+ }
+ return nodes[0];
+ }
+
+ function isTextRange(range) {
+ return !!range && typeof range.text != "undefined";
+ }
+
+ function updateFromTextRange(sel, range) {
+ // Create a Range from the selected TextRange
+ var wrappedRange = new WrappedRange(range);
+ sel._ranges = [wrappedRange];
+
+ updateAnchorAndFocusFromRange(sel, wrappedRange, false);
+ sel.rangeCount = 1;
+ sel.isCollapsed = wrappedRange.collapsed;
+ }
+
+ function updateControlSelection(sel) {
+ // Update the wrapped selection based on what's now in the native selection
+ sel._ranges.length = 0;
+ if (sel.docSelection.type == "None") {
+ updateEmptySelection(sel);
+ } else {
+ var controlRange = sel.docSelection.createRange();
+ if (isTextRange(controlRange)) {
+ // This case (where the selection type is "Control" and calling createRange() on the selection returns
+ // a TextRange) can happen in IE 9. It happens, for example, when all elements in the selected
+ // ControlRange have been removed from the ControlRange and removed from the document.
+ updateFromTextRange(sel, controlRange);
+ } else {
+ sel.rangeCount = controlRange.length;
+ var range, doc = dom.getDocument(controlRange.item(0));
+ for (var i = 0; i < sel.rangeCount; ++i) {
+ range = api.createRange(doc);
+ range.selectNode(controlRange.item(i));
+ sel._ranges.push(range);
+ }
+ sel.isCollapsed = sel.rangeCount == 1 && sel._ranges[0].collapsed;
+ updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], false);
+ }
+ }
+ }
+
+ function addRangeToControlSelection(sel, range) {
+ var controlRange = sel.docSelection.createRange();
+ var rangeElement = getSingleElementFromRange(range);
+
+ // Create a new ControlRange containing all the elements in the selected ControlRange plus the element
+ // contained by the supplied range
+ var doc = dom.getDocument(controlRange.item(0));
+ var newControlRange = dom.getBody(doc).createControlRange();
+ for (var i = 0, len = controlRange.length; i < len; ++i) {
+ newControlRange.add(controlRange.item(i));
+ }
+ try {
+ newControlRange.add(rangeElement);
+ } catch (ex) {
+ throw new Error("addRange(): Element within the specified Range could not be added to control selection (does it have layout?)");
+ }
+ newControlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(sel);
+ }
+
+ var getSelectionRangeAt;
+
+ if (util.isHostMethod(testSelection, "getRangeAt")) {
+ getSelectionRangeAt = function(sel, index) {
+ try {
+ return sel.getRangeAt(index);
+ } catch(ex) {
+ return null;
+ }
+ };
+ } else if (selectionHasAnchorAndFocus) {
+ getSelectionRangeAt = function(sel) {
+ var doc = dom.getDocument(sel.anchorNode);
+ var range = api.createRange(doc);
+ range.setStart(sel.anchorNode, sel.anchorOffset);
+ range.setEnd(sel.focusNode, sel.focusOffset);
+
+ // Handle the case when the selection was selected backwards (from the end to the start in the
+ // document)
+ if (range.collapsed !== this.isCollapsed) {
+ range.setStart(sel.focusNode, sel.focusOffset);
+ range.setEnd(sel.anchorNode, sel.anchorOffset);
+ }
+
+ return range;
+ };
+ }
+
+ /**
+ * @constructor
+ */
+ function WrappedSelection(selection, docSelection, win) {
+ this.nativeSelection = selection;
+ this.docSelection = docSelection;
+ this._ranges = [];
+ this.win = win;
+ this.refresh();
+ }
+
+ api.getSelection = function(win) {
+ win = win || window;
+ var sel = win[windowPropertyName];
+ var nativeSel = getSelection(win), docSel = implementsDocSelection ? getDocSelection(win) : null;
+ if (sel) {
+ sel.nativeSelection = nativeSel;
+ sel.docSelection = docSel;
+ sel.refresh(win);
+ } else {
+ sel = new WrappedSelection(nativeSel, docSel, win);
+ win[windowPropertyName] = sel;
+ }
+ return sel;
+ };
+
+ api.getIframeSelection = function(iframeEl) {
+ return api.getSelection(dom.getIframeWindow(iframeEl));
+ };
+
+ var selProto = WrappedSelection.prototype;
+
+ function createControlSelection(sel, ranges) {
+ // Ensure that the selection becomes of type "Control"
+ var doc = dom.getDocument(ranges[0].startContainer);
+ var controlRange = dom.getBody(doc).createControlRange();
+ for (var i = 0, el; i < rangeCount; ++i) {
+ el = getSingleElementFromRange(ranges[i]);
+ try {
+ controlRange.add(el);
+ } catch (ex) {
+ throw new Error("setRanges(): Element within the one of the specified Ranges could not be added to control selection (does it have layout?)");
+ }
+ }
+ controlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(sel);
+ }
+
+ // Selecting a range
+ if (!useDocumentSelection && selectionHasAnchorAndFocus && util.areHostMethods(testSelection, ["removeAllRanges", "addRange"])) {
+ selProto.removeAllRanges = function() {
+ this.nativeSelection.removeAllRanges();
+ updateEmptySelection(this);
+ };
+
+ var addRangeBackwards = function(sel, range) {
+ var doc = DomRange.getRangeDocument(range);
+ var endRange = api.createRange(doc);
+ endRange.collapseToPoint(range.endContainer, range.endOffset);
+ sel.nativeSelection.addRange(getNativeRange(endRange));
+ sel.nativeSelection.extend(range.startContainer, range.startOffset);
+ sel.refresh();
+ };
+
+ if (selectionHasRangeCount) {
+ selProto.addRange = function(range, backwards) {
+ if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
+ addRangeToControlSelection(this, range);
+ } else {
+ if (backwards && selectionHasExtend) {
+ addRangeBackwards(this, range);
+ } else {
+ var previousRangeCount;
+ if (selectionSupportsMultipleRanges) {
+ previousRangeCount = this.rangeCount;
+ } else {
+ this.removeAllRanges();
+ previousRangeCount = 0;
+ }
+ this.nativeSelection.addRange(getNativeRange(range));
+
+ // Check whether adding the range was successful
+ this.rangeCount = this.nativeSelection.rangeCount;
+
+ if (this.rangeCount == previousRangeCount + 1) {
+ // The range was added successfully
+
+ // Check whether the range that we added to the selection is reflected in the last range extracted from
+ // the selection
+ if (api.config.checkSelectionRanges) {
+ var nativeRange = getSelectionRangeAt(this.nativeSelection, this.rangeCount - 1);
+ if (nativeRange && !DomRange.rangesEqual(nativeRange, range)) {
+ // Happens in WebKit with, for example, a selection placed at the start of a text node
+ range = new WrappedRange(nativeRange);
+ }
+ }
+ this._ranges[this.rangeCount - 1] = range;
+ updateAnchorAndFocusFromRange(this, range, selectionIsBackwards(this.nativeSelection));
+ this.isCollapsed = selectionIsCollapsed(this);
+ } else {
+ // The range was not added successfully. The simplest thing is to refresh
+ this.refresh();
+ }
+ }
+ }
+ };
+ } else {
+ selProto.addRange = function(range, backwards) {
+ if (backwards && selectionHasExtend) {
+ addRangeBackwards(this, range);
+ } else {
+ this.nativeSelection.addRange(getNativeRange(range));
+ this.refresh();
+ }
+ };
+ }
+
+ selProto.setRanges = function(ranges) {
+ if (implementsControlRange && ranges.length > 1) {
+ createControlSelection(this, ranges);
+ } else {
+ this.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ this.addRange(ranges[i]);
+ }
+ }
+ };
+ } else if (util.isHostMethod(testSelection, "empty") && util.isHostMethod(testRange, "select") &&
+ implementsControlRange && useDocumentSelection) {
+
+ selProto.removeAllRanges = function() {
+ // Added try/catch as fix for issue #21
+ try {
+ this.docSelection.empty();
+
+ // Check for empty() not working (issue #24)
+ if (this.docSelection.type != "None") {
+ // Work around failure to empty a control selection by instead selecting a TextRange and then
+ // calling empty()
+ var doc;
+ if (this.anchorNode) {
+ doc = dom.getDocument(this.anchorNode);
+ } else if (this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ if (controlRange.length) {
+ doc = dom.getDocument(controlRange.item(0)).body.createTextRange();
+ }
+ }
+ if (doc) {
+ var textRange = doc.body.createTextRange();
+ textRange.select();
+ this.docSelection.empty();
+ }
+ }
+ } catch(ex) {}
+ updateEmptySelection(this);
+ };
+
+ selProto.addRange = function(range) {
+ if (this.docSelection.type == CONTROL) {
+ addRangeToControlSelection(this, range);
+ } else {
+ WrappedRange.rangeToTextRange(range).select();
+ this._ranges[0] = range;
+ this.rangeCount = 1;
+ this.isCollapsed = this._ranges[0].collapsed;
+ updateAnchorAndFocusFromRange(this, range, false);
+ }
+ };
+
+ selProto.setRanges = function(ranges) {
+ this.removeAllRanges();
+ var rangeCount = ranges.length;
+ if (rangeCount > 1) {
+ createControlSelection(this, ranges);
+ } else if (rangeCount) {
+ this.addRange(ranges[0]);
+ }
+ };
+ } else {
+ module.fail("No means of selecting a Range or TextRange was found");
+ return false;
+ }
+
+ selProto.getRangeAt = function(index) {
+ if (index < 0 || index >= this.rangeCount) {
+ throw new DOMException("INDEX_SIZE_ERR");
+ } else {
+ return this._ranges[index];
+ }
+ };
+
+ var refreshSelection;
+
+ if (useDocumentSelection) {
+ refreshSelection = function(sel) {
+ var range;
+ if (api.isSelectionValid(sel.win)) {
+ range = sel.docSelection.createRange();
+ } else {
+ range = dom.getBody(sel.win.document).createTextRange();
+ range.collapse(true);
+ }
+
+
+ if (sel.docSelection.type == CONTROL) {
+ updateControlSelection(sel);
+ } else if (isTextRange(range)) {
+ updateFromTextRange(sel, range);
+ } else {
+ updateEmptySelection(sel);
+ }
+ };
+ } else if (util.isHostMethod(testSelection, "getRangeAt") && typeof testSelection.rangeCount == "number") {
+ refreshSelection = function(sel) {
+ if (implementsControlRange && implementsDocSelection && sel.docSelection.type == CONTROL) {
+ updateControlSelection(sel);
+ } else {
+ sel._ranges.length = sel.rangeCount = sel.nativeSelection.rangeCount;
+ if (sel.rangeCount) {
+ for (var i = 0, len = sel.rangeCount; i < len; ++i) {
+ sel._ranges[i] = new api.WrappedRange(sel.nativeSelection.getRangeAt(i));
+ }
+ updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], selectionIsBackwards(sel.nativeSelection));
+ sel.isCollapsed = selectionIsCollapsed(sel);
+ } else {
+ updateEmptySelection(sel);
+ }
+ }
+ };
+ } else if (selectionHasAnchorAndFocus && typeof testSelection.isCollapsed == BOOLEAN && typeof testRange.collapsed == BOOLEAN && api.features.implementsDomRange) {
+ refreshSelection = function(sel) {
+ var range, nativeSel = sel.nativeSelection;
+ if (nativeSel.anchorNode) {
+ range = getSelectionRangeAt(nativeSel, 0);
+ sel._ranges = [range];
+ sel.rangeCount = 1;
+ updateAnchorAndFocusFromNativeSelection(sel);
+ sel.isCollapsed = selectionIsCollapsed(sel);
+ } else {
+ updateEmptySelection(sel);
+ }
+ };
+ } else {
+ module.fail("No means of obtaining a Range or TextRange from the user's selection was found");
+ return false;
+ }
+
+ selProto.refresh = function(checkForChanges) {
+ var oldRanges = checkForChanges ? this._ranges.slice(0) : null;
+ refreshSelection(this);
+ if (checkForChanges) {
+ var i = oldRanges.length;
+ if (i != this._ranges.length) {
+ return false;
+ }
+ while (i--) {
+ if (!DomRange.rangesEqual(oldRanges[i], this._ranges[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ // Removal of a single range
+ var removeRangeManually = function(sel, range) {
+ var ranges = sel.getAllRanges(), removed = false;
+ sel.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ if (removed || range !== ranges[i]) {
+ sel.addRange(ranges[i]);
+ } else {
+ // According to the draft WHATWG Range spec, the same range may be added to the selection multiple
+ // times. removeRange should only remove the first instance, so the following ensures only the first
+ // instance is removed
+ removed = true;
+ }
+ }
+ if (!sel.rangeCount) {
+ updateEmptySelection(sel);
+ }
+ };
+
+ if (implementsControlRange) {
+ selProto.removeRange = function(range) {
+ if (this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ var rangeElement = getSingleElementFromRange(range);
+
+ // Create a new ControlRange containing all the elements in the selected ControlRange minus the
+ // element contained by the supplied range
+ var doc = dom.getDocument(controlRange.item(0));
+ var newControlRange = dom.getBody(doc).createControlRange();
+ var el, removed = false;
+ for (var i = 0, len = controlRange.length; i < len; ++i) {
+ el = controlRange.item(i);
+ if (el !== rangeElement || removed) {
+ newControlRange.add(controlRange.item(i));
+ } else {
+ removed = true;
+ }
+ }
+ newControlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(this);
+ } else {
+ removeRangeManually(this, range);
+ }
+ };
+ } else {
+ selProto.removeRange = function(range) {
+ removeRangeManually(this, range);
+ };
+ }
+
+ // Detecting if a selection is backwards
+ var selectionIsBackwards;
+ if (!useDocumentSelection && selectionHasAnchorAndFocus && api.features.implementsDomRange) {
+ selectionIsBackwards = function(sel) {
+ var backwards = false;
+ if (sel.anchorNode) {
+ backwards = (dom.comparePoints(sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset) == 1);
+ }
+ return backwards;
+ };
+
+ selProto.isBackwards = function() {
+ return selectionIsBackwards(this);
+ };
+ } else {
+ selectionIsBackwards = selProto.isBackwards = function() {
+ return false;
+ };
+ }
+
+ // Selection text
+ // This is conformant to the new WHATWG DOM Range draft spec but differs from WebKit and Mozilla's implementation
+ selProto.toString = function() {
+
+ var rangeTexts = [];
+ for (var i = 0, len = this.rangeCount; i < len; ++i) {
+ rangeTexts[i] = "" + this._ranges[i];
+ }
+ return rangeTexts.join("");
+ };
+
+ function assertNodeInSameDocument(sel, node) {
+ if (sel.anchorNode && (dom.getDocument(sel.anchorNode) !== dom.getDocument(node))) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+ }
+
+ // No current browsers conform fully to the HTML 5 draft spec for this method, so Rangy's own method is always used
+ selProto.collapse = function(node, offset) {
+ assertNodeInSameDocument(this, node);
+ var range = api.createRange(dom.getDocument(node));
+ range.collapseToPoint(node, offset);
+ this.removeAllRanges();
+ this.addRange(range);
+ this.isCollapsed = true;
+ };
+
+ selProto.collapseToStart = function() {
+ if (this.rangeCount) {
+ var range = this._ranges[0];
+ this.collapse(range.startContainer, range.startOffset);
+ } else {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ };
+
+ selProto.collapseToEnd = function() {
+ if (this.rangeCount) {
+ var range = this._ranges[this.rangeCount - 1];
+ this.collapse(range.endContainer, range.endOffset);
+ } else {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ };
+
+ // The HTML 5 spec is very specific on how selectAllChildren should be implemented so the native implementation is
+ // never used by Rangy.
+ selProto.selectAllChildren = function(node) {
+ assertNodeInSameDocument(this, node);
+ var range = api.createRange(dom.getDocument(node));
+ range.selectNodeContents(node);
+ this.removeAllRanges();
+ this.addRange(range);
+ };
+
+ selProto.deleteFromDocument = function() {
+ // Sepcial behaviour required for Control selections
+ if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ var element;
+ while (controlRange.length) {
+ element = controlRange.item(0);
+ controlRange.remove(element);
+ element.parentNode.removeChild(element);
+ }
+ this.refresh();
+ } else if (this.rangeCount) {
+ var ranges = this.getAllRanges();
+ this.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ ranges[i].deleteContents();
+ }
+ // The HTML5 spec says nothing about what the selection should contain after calling deleteContents on each
+ // range. Firefox moves the selection to where the final selected range was, so we emulate that
+ this.addRange(ranges[len - 1]);
+ }
+ };
+
+ // The following are non-standard extensions
+ selProto.getAllRanges = function() {
+ return this._ranges.slice(0);
+ };
+
+ selProto.setSingleRange = function(range) {
+ this.setRanges( [range] );
+ };
+
+ selProto.containsNode = function(node, allowPartial) {
+ for (var i = 0, len = this._ranges.length; i < len; ++i) {
+ if (this._ranges[i].containsNode(node, allowPartial)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ selProto.toHtml = function() {
+ var html = "";
+ if (this.rangeCount) {
+ var container = DomRange.getRangeDocument(this._ranges[0]).createElement("div");
+ for (var i = 0, len = this._ranges.length; i < len; ++i) {
+ container.appendChild(this._ranges[i].cloneContents());
+ }
+ html = container.innerHTML;
+ }
+ return html;
+ };
+
+ function inspect(sel) {
+ var rangeInspects = [];
+ var anchor = new DomPosition(sel.anchorNode, sel.anchorOffset);
+ var focus = new DomPosition(sel.focusNode, sel.focusOffset);
+ var name = (typeof sel.getName == "function") ? sel.getName() : "Selection";
+
+ if (typeof sel.rangeCount != "undefined") {
+ for (var i = 0, len = sel.rangeCount; i < len; ++i) {
+ rangeInspects[i] = DomRange.inspect(sel.getRangeAt(i));
+ }
+ }
+ return "[" + name + "(Ranges: " + rangeInspects.join(", ") +
+ ")(anchor: " + anchor.inspect() + ", focus: " + focus.inspect() + "]";
+
+ }
+
+ selProto.getName = function() {
+ return "WrappedSelection";
+ };
+
+ selProto.inspect = function() {
+ return inspect(this);
+ };
+
+ selProto.detach = function() {
+ this.win[windowPropertyName] = null;
+ this.win = this.anchorNode = this.focusNode = null;
+ };
+
+ WrappedSelection.inspect = inspect;
+
+ api.Selection = WrappedSelection;
+
+ api.selectionPrototype = selProto;
+
+ api.addCreateMissingNativeApiListener(function(win) {
+ if (typeof win.getSelection == "undefined") {
+ win.getSelection = function() {
+ return api.getSelection(this);
+ };
+ }
+ win = null;
+ });
+});
+
+/**
+ * @license CSS Class Applier module for Rangy.
+ * Adds, removes and toggles CSS classes on Ranges and Selections
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * http://code.google.com/p/rangy/
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2012, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.2.3
+ * Build date: 26 February 2012
+ */
+rangy.createModule("CssClassApplier", function(api, module) {
+ api.requireModules( ["WrappedSelection", "WrappedRange"] );
+
+ var dom = api.dom;
+
+
+
+ var defaultTagName = "span";
+
+ function trim(str) {
+ return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
+ }
+
+ function hasClass(el, cssClass) {
+ return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
+ }
+
+ function addClass(el, cssClass) {
+ if (el.className) {
+ if (!hasClass(el, cssClass)) {
+ el.className += " " + cssClass;
+ }
+ } else {
+ el.className = cssClass;
+ }
+ }
+
+ var removeClass = (function() {
+ function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
+ return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
+ }
+
+ return function(el, cssClass) {
+ if (el.className) {
+ el.className = el.className.replace(new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)"), replacer);
+ }
+ };
+ })();
+
+ function sortClassName(className) {
+ return className.split(/\s+/).sort().join(" ");
+ }
+
+ function getSortedClassName(el) {
+ return sortClassName(el.className);
+ }
+
+ function haveSameClasses(el1, el2) {
+ return getSortedClassName(el1) == getSortedClassName(el2);
+ }
+
+ function replaceWithOwnChildren(el) {
+
+ var parent = el.parentNode;
+ while (el.hasChildNodes()) {
+ parent.insertBefore(el.firstChild, el);
+ }
+ parent.removeChild(el);
+ }
+
+ function rangeSelectsAnyText(range, textNode) {
+ var textRange = range.cloneRange();
+ textRange.selectNodeContents(textNode);
+
+ var intersectionRange = textRange.intersection(range);
+ var text = intersectionRange ? intersectionRange.toString() : "";
+ textRange.detach();
+
+ return text != "";
+ }
+
+ function getEffectiveTextNodes(range) {
+ return range.getNodes([3], function(textNode) {
+ return rangeSelectsAnyText(range, textNode);
+ });
+ }
+
+ function elementsHaveSameNonClassAttributes(el1, el2) {
+ if (el1.attributes.length != el2.attributes.length) return false;
+ for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
+ attr1 = el1.attributes[i];
+ name = attr1.name;
+ if (name != "class") {
+ attr2 = el2.attributes.getNamedItem(name);
+ if (attr1.specified != attr2.specified) return false;
+ if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
+ }
+ }
+ return true;
+ }
+
+ function elementHasNonClassAttributes(el, exceptions) {
+ for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
+ attrName = el.attributes[i].name;
+ if ( !(exceptions && dom.arrayContains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function elementHasProps(el, props) {
+ for (var p in props) {
+ if (props.hasOwnProperty(p) && el[p] !== props[p]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var getComputedStyleProperty;
+
+ if (typeof window.getComputedStyle != "undefined") {
+ getComputedStyleProperty = function(el, propName) {
+ return dom.getWindow(el).getComputedStyle(el, null)[propName];
+ };
+ } else if (typeof document.documentElement.currentStyle != "undefined") {
+ getComputedStyleProperty = function(el, propName) {
+ return el.currentStyle[propName];
+ };
+ } else {
+ module.fail("No means of obtaining computed style properties found");
+ }
+
+ var isEditableElement;
+
+ (function() {
+ var testEl = document.createElement("div");
+ if (typeof testEl.isContentEditable == "boolean") {
+ isEditableElement = function(node) {
+ return node && node.nodeType == 1 && node.isContentEditable;
+ };
+ } else {
+ isEditableElement = function(node) {
+ if (!node || node.nodeType != 1 || node.contentEditable == "false") {
+ return false;
+ }
+ return node.contentEditable == "true" || isEditableElement(node.parentNode);
+ };
+ }
+ })();
+
+ function isEditingHost(node) {
+ var parent;
+ return node && node.nodeType == 1
+ && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
+ || (isEditableElement(node) && !isEditableElement(node.parentNode)));
+ }
+
+ function isEditable(node) {
+ return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
+ }
+
+ var inlineDisplayRegex = /^inline(-block|-table)?$/i;
+
+ function isNonInlineElement(node) {
+ return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
+ }
+
+ // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
+ var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
+
+ function isUnrenderedWhiteSpaceNode(node) {
+ if (node.data.length == 0) {
+ return true;
+ }
+ if (htmlNonWhiteSpaceRegex.test(node.data)) {
+ return false;
+ }
+ var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
+ switch (cssWhiteSpace) {
+ case "pre":
+ case "pre-wrap":
+ case "-moz-pre-wrap":
+ return false;
+ case "pre-line":
+ if (/[\r\n]/.test(node.data)) {
+ return false;
+ }
+ }
+
+ // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
+ // non-inline element, it will not be rendered. This seems to be a good enough definition.
+ return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
+ }
+
+ function isSplitPoint(node, offset) {
+ if (dom.isCharacterDataNode(node)) {
+ if (offset == 0) {
+ return !!node.previousSibling;
+ } else if (offset == node.length) {
+ return !!node.nextSibling;
+ } else {
+ return true;
+ }
+ }
+
+ return offset > 0 && offset < node.childNodes.length;
+ }
+
+ function splitNodeAt(node, descendantNode, descendantOffset, rangesToPreserve) {
+ var newNode;
+ var splitAtStart = (descendantOffset == 0);
+
+ if (dom.isAncestorOf(descendantNode, node)) {
+
+ return node;
+ }
+
+ if (dom.isCharacterDataNode(descendantNode)) {
+ if (descendantOffset == 0) {
+ descendantOffset = dom.getNodeIndex(descendantNode);
+ descendantNode = descendantNode.parentNode;
+ } else if (descendantOffset == descendantNode.length) {
+ descendantOffset = dom.getNodeIndex(descendantNode) + 1;
+ descendantNode = descendantNode.parentNode;
+ } else {
+ throw module.createError("splitNodeAt should not be called with offset in the middle of a data node ("
+ + descendantOffset + " in " + descendantNode.data);
+ }
+ }
+
+ if (isSplitPoint(descendantNode, descendantOffset)) {
+ if (!newNode) {
+ newNode = descendantNode.cloneNode(false);
+ if (newNode.id) {
+ newNode.removeAttribute("id");
+ }
+ var child;
+ while ((child = descendantNode.childNodes[descendantOffset])) {
+ newNode.appendChild(child);
+ }
+ dom.insertAfter(newNode, descendantNode);
+ }
+ return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, dom.getNodeIndex(newNode), rangesToPreserve);
+ } else if (node != descendantNode) {
+ newNode = descendantNode.parentNode;
+
+ // Work out a new split point in the parent node
+ var newNodeIndex = dom.getNodeIndex(descendantNode);
+
+ if (!splitAtStart) {
+ newNodeIndex++;
+ }
+ return splitNodeAt(node, newNode, newNodeIndex, rangesToPreserve);
+ }
+ return node;
+ }
+
+ function areElementsMergeable(el1, el2) {
+ return el1.tagName == el2.tagName && haveSameClasses(el1, el2) && elementsHaveSameNonClassAttributes(el1, el2);
+ }
+
+ function createAdjacentMergeableTextNodeGetter(forward) {
+ var propName = forward ? "nextSibling" : "previousSibling";
+
+ return function(textNode, checkParentElement) {
+ var el = textNode.parentNode;
+ var adjacentNode = textNode[propName];
+ if (adjacentNode) {
+ // Can merge if the node's previous/next sibling is a text node
+ if (adjacentNode && adjacentNode.nodeType == 3) {
+ return adjacentNode;
+ }
+ } else if (checkParentElement) {
+ // Compare text node parent element with its sibling
+ adjacentNode = el[propName];
+
+ if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) {
+ return adjacentNode[forward ? "firstChild" : "lastChild"];
+ }
+ }
+ return null;
+ }
+ }
+
+ var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
+ getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
+
+
+ function Merge(firstNode) {
+ this.isElementMerge = (firstNode.nodeType == 1);
+ this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
+ this.textNodes = [this.firstTextNode];
+ }
+
+ Merge.prototype = {
+ doMerge: function() {
+ var textBits = [], textNode, parent, text;
+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
+ textNode = this.textNodes[i];
+ parent = textNode.parentNode;
+ textBits[i] = textNode.data;
+ if (i) {
+ parent.removeChild(textNode);
+ if (!parent.hasChildNodes()) {
+ parent.parentNode.removeChild(parent);
+ }
+ }
+ }
+ this.firstTextNode.data = text = textBits.join("");
+ return text;
+ },
+
+ getLength: function() {
+ var i = this.textNodes.length, len = 0;
+ while (i--) {
+ len += this.textNodes[i].length;
+ }
+ return len;
+ },
+
+ toString: function() {
+ var textBits = [];
+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
+ textBits[i] = "'" + this.textNodes[i].data + "'";
+ }
+ return "[Merge(" + textBits.join(",") + ")]";
+ }
+ };
+
+ var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly"];
+
+ // Allow "class" as a property name in object properties
+ var mappedPropertyNames = {"class" : "className"};
+
+ function CssClassApplier(cssClass, options, tagNames) {
+ this.cssClass = cssClass;
+ var normalize, i, len, propName;
+
+ var elementPropertiesFromOptions = null;
+
+ // Initialize from options object
+ if (typeof options == "object" && options !== null) {
+ tagNames = options.tagNames;
+ elementPropertiesFromOptions = options.elementProperties;
+
+ for (i = 0; propName = optionProperties[i++]; ) {
+ if (options.hasOwnProperty(propName)) {
+ this[propName] = options[propName];
+ }
+ }
+ normalize = options.normalize;
+ } else {
+ normalize = options;
+ }
+
+ // Backwards compatibility: the second parameter can also be a Boolean indicating whether normalization
+ this.normalize = (typeof normalize == "undefined") ? true : normalize;
+
+ // Initialize element properties and attribute exceptions
+ this.attrExceptions = [];
+ var el = document.createElement(this.elementTagName);
+ this.elementProperties = {};
+ for (var p in elementPropertiesFromOptions) {
+ if (elementPropertiesFromOptions.hasOwnProperty(p)) {
+ // Map "class" to "className"
+ if (mappedPropertyNames.hasOwnProperty(p)) {
+ p = mappedPropertyNames[p];
+ }
+ el[p] = elementPropertiesFromOptions[p];
+
+ // Copy the property back from the dummy element so that later comparisons to check whether elements
+ // may be removed are checking against the right value. For example, the href property of an element
+ // returns a fully qualified URL even if it was previously assigned a relative URL.
+ this.elementProperties[p] = el[p];
+ this.attrExceptions.push(p);
+ }
+ }
+
+ this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ?
+ sortClassName(this.elementProperties.className + " " + cssClass) : cssClass;
+
+ // Initialize tag names
+ this.applyToAnyTagName = false;
+ var type = typeof tagNames;
+ if (type == "string") {
+ if (tagNames == "*") {
+ this.applyToAnyTagName = true;
+ } else {
+ this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
+ }
+ } else if (type == "object" && typeof tagNames.length == "number") {
+ this.tagNames = [];
+ for (i = 0, len = tagNames.length; i < len; ++i) {
+ if (tagNames[i] == "*") {
+ this.applyToAnyTagName = true;
+ } else {
+ this.tagNames.push(tagNames[i].toLowerCase());
+ }
+ }
+ } else {
+ this.tagNames = [this.elementTagName];
+ }
+ }
+
+ CssClassApplier.prototype = {
+ elementTagName: defaultTagName,
+ elementProperties: {},
+ ignoreWhiteSpace: true,
+ applyToEditableOnly: false,
+
+ hasClass: function(node) {
+ return node.nodeType == 1 && dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && hasClass(node, this.cssClass);
+ },
+
+ getSelfOrAncestorWithClass: function(node) {
+ while (node) {
+ if (this.hasClass(node, this.cssClass)) {
+ return node;
+ }
+ node = node.parentNode;
+ }
+ return null;
+ },
+
+ isModifiable: function(node) {
+ return !this.applyToEditableOnly || isEditable(node);
+ },
+
+ // White space adjacent to an unwrappable node can be ignored for wrapping
+ isIgnorableWhiteSpaceNode: function(node) {
+ return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
+ },
+
+ // Normalizes nodes after applying a CSS class to a Range.
+ postApply: function(textNodes, range, isUndo) {
+
+ var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
+
+ var merges = [], currentMerge;
+
+ var rangeStartNode = firstNode, rangeEndNode = lastNode;
+ var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
+
+ var textNode, precedingTextNode;
+
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+ precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
+
+ if (precedingTextNode) {
+ if (!currentMerge) {
+ currentMerge = new Merge(precedingTextNode);
+ merges.push(currentMerge);
+ }
+ currentMerge.textNodes.push(textNode);
+ if (textNode === firstNode) {
+ rangeStartNode = currentMerge.firstTextNode;
+ rangeStartOffset = rangeStartNode.length;
+ }
+ if (textNode === lastNode) {
+ rangeEndNode = currentMerge.firstTextNode;
+ rangeEndOffset = currentMerge.getLength();
+ }
+ } else {
+ currentMerge = null;
+ }
+ }
+
+ // Test whether the first node after the range needs merging
+ var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
+
+ if (nextTextNode) {
+ if (!currentMerge) {
+ currentMerge = new Merge(lastNode);
+ merges.push(currentMerge);
+ }
+ currentMerge.textNodes.push(nextTextNode);
+ }
+
+ // Do the merges
+ if (merges.length) {
+
+ for (i = 0, len = merges.length; i < len; ++i) {
+ merges[i].doMerge();
+ }
+
+
+ // Set the range boundaries
+ range.setStart(rangeStartNode, rangeStartOffset);
+ range.setEnd(rangeEndNode, rangeEndOffset);
+ }
+
+ },
+
+ createContainer: function(doc) {
+ var el = doc.createElement(this.elementTagName);
+ api.util.extend(el, this.elementProperties);
+ addClass(el, this.cssClass);
+ return el;
+ },
+
+ applyToTextNode: function(textNode) {
+
+
+ var parent = textNode.parentNode;
+ if (parent.childNodes.length == 1 && dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
+ addClass(parent, this.cssClass);
+ } else {
+ var el = this.createContainer(dom.getDocument(textNode));
+ textNode.parentNode.insertBefore(el, textNode);
+ el.appendChild(textNode);
+ }
+
+ },
+
+ isRemovable: function(el) {
+ return el.tagName.toLowerCase() == this.elementTagName
+ && getSortedClassName(el) == this.elementSortedClassName
+ && elementHasProps(el, this.elementProperties)
+ && !elementHasNonClassAttributes(el, this.attrExceptions)
+ && this.isModifiable(el);
+ },
+
+ undoToTextNode: function(textNode, range, ancestorWithClass) {
+
+ if (!range.containsNode(ancestorWithClass)) {
+ // Split out the portion of the ancestor from which we can remove the CSS class
+ //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
+ var ancestorRange = range.cloneRange();
+ ancestorRange.selectNode(ancestorWithClass);
+
+ if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)/* && isSplitPoint(range.endContainer, range.endOffset)*/) {
+ splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, [range]);
+ range.setEndAfter(ancestorWithClass);
+ }
+ if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)/* && isSplitPoint(range.startContainer, range.startOffset)*/) {
+ ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, [range]);
+ }
+ }
+
+ if (this.isRemovable(ancestorWithClass)) {
+ replaceWithOwnChildren(ancestorWithClass);
+ } else {
+ removeClass(ancestorWithClass, this.cssClass);
+ }
+ },
+
+ applyToRange: function(range) {
+ range.splitBoundaries();
+ var textNodes = getEffectiveTextNodes(range);
+
+ if (textNodes.length) {
+ var textNode;
+
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+
+ if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode)
+ && this.isModifiable(textNode)) {
+ this.applyToTextNode(textNode);
+ }
+ }
+ range.setStart(textNodes[0], 0);
+ textNode = textNodes[textNodes.length - 1];
+ range.setEnd(textNode, textNode.length);
+ if (this.normalize) {
+ this.postApply(textNodes, range, false);
+ }
+ }
+ },
+
+ applyToSelection: function(win) {
+
+ win = win || window;
+ var sel = api.getSelection(win);
+
+ var range, ranges = sel.getAllRanges();
+ sel.removeAllRanges();
+ var i = ranges.length;
+ while (i--) {
+ range = ranges[i];
+ this.applyToRange(range);
+ sel.addRange(range);
+ }
+
+ },
+
+ undoToRange: function(range) {
+
+ range.splitBoundaries();
+ var textNodes = getEffectiveTextNodes(range);
+ var textNode, ancestorWithClass;
+ var lastTextNode = textNodes[textNodes.length - 1];
+
+ if (textNodes.length) {
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+ ancestorWithClass = this.getSelfOrAncestorWithClass(textNode);
+ if (ancestorWithClass && this.isModifiable(textNode)) {
+ this.undoToTextNode(textNode, range, ancestorWithClass);
+ }
+
+ // Ensure the range is still valid
+ range.setStart(textNodes[0], 0);
+ range.setEnd(lastTextNode, lastTextNode.length);
+ }
+
+
+
+ if (this.normalize) {
+ this.postApply(textNodes, range, true);
+ }
+ }
+ },
+
+ undoToSelection: function(win) {
+ win = win || window;
+ var sel = api.getSelection(win);
+ var ranges = sel.getAllRanges(), range;
+ sel.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ range = ranges[i];
+ this.undoToRange(range);
+ sel.addRange(range);
+ }
+ },
+
+ getTextSelectedByRange: function(textNode, range) {
+ var textRange = range.cloneRange();
+ textRange.selectNodeContents(textNode);
+
+ var intersectionRange = textRange.intersection(range);
+ var text = intersectionRange ? intersectionRange.toString() : "";
+ textRange.detach();
+
+ return text;
+ },
+
+ isAppliedToRange: function(range) {
+ if (range.collapsed) {
+ return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
+ } else {
+ var textNodes = range.getNodes( [3] );
+ for (var i = 0, textNode; textNode = textNodes[i++]; ) {
+ if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode)
+ && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ },
+
+ isAppliedToSelection: function(win) {
+ win = win || window;
+ var sel = api.getSelection(win);
+ var ranges = sel.getAllRanges();
+ var i = ranges.length;
+ while (i--) {
+ if (!this.isAppliedToRange(ranges[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ toggleRange: function(range) {
+ if (this.isAppliedToRange(range)) {
+ this.undoToRange(range);
+ } else {
+ this.applyToRange(range);
+ }
+ },
+
+ toggleSelection: function(win) {
+ if (this.isAppliedToSelection(win)) {
+ this.undoToSelection(win);
+ } else {
+ this.applyToSelection(win);
+ }
+ },
+
+ detach: function() {}
+ };
+
+ function createCssClassApplier(cssClass, options, tagNames) {
+ return new CssClassApplier(cssClass, options, tagNames);
+ }
+
+ CssClassApplier.util = {
+ hasClass: hasClass,
+ addClass: addClass,
+ removeClass: removeClass,
+ hasSameClasses: haveSameClasses,
+ replaceWithOwnChildren: replaceWithOwnChildren,
+ elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
+ elementHasNonClassAttributes: elementHasNonClassAttributes,
+ splitNodeAt: splitNodeAt,
+ isEditableElement: isEditableElement,
+ isEditingHost: isEditingHost,
+ isEditable: isEditable
+ };
+
+ api.CssClassApplier = CssClassApplier;
+ api.createCssClassApplier = createCssClassApplier;
+});
+
+/**
+ * @license Selection save and restore module for Rangy.
+ * Saves and restores user selections using marker invisible elements in the DOM.
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * http://code.google.com/p/rangy/
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2012, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.2.3
+ * Build date: 26 February 2012
+ */
+rangy.createModule("SaveRestore", function(api, module) {
+ api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
+
+ var dom = api.dom;
+
+ var markerTextChar = "\ufeff";
+
+ function gEBI(id, doc) {
+ return (doc || document).getElementById(id);
+ }
+
+ function insertRangeBoundaryMarker(range, atStart) {
+ var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
+ var markerEl;
+ var doc = dom.getDocument(range.startContainer);
+
+ // Clone the Range and collapse to the appropriate boundary point
+ var boundaryRange = range.cloneRange();
+ boundaryRange.collapse(atStart);
+
+ // Create the marker element containing a single invisible character using DOM methods and insert it
+ markerEl = doc.createElement("span");
+ markerEl.id = markerId;
+ markerEl.style.lineHeight = "0";
+ markerEl.style.display = "none";
+ markerEl.className = "rangySelectionBoundary";
+ markerEl.appendChild(doc.createTextNode(markerTextChar));
+
+ boundaryRange.insertNode(markerEl);
+ boundaryRange.detach();
+ return markerEl;
+ }
+
+ function setRangeBoundary(doc, range, markerId, atStart) {
+ var markerEl = gEBI(markerId, doc);
+ if (markerEl) {
+ range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
+ markerEl.parentNode.removeChild(markerEl);
+ } else {
+ module.warn("Marker element has been removed. Cannot restore selection.");
+ }
+ }
+
+ function compareRanges(r1, r2) {
+ return r2.compareBoundaryPoints(r1.START_TO_START, r1);
+ }
+
+ function saveSelection(win) {
+ win = win || window;
+ var doc = win.document;
+ if (!api.isSelectionValid(win)) {
+ module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
+ return;
+ }
+ var sel = api.getSelection(win);
+ var ranges = sel.getAllRanges();
+ var rangeInfos = [], startEl, endEl, range;
+
+ // Order the ranges by position within the DOM, latest first
+ ranges.sort(compareRanges);
+
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ range = ranges[i];
+ if (range.collapsed) {
+ endEl = insertRangeBoundaryMarker(range, false);
+ rangeInfos.push({
+ markerId: endEl.id,
+ collapsed: true
+ });
+ } else {
+ endEl = insertRangeBoundaryMarker(range, false);
+ startEl = insertRangeBoundaryMarker(range, true);
+
+ rangeInfos[i] = {
+ startMarkerId: startEl.id,
+ endMarkerId: endEl.id,
+ collapsed: false,
+ backwards: ranges.length == 1 && sel.isBackwards()
+ };
+ }
+ }
+
+ // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
+ // between its markers
+ for (i = len - 1; i >= 0; --i) {
+ range = ranges[i];
+ if (range.collapsed) {
+ range.collapseBefore(gEBI(rangeInfos[i].markerId, doc));
+ } else {
+ range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
+ range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
+ }
+ }
+
+ // Ensure current selection is unaffected
+ sel.setRanges(ranges);
+ return {
+ win: win,
+ doc: doc,
+ rangeInfos: rangeInfos,
+ restored: false
+ };
+ }
+
+ function restoreSelection(savedSelection, preserveDirection) {
+ if (!savedSelection.restored) {
+ var rangeInfos = savedSelection.rangeInfos;
+ var sel = api.getSelection(savedSelection.win);
+ var ranges = [];
+
+ // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
+ // normalization affecting previously restored ranges.
+ for (var len = rangeInfos.length, i = len - 1, rangeInfo, range; i >= 0; --i) {
+ rangeInfo = rangeInfos[i];
+ range = api.createRange(savedSelection.doc);
+ if (rangeInfo.collapsed) {
+ var markerEl = gEBI(rangeInfo.markerId, savedSelection.doc);
+ if (markerEl) {
+ markerEl.style.display = "inline";
+ var previousNode = markerEl.previousSibling;
+
+ // Workaround for issue 17
+ if (previousNode && previousNode.nodeType == 3) {
+ markerEl.parentNode.removeChild(markerEl);
+ range.collapseToPoint(previousNode, previousNode.length);
+ } else {
+ range.collapseBefore(markerEl);
+ markerEl.parentNode.removeChild(markerEl);
+ }
+ } else {
+ module.warn("Marker element has been removed. Cannot restore selection.");
+ }
+ } else {
+ setRangeBoundary(savedSelection.doc, range, rangeInfo.startMarkerId, true);
+ setRangeBoundary(savedSelection.doc, range, rangeInfo.endMarkerId, false);
+ }
+
+ // Normalizing range boundaries is only viable if the selection contains only one range. For example,
+ // if the selection contained two ranges that were both contained within the same single text node,
+ // both would alter the same text node when restoring and break the other range.
+ if (len == 1) {
+ range.normalizeBoundaries();
+ }
+ ranges[i] = range;
+ }
+ if (len == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backwards) {
+ sel.removeAllRanges();
+ sel.addRange(ranges[0], true);
+ } else {
+ sel.setRanges(ranges);
+ }
+
+ savedSelection.restored = true;
+ }
+ }
+
+ function removeMarkerElement(doc, markerId) {
+ var markerEl = gEBI(markerId, doc);
+ if (markerEl) {
+ markerEl.parentNode.removeChild(markerEl);
+ }
+ }
+
+ function removeMarkers(savedSelection) {
+ var rangeInfos = savedSelection.rangeInfos;
+ for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
+ rangeInfo = rangeInfos[i];
+ if (rangeInfo.collapsed) {
+ removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
+ } else {
+ removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
+ removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
+ }
+ }
+ }
+
+ api.saveSelection = saveSelection;
+ api.restoreSelection = restoreSelection;
+ api.removeMarkerElement = removeMarkerElement;
+ api.removeMarkers = removeMarkers;
+});
+
+/**
+ * @license Serializer module for Rangy.
+ * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
+ * cookie or local storage and restore it on the user's next visit to the same page.
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * http://code.google.com/p/rangy/
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2012, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.2.3
+ * Build date: 26 February 2012
+ */
+rangy.createModule("Serializer", function(api, module) {
+ api.requireModules( ["WrappedSelection", "WrappedRange"] );
+ var UNDEF = "undefined";
+
+ // encodeURIComponent and decodeURIComponent are required for cookie handling
+ if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
+ module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");
+ }
+
+ // Checksum for checking whether range can be serialized
+ var crc32 = (function() {
+ function utf8encode(str) {
+ var utf8CharCodes = [];
+
+ for (var i = 0, len = str.length, c; i < len; ++i) {
+ c = str.charCodeAt(i);
+ if (c < 128) {
+ utf8CharCodes.push(c);
+ } else if (c < 2048) {
+ utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
+ } else {
+ utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
+ }
+ }
+ return utf8CharCodes;
+ }
+
+ var cachedCrcTable = null;
+
+ function buildCRCTable() {
+ var table = [];
+ for (var i = 0, j, crc; i < 256; ++i) {
+ crc = i;
+ j = 8;
+ while (j--) {
+ if ((crc & 1) == 1) {
+ crc = (crc >>> 1) ^ 0xEDB88320;
+ } else {
+ crc >>>= 1;
+ }
+ }
+ table[i] = crc >>> 0;
+ }
+ return table;
+ }
+
+ function getCrcTable() {
+ if (!cachedCrcTable) {
+ cachedCrcTable = buildCRCTable();
+ }
+ return cachedCrcTable;
+ }
+
+ return function(str) {
+ var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
+ for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
+ y = (crc ^ utf8CharCodes[i]) & 0xFF;
+ crc = (crc >>> 8) ^ crcTable[y];
+ }
+ return (crc ^ -1) >>> 0;
+ };
+ })();
+
+ var dom = api.dom;
+
+ function escapeTextForHtml(str) {
+ return str.replace(//g, ">");
+ }
+
+ function nodeToInfoString(node, infoParts) {
+ infoParts = infoParts || [];
+ var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
+ var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
+ var start = "", end = "";
+ switch (nodeType) {
+ case 3: // Text node
+ start = escapeTextForHtml(node.nodeValue);
+ break;
+ case 8: // Comment
+ start = "";
+ break;
+ default:
+ start = "<" + nodeInfo + ">";
+ end = ">";
+ break;
+ }
+ if (start) {
+ infoParts.push(start);
+ }
+ for (var i = 0; i < childCount; ++i) {
+ nodeToInfoString(children[i], infoParts);
+ }
+ if (end) {
+ infoParts.push(end);
+ }
+ return infoParts;
+ }
+
+ // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
+ // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
+ // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
+ // innerHTML whenever the user changes an input within the element.
+ function getElementChecksum(el) {
+ var info = nodeToInfoString(el).join("");
+ return crc32(info).toString(16);
+ }
+
+ function serializePosition(node, offset, rootNode) {
+ var pathBits = [], n = node;
+ rootNode = rootNode || dom.getDocument(node).documentElement;
+ while (n && n != rootNode) {
+ pathBits.push(dom.getNodeIndex(n, true));
+ n = n.parentNode;
+ }
+ return pathBits.join("/") + ":" + offset;
+ }
+
+ function deserializePosition(serialized, rootNode, doc) {
+ if (rootNode) {
+ doc = doc || dom.getDocument(rootNode);
+ } else {
+ doc = doc || document;
+ rootNode = doc.documentElement;
+ }
+ var bits = serialized.split(":");
+ var node = rootNode;
+ var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex;
+
+ while (i--) {
+ nodeIndex = parseInt(nodeIndices[i], 10);
+ if (nodeIndex < node.childNodes.length) {
+ node = node.childNodes[parseInt(nodeIndices[i], 10)];
+ } else {
+ throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) +
+ " has no child with index " + nodeIndex + ", " + i);
+ }
+ }
+
+ return new dom.DomPosition(node, parseInt(bits[1], 10));
+ }
+
+ function serializeRange(range, omitChecksum, rootNode) {
+ rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
+ if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) {
+ throw new Error("serializeRange: range is not wholly contained within specified root node");
+ }
+ var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
+ serializePosition(range.endContainer, range.endOffset, rootNode);
+ if (!omitChecksum) {
+ serialized += "{" + getElementChecksum(rootNode) + "}";
+ }
+ return serialized;
+ }
+
+ function deserializeRange(serialized, rootNode, doc) {
+ if (rootNode) {
+ doc = doc || dom.getDocument(rootNode);
+ } else {
+ doc = doc || document;
+ rootNode = doc.documentElement;
+ }
+ var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized);
+ var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);
+ if (checksum && checksum !== getElementChecksum(rootNode)) {
+ throw new Error("deserializeRange: checksums of serialized range root node (" + checksum +
+ ") and target root node (" + rootNodeChecksum + ") do not match");
+ }
+ var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
+ var range = api.createRange(doc);
+ range.setStart(start.node, start.offset);
+ range.setEnd(end.node, end.offset);
+ return range;
+ }
+
+ function canDeserializeRange(serialized, rootNode, doc) {
+ if (rootNode) {
+ doc = doc || dom.getDocument(rootNode);
+ } else {
+ doc = doc || document;
+ rootNode = doc.documentElement;
+ }
+ var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized);
+ var checksum = result[3];
+ return !checksum || checksum === getElementChecksum(rootNode);
+ }
+
+ function serializeSelection(selection, omitChecksum, rootNode) {
+ selection = selection || api.getSelection();
+ var ranges = selection.getAllRanges(), serializedRanges = [];
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
+ }
+ return serializedRanges.join("|");
+ }
+
+ function deserializeSelection(serialized, rootNode, win) {
+ if (rootNode) {
+ win = win || dom.getWindow(rootNode);
+ } else {
+ win = win || window;
+ rootNode = win.document.documentElement;
+ }
+ var serializedRanges = serialized.split("|");
+ var sel = api.getSelection(win);
+ var ranges = [];
+
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
+ ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
+ }
+ sel.setRanges(ranges);
+
+ return sel;
+ }
+
+ function canDeserializeSelection(serialized, rootNode, win) {
+ var doc;
+ if (rootNode) {
+ doc = win ? win.document : dom.getDocument(rootNode);
+ } else {
+ win = win || window;
+ rootNode = win.document.documentElement;
+ }
+ var serializedRanges = serialized.split("|");
+
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
+ if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ var cookieName = "rangySerializedSelection";
+
+ function getSerializedSelectionFromCookie(cookie) {
+ var parts = cookie.split(/[;,]/);
+ for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
+ nameVal = parts[i].split("=");
+ if (nameVal[0].replace(/^\s+/, "") == cookieName) {
+ val = nameVal[1];
+ if (val) {
+ return decodeURIComponent(val.replace(/\s+$/, ""));
+ }
+ }
+ }
+ return null;
+ }
+
+ function restoreSelectionFromCookie(win) {
+ win = win || window;
+ var serialized = getSerializedSelectionFromCookie(win.document.cookie);
+ if (serialized) {
+ deserializeSelection(serialized, win.doc)
+ }
+ }
+
+ function saveSelectionCookie(win, props) {
+ win = win || window;
+ props = (typeof props == "object") ? props : {};
+ var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
+ var path = props.path ? ";path=" + props.path : "";
+ var domain = props.domain ? ";domain=" + props.domain : "";
+ var secure = props.secure ? ";secure" : "";
+ var serialized = serializeSelection(api.getSelection(win));
+ win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
+ }
+
+ api.serializePosition = serializePosition;
+ api.deserializePosition = deserializePosition;
+
+ api.serializeRange = serializeRange;
+ api.deserializeRange = deserializeRange;
+ api.canDeserializeRange = canDeserializeRange;
+
+ api.serializeSelection = serializeSelection;
+ api.deserializeSelection = deserializeSelection;
+ api.canDeserializeSelection = canDeserializeSelection;
+
+ api.restoreSelectionFromCookie = restoreSelectionFromCookie;
+ api.saveSelectionCookie = saveSelectionCookie;
+
+ api.getElementChecksum = getElementChecksum;
+});
+YUI.add('moodle-editor_atto-rangy', 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 .
+
+if (!rangy.initialized) {
+ rangy.init();
+}
+
+
+}, '@VERSION@', {"requires": []});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-min.js
new file mode 100644
index 00000000000..f900c4eb35f
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-min.js
@@ -0,0 +1,10 @@
+window.rangy=function(){function u(n,r){var i=typeof n[r];return i==t||i==e&&!!n[r]||i=="unknown"}function a(t,n){return typeof t[n]==e&&!!t[n]}function f(e,t){return typeof e[t]!=n}function l(e){return function(t,n){var r=n.length;while(r--)if(!e(t,n[r]))return!1;return!0}}function d(e){return e&&c(e,o)&&p(e,s)}function m(e){window.alert("Rangy not supported in your browser. Reason: "+e),v.initialized=!0,v.supported=!1}function g(e){var t="Rangy warning: "+e;v.config.alertOnWarn?window.alert(t):typeof window.console!=n&&typeof window.console.log!=n&&window.console.log(t)}function w(){if(v.initialized)return;var e,t=!1,n=!1;u(document,"createRange")&&(e=document.createRange(),c(e,i)&&p(e,r)&&(t=!0),e.detach());var s=a(document,"body")?document.body:document.getElementsByTagName("body")[0];s&&u(s,"createTextRange")&&(e=s.createTextRange(),d(e)&&(n=!0)),!t&&!n&&m("Neither Range nor TextRange are implemented"),v.initialized=!0,v.features={implementsDomRange:t,implementsTextRange:n};var o=b.concat(y);for(var f=0,l=o.length;f["+e.childNodes.length+"]"}return e.nodeName}function C(e){this.root=e,this._next=e}function k(e){return new C(e)}function L(e,t){this.node=e,this.offset=t}function A(e){this.code=this[e],this.codeName=e,this.message="DOMException: "+this.codeName}var n="undefined",r=e.util;r.areHostMethods(document,["createDocumentFragment","createElement","createTextNode"])||t.fail("document missing a Node creation method"),r.isHostMethod(document,"getElementsByTagName")||t.fail("document missing getElementsByTagName method");var i=document.createElement("div");r.areHostMethods(i,["insertBefore","appendChild","cloneNode"]||!r.areHostObjects(i,["previousSibling","nextSibling","childNodes","parentNode"]))||t.fail("Incomplete Element implementation"),r.isHostProperty(i,"innerHTML")||t.fail("Element is missing innerHTML property");var s=document.createTextNode("test");r.areHostMethods(s,["splitText","deleteData","insertData","appendData","cloneNode"]||!r.areHostObjects(i,["previousSibling","nextSibling","childNodes","parentNode"])||!r.areHostProperties(s,["data"]))||t.fail("Incomplete Text Node implementation");var o=function(e,t){var n=e.length;while(n--)if(e[n]===t)return!0;return!1};C.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){var e=this._current=this._next,t,n;if(this._current){t=e.firstChild;if(t)this._next=t;else{n=null;while(e!==this.root&&!(n=e.nextSibling))e=e.parentNode;this._next=n}}return this._current},detach:function(){this._current=this._next=this.root=null}},L.prototype={equals:function(e){return this.node===e.node&this.offset==e.offset},inspect:function(){return"[DomPosition("+N(this.node)+":"+this.offset+")]"}},A.prototype={INDEX_SIZE_ERR:1,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INVALID_STATE_ERR:11},A.prototype.toString=function(){return this.message},e.dom={arrayContains:o,isHtmlNamespace:u,parentElement:a,getNodeIndex:f,getNodeLength:l,getCommonAncestor:c,isAncestorOf:h,getClosestAncestorIn:p,isCharacterDataNode:d,insertAfter:v,splitDataNode:m,getDocument:g,getWindow:y,getIframeWindow:w,getIframeDocument:b,getBody:E,getRootContainer:S,comparePoints:x,inspectNode:N,fragmentFromNodeChildren:T,createIterator:k,DomPosition:L},e.DOMException=A}),rangy.createModule("DomRange",function(e,t){function s(e,t){return e.nodeType!=3&&(n.isAncestorOf(e,t.startContainer,!0)||n.isAncestorOf(e,t.endContainer,!0))}function o(e){return n.getDocument(e.startContainer)}function u(e,t,n){var r=e._listeners[t];if(r)for(var i=0,s=r.length;i=t.childNodes.length?t.appendChild(e):t.insertBefore(e,t.childNodes[r]),i}function c(e){var t;for(var n,r=o(e.range).createDocumentFragment(),s;n=e.next();){t=e.isPartiallySelectedSubtree(),n=n.cloneNode(!t),t&&(s=e.getSubtreeIterator(),n.appendChild(c(s)),s.detach(!0));if(n.nodeType==10)throw new i("HIERARCHY_REQUEST_ERR");r.appendChild(n)}return r}function h(e,t,r){var i,s;r=r||{stop:!1};for(var o,u;o=e.next();)if(e.isPartiallySelectedSubtree()){if(t(o)===!1){r.stop=!0;return}u=e.getSubtreeIterator(),h(u,t,r),u.detach(!0);if(r.stop)return}else{i=n.createIterator(o);while(s=i.next())if(t(s)===!1){r.stop=!0;return}}}function p(e){var t;while(e.next())e.isPartiallySelectedSubtree()?(t=e.getSubtreeIterator(),p(t),t.detach(!0)):e.remove()}function d(e){for(var t,n=o(e.range).createDocumentFragment(),r;t=e.next();){e.isPartiallySelectedSubtree()?(t=t.cloneNode(!1),r=e.getSubtreeIterator(),t.appendChild(d(r)),r.detach(!0)):e.remove();if(t.nodeType==10)throw new i("HIERARCHY_REQUEST_ERR");n.appendChild(t)}return n}function v(e,t,n){var r=!!t&&!!t.length,i,s=!!n;r&&(i=new RegExp("^("+t.join("|")+")$"));var o=[];return h(new g(e,!1),function(e){(!r||i.test(e.nodeType))&&(!s||n(e))&&o.push(e)}),o}function m(e){var t=typeof e.getName=="undefined"?"Range":e.getName();return"["+t+"("+n.inspectNode(e.startContainer)+":"+e.startOffset+", "+n.inspectNode(e.endContainer)+":"+e.endOffset+")]"}function g(e,t){this.range=e,this.clonePartiallySelectedTextNodes=t;if(!e.collapsed){this.sc=e.startContainer,this.so=e.startOffset,this.ec=e.endContainer,this.eo=e.endOffset;var r=e.commonAncestorContainer;this.sc===this.ec&&n.isCharacterDataNode(this.sc)?(this.isSingleCharacterDataNode=!0,this._first=this._last=this._next=this.sc):(this._first=this._next=this.sc===r&&!n.isCharacterDataNode(this.sc)?this.sc.childNodes[this.so]:n.getClosestAncestorIn(this.sc,r,!0),this._last=this.ec===r&&!n.isCharacterDataNode(this.ec)?this.ec.childNodes[this.eo-1]:n.getClosestAncestorIn(this.ec,r,!0))}}function y(e){this.code=this[e],this.codeName=e,this.message="RangeException: "+this.codeName}function b(e,t,n){this.nodes=v(e,t,n),this._next=this.nodes[0],this._position=0}function N(e){return function(t,r){var i,s=r?t:t.parentNode;while(s){i=s.nodeType;if(n.arrayContains(e,i))return s;s=s.parentNode}return null}}function O(e,t){if(A(e,t))throw new y("INVALID_NODE_TYPE_ERR")}function M(e){if(!e.startContainer)throw new i("INVALID_STATE_ERR")}function _(e,t){if(!n.arrayContains(t,e.nodeType))throw new y("INVALID_NODE_TYPE_ERR")}function D(e,t){if(t<0||t>(n.isCharacterDataNode(e)?e.length:e.childNodes.length))throw new i("INDEX_SIZE_ERR")}function P(e,t){if(k(e,!0)!==k(t,!0))throw new i("WRONG_DOCUMENT_ERR")}function H(e){if(L(e,!0))throw new i("NO_MODIFICATION_ALLOWED_ERR")}function B(e,t){if(!e)throw new i(t)}function j(e){return!n.arrayContains(E,e.nodeType)&&!k(e,!0)}
+function F(e,t){return t<=(n.isCharacterDataNode(e)?e.length:e.childNodes.length)}function I(e){return!!e.startContainer&&!!e.endContainer&&!j(e.startContainer)&&!j(e.endContainer)&&F(e.startContainer,e.startOffset)&&F(e.endContainer,e.endOffset)}function q(e){M(e);if(!I(e))throw new Error("Range error: Range is no longer valid after DOM mutation ("+e.inspect()+")")}function et(){}function tt(e){e.START_TO_START=V,e.START_TO_END=$,e.END_TO_END=J,e.END_TO_START=K,e.NODE_BEFORE=Q,e.NODE_AFTER=G,e.NODE_BEFORE_AND_AFTER=Y,e.NODE_INSIDE=Z}function nt(e){tt(e),tt(e.prototype)}function rt(e,t){return function(){q(this);var r=this.startContainer,i=this.startOffset,s=this.commonAncestorContainer,o=new g(this,!0),u,a;r!==s&&(u=n.getClosestAncestorIn(r,s,!0),a=f(u),r=a.node,i=a.offset),h(o,H),o.reset();var l=e(o);return o.detach(),t(this,r,i,r,i),l}}function it(t,r,i){function o(e,t){return function(n){M(this),_(n,w),_(C(n),E);var r=(e?a:f)(n);(t?u:l)(this,r.node,r.offset)}}function u(e,t,i){var s=e.endContainer,o=e.endOffset;if(t!==e.startContainer||i!==e.startOffset){if(C(t)!=C(s)||n.comparePoints(t,i,s,o)==1)s=t,o=i;r(e,t,i,s,o)}}function l(e,t,i){var s=e.startContainer,o=e.startOffset;if(t!==e.endContainer||i!==e.endOffset){if(C(t)!=C(s)||n.comparePoints(t,i,s,o)==-1)s=t,o=i;r(e,s,o,t,i)}}function c(e,t,n){(t!==e.startContainer||n!==e.startOffset||t!==e.endContainer||n!==e.endOffset)&&r(e,t,n,t,n)}t.prototype=new et,e.util.extend(t.prototype,{setStart:function(e,t){M(this),O(e,!0),D(e,t),u(this,e,t)},setEnd:function(e,t){M(this),O(e,!0),D(e,t),l(this,e,t)},setStartBefore:o(!0,!0),setStartAfter:o(!1,!0),setEndBefore:o(!0,!1),setEndAfter:o(!1,!1),collapse:function(e){q(this),e?r(this,this.startContainer,this.startOffset,this.startContainer,this.startOffset):r(this,this.endContainer,this.endOffset,this.endContainer,this.endOffset)},selectNodeContents:function(e){M(this),O(e,!0),r(this,e,0,e,n.getNodeLength(e))},selectNode:function(e){M(this),O(e,!1),_(e,w);var t=a(e),n=f(e);r(this,t.node,t.offset,n.node,n.offset)},extractContents:rt(d,r),deleteContents:rt(p,r),canSurroundContents:function(){q(this),H(this.startContainer),H(this.endContainer);var e=new g(this,!0),t=e._first&&s(e._first,this)||e._last&&s(e._last,this);return e.detach(),!t},detach:function(){i(this)},splitBoundaries:function(){q(this);var e=this.startContainer,t=this.startOffset,i=this.endContainer,s=this.endOffset,o=e===i;n.isCharacterDataNode(i)&&s>0&&s0&&t=n.getNodeIndex(e)&&s++,t=0),r(this,e,t,i,s)},normalizeBoundaries:function(){q(this);var e=this.startContainer,t=this.startOffset,i=this.endContainer,s=this.endOffset,o=function(e){var t=e.nextSibling;t&&t.nodeType==e.nodeType&&(i=e,s=e.length,e.appendData(t.data),t.parentNode.removeChild(t))},u=function(r){var o=r.previousSibling;if(o&&o.nodeType==r.nodeType){e=r;var u=r.length;t=o.length,r.insertData(0,o.data),o.parentNode.removeChild(o);if(e==i)s+=t,i=e;else if(i==r.parentNode){var a=n.getNodeIndex(r);s==a?(i=r,s=u):s>a&&s--}}},a=!0;if(n.isCharacterDataNode(i))i.length==s&&o(i);else{if(s>0){var f=i.childNodes[s-1];f&&n.isCharacterDataNode(f)&&o(f)}a=!this.collapsed}if(a){if(n.isCharacterDataNode(e))t==0&&u(e);else if(tx",U=R.firstChild.nodeType==3}catch(z){}e.features.htmlParsingConforms=U;var W=U?function(e){var t=this.startContainer,r=n.getDocument(t);if(!t)throw new i("INVALID_STATE_ERR");var s=null;return t.nodeType==1?s=t:n.isCharacterDataNode(t)&&(s=n.parentElement(t)),s===null||s.nodeName=="HTML"&&n.isHtmlNamespace(n.getDocument(s).documentElement)&&n.isHtmlNamespace(s)?s=r.createElement("body"):s=s.cloneNode(!1),s.innerHTML=e,n.fragmentFromNodeChildren(s)}:function(e){M(this);var t=o(this),r=t.createElement("body");return r.innerHTML=e,n.fragmentFromNodeChildren(r)},X=["startContainer","startOffset","endContainer","endOffset","collapsed","commonAncestorContainer"],V=0,$=1,J=2,K=3,Q=0,G=1,Y=2,Z=3;et.prototype={attachListener:function(e,t){this._listeners[e].push(t)},compareBoundaryPoints:function(e,t){q(this),P(this.startContainer,t.startContainer);var r,i,s,o,u=e==K||e==V?"start":"end",a=e==$||e==V?"start":"end";return r=this[u+"Container"],i=this[u+"Offset"],s=t[a+"Container"],o=t[a+"Offset"],n.comparePoints(r,i,s,o)},insertNode:function(e){q(this),_(e,x),H(this.startContainer);if(n.isAncestorOf(e,this.startContainer,!0))throw new i("HIERARCHY_REQUEST_ERR");var t=l(e,this.startContainer,this.startOffset);this.setStartBefore(t)},cloneContents:function(){q(this);var e,t;if(this.collapsed)return o(this).createDocumentFragment();if(this.startContainer===this.endContainer&&n.isCharacterDataNode(this.startContainer))return e=this.startContainer.cloneNode(!0),e.data=e.data.slice(this.startOffset,this.endOffset),t=o(this).createDocumentFragment(),t.appendChild(e),t;var r=new g(this,!0);return e=c(r),r.detach(),e},canSurroundContents:function(){q(this),H(this.startContainer),H(this.endContainer);var e=new g(this,!0),t=e._first&&s(e._first,this)||e._last&&s(e._last,this);return e.detach(),!t},surroundContents:function(e){_(e,T);if(!this.canSurroundContents())throw new y("BAD_BOUNDARYPOINTS_ERR");var t=this.extractContents();if(e.hasChildNodes())while(e.lastChild)e.removeChild(e.lastChild);l(e,this.startContainer,this.startOffset),e.appendChild(t),this.selectNode(e)},cloneRange:function(){q(this);var e=new at(o(this)),t=X.length,n;while(t--)n=X[t],e[n]=this[n];return e},toString:function(){q(this);var e=this.startContainer;if(e===this.endContainer&&n.isCharacterDataNode(e))return e.nodeType==3||e.nodeType==4?e.data.slice(this.startOffset,this.endOffset):"";var t=[],r=new g(this,!0);return h(r,function(e){(e.nodeType==3||e.nodeType==4)&&t.push(e.data)}),r.detach(),t.join("")},compareNode:function(e){q(this);var t=e.parentNode,r=n.getNodeIndex(e);if(!t)throw new i("NOT_FOUND_ERR");var s=this.comparePoint(t,r),o=this.comparePoint(t,r+1);return s<0?o>0?Y:Q:o>0?G:Z},comparePoint:function(e,t){return q(this),B(e,"HIERARCHY_REQUEST_ERR"),P(e,this.startContainer),n.comparePoints(e,t,this.startContainer,this.startOffset)<0?-1:n.comparePoints(e,t,this.endContainer,this.endOffset)>0?1:0},createContextualFragment:W,toHtml:function(){q(this);var e=o(this).createElement("div");return e.appendChild(this.cloneContents()),e.innerHTML},intersectsNode:function(e,t){q(this),B(e,"NOT_FOUND_ERR");if(n.getDocument(e)!==o(this))return!1;var r=e.parentNode,i=n.getNodeIndex(e);B(r,"NOT_FOUND_ERR");var s=n.comparePoints(r,i,this.endContainer,this.endOffset),u=n.comparePoints(r,i+1,this.startContainer,this.startOffset);return t?s<=0&&u>=0:s<0&&u>0},isPointInRange:function(e,t){return q(this),B(e,"HIERARCHY_REQUEST_ERR"),P(e,this.startContainer),n.comparePoints(e,t,this.startContainer,this.startOffset)>=0&&n.comparePoints(e,t,this.endContainer,this.endOffset)<=0},intersectsRange:function(e,t){q(this);if(o(e)!=o(this))throw new i("WRONG_DOCUMENT_ERR");var r=n.comparePoints(this.startContainer,this.startOffset,e.endContainer,e.endOffset),s=n.comparePoints(this.endContainer,this.endOffset,e.startContainer,e.startOffset);return t?r<=0&&s>=0:r<0&&s>0},intersection:function(e){if(this.intersectsRange(e)){var t=n.comparePoints(this.startContainer,this.startOffset,e.startContainer,e.startOffset),r=n.comparePoints(this.endContainer,this.endOffset,e.endContainer,e.endOffset),i=this.cloneRange();return t==-1&&i.setStart(e.startContainer,e.startOffset),r==1&&i.setEnd(e.endContainer,e.endOffset),i}return null},union:function(e){if(this.intersectsRange(e,!0)){var t=this.cloneRange();return n.comparePoints(e.startContainer,e.startOffset,this.startContainer,this.startOffset)==-1&&t.setStart(e.startContainer,e.startOffset),n.comparePoints(e.endContainer,e.endOffset,this.endContainer,this.endOffset)==1&&t.setEnd(e.endContainer,e.endOffset),t}throw new y("Ranges do not intersect")},containsNode:function(e,t){return t?this.intersectsNode(e,!1):this.compareNode(e)==Z},containsNodeContents:function(e){return this.comparePoint(e,0)>=0&&this.comparePoint(e,n.getNodeLength(e))<=0},containsRange:function(e){return this.intersection(e).equals(e)},containsNodeText:function(e){var t=this.cloneRange();t.selectNode(e);var n=t.getNodes([3]);if(n.length>0){t.setStart(n[0],0);var r=n.pop();t.setEnd(r,r.length);var i=this.containsRange(t);return t.detach(),i}return this.containsNodeContents(e)},createNodeIterator:function(e,t){return q(this),new b(this,e,t)},getNodes:function(e,t){return q(this),v(this,e,t)},getDocument:function(){return o(this)},collapseBefore:function(e){M(this),this.setEndBefore(e),this.collapse(!1)},collapseAfter:function(e){M(this),this.setStartAfter(e),this.collapse(!0)},getName:function(){return"DomRange"},equals:function(e){return at.rangesEqual(this,e)},isValid:function(){return I(this)},inspect:function(){return m(this)}},it(at,ot,ut),e.rangePrototype=et.prototype,at.rangeProperties=X,at.RangeIterator=g,at.copyComparisonConstants=nt,at.createPrototypeRange=it,at.inspect=m,at.getRangeDocument=o,at.rangesEqual=function(e,t){return e.startContainer===t.startContainer&&e.startOffset===t
+.startOffset&&e.endContainer===t.endContainer&&e.endOffset===t.endOffset},e.DomRange=at,e.RangeException=y}),rangy.createModule("WrappedRange",function(e,t){function o(e){var t=e.parentElement(),n=e.duplicate();n.collapse(!0);var i=n.parentElement();n=e.duplicate(),n.collapse(!1);var s=n.parentElement(),o=i==s?i:r.getCommonAncestor(i,s);return o==t?o:r.getCommonAncestor(t,o)}function u(e){return e.compareEndPoints("StartToEnd",e)==0}function a(e,t,n,s){var o=e.duplicate();o.collapse(n);var u=o.parentElement();r.isAncestorOf(t,u,!0)||(u=t);if(!u.canHaveHTML)return new i(u.parentNode,r.getNodeIndex(u));var a=r.getDocument(u).createElement("span"),f,l=n?"StartToStart":"StartToEnd",c,h,p,d;do u.insertBefore(a,a.previousSibling),o.moveToElementText(a);while((f=o.compareEndPoints(l,e))>0&&a.previousSibling);d=a.nextSibling;if(f==-1&&d&&r.isCharacterDataNode(d)){o.setEndPoint(n?"EndToStart":"EndToEnd",e);var v;if(/[\r\n]/.test(d.data)){var m=o.duplicate(),g=m.text.replace(/\r\n/g,"\r").length;v=m.moveStart("character",g);while((f=m.compareEndPoints("StartToEnd",m))==-1)v++,m.moveStart("character",1)}else v=o.text.length;p=new i(d,v)}else c=(s||!n)&&a.previousSibling,h=(s||n)&&a.nextSibling,h&&r.isCharacterDataNode(h)?p=new i(h,0):c&&r.isCharacterDataNode(c)?p=new i(c,c.length):p=new i(u,r.getNodeIndex(a));return a.parentNode.removeChild(a),p}function f(e,t){var n,i,s=e.offset,o=r.getDocument(e.node),u,a,f=o.body.createTextRange(),l=r.isCharacterDataNode(e.node);return l?(n=e.node,i=n.parentNode):(a=e.node.childNodes,n=s12