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 6a257507b0f..6554e4e9d35 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
@@ -50,8 +50,7 @@ M.atto_bold = M.atto_bold || {
// 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, 'bold');
} else {
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 95d24988962..b0c51682bf5 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){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),M.editor_atto.add_button_shortcut({action:"bold",keys:66});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","moodle-editor_atto-editor-shortcut"]});
+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),M.editor_atto.add_button_shortcut({action:"bold",keys:66}),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,"bold"):M.editor_atto.remove_widget_highlight(e.elementid,"bold")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
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 6a257507b0f..6554e4e9d35 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
@@ -50,8 +50,7 @@ M.atto_bold = M.atto_bold || {
// 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, 'bold');
} else {
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 1fa76664d5d..acff27eae92 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
@@ -48,8 +48,7 @@ M.atto_bold = M.atto_bold || {
// 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, 'bold');
} else {
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 31ac2fd4116..4fee53bca80 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
@@ -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/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 e5de63d3fe5..6c40ed061fa 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");if(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);var n=M.editor_atto.get_editable_node(e.elementid);n.on("atto:selectionchanged",function(e){M.atto_equation.resolve_equation()!==!1?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 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);