From d321f68b3dd3d4cb77e9037d89b7fe2bae77b657 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Mon, 10 Mar 2014 16:39:21 +0800 Subject: [PATCH] MDL-44219 Atto: Convert selection logic to use Rangy JS Library This removes alot of complex, hard to test, hard to debug code from the editor so we can focus on the things we want to spend time on. Rangy seems a good choice for browser support, stability and features. --- .../moodle-atto_equation-button-debug.js | 2 +- .../moodle-atto_equation-button-min.js | 2 +- .../moodle-atto_equation-button.js | 2 +- .../equation/yui/src/button/js/button.js | 2 +- .../moodle-atto_image-button-debug.js | 24 +- .../moodle-atto_image-button-min.js | 4 +- .../moodle-atto_image-button.js | 24 +- .../plugins/image/yui/src/button/js/button.js | 24 +- lib/editor/atto/thirdpartylibs.xml | 10 + .../moodle-editor_atto-editor-debug.js | 515 +- .../moodle-editor_atto-editor-min.js | 7 +- .../moodle-editor_atto-editor.js | 515 +- .../moodle-editor_atto-rangy-debug.js | 4458 +++++++++++++++++ .../moodle-editor_atto-rangy-min.js | 10 + .../moodle-editor_atto-rangy.js | 4458 +++++++++++++++++ lib/editor/atto/yui/src/editor/js/editor.js | 512 +- .../atto/yui/src/editor/meta/editor.json | 3 +- lib/editor/atto/yui/src/rangy/build.json | 19 + lib/editor/atto/yui/src/rangy/js/init.js | 18 + lib/editor/atto/yui/src/rangy/js/license.txt | 21 + .../atto/yui/src/rangy/js/rangy-core.js | 3224 ++++++++++++ .../yui/src/rangy/js/rangy-cssclassapplier.js | 713 +++ .../rangy/js/rangy-selectionsaverestore.js | 195 + .../atto/yui/src/rangy/js/rangy-serializer.js | 300 ++ lib/editor/atto/yui/src/rangy/meta/rangy.json | 5 + 25 files changed, 13767 insertions(+), 1300 deletions(-) create mode 100644 lib/editor/atto/thirdpartylibs.xml create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-debug.js create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy-min.js create mode 100644 lib/editor/atto/yui/build/moodle-editor_atto-rangy/moodle-editor_atto-rangy.js create mode 100644 lib/editor/atto/yui/src/rangy/build.json create mode 100644 lib/editor/atto/yui/src/rangy/js/init.js create mode 100644 lib/editor/atto/yui/src/rangy/js/license.txt create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-core.js create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-cssclassapplier.js create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-selectionsaverestore.js create mode 100644 lib/editor/atto/yui/src/rangy/js/rangy-serializer.js create mode 100644 lib/editor/atto/yui/src/rangy/meta/rangy.json diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js index 4fee53bca80..c0c14aede45 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js @@ -81,7 +81,7 @@ M.atto_equation = M.atto_equation || { M.editor_atto.focus(elementid); } M.atto_equation.selection = M.editor_atto.get_selection(); - if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) { + if (M.atto_equation.selection) { var dialogue; if (!M.atto_equation.dialogue) { dialogue = new M.core.dialogue({ diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js index 6c40ed061fa..0f416d5eab3 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js @@ -1 +1 @@ -YUI.add("moodle-atto_equation-button",function(e,t){M.atto_equation=M.atto_equation||{dialogue:null,selection:null,contextids:{},library:{},lastcursor:0,display_chooser:function(t,n){t.preventDefault(),M.editor_atto.is_active(n)||M.editor_atto.focus(n),M.atto_equation.selection=M.editor_atto.get_selection();if(M.atto_equation.selection!==!1&&!M.atto_equation.selection.collapsed){var r;M.atto_equation.dialogue?r=M.atto_equation.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.render(),r.set("bodyContent",M.atto_equation.get_form_content(n)),r.set("headerContent",M.util.get_string("pluginname","atto_equation"));var i=new e.TabView({srcNode:"#atto_equation_library"});i.render(),r.show();var s=M.atto_equation.resolve_equation();s&&e.one("#atto_equation_equation").set("text",s),M.atto_equation.update_preview(!1,n),M.atto_equation.dialogue=r}},init:function(e){var t=M.util.image_url("e/math","core");e.texfilteractive&&(this.contextids[e.elementid]=e.contextid,this.library=e.library,M.editor_atto.add_toolbar_button(e.elementid,"equation",t,e.group,this.display_chooser),M.editor_atto.on("atto:selectionchanged",function(e){M.atto_equation.resolve_equation()?M.editor_atto.add_widget_highlight(e.elementid,"equation"):M.editor_atto.remove_widget_highlight(e.elementid,"equation")}))},resolve_equation:function(){var t=M.editor_atto.get_selection_parent_node(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},set_equation:function(t,n){var r,i,s,o,u,a;t.preventDefault(),M.atto_equation.dialogue.hide(),M.editor_atto.set_selection(M.atto_equation.selection),r=t.currentTarget.ancestor(".atto_form").one("textarea"),a=r.get("value"),a!==""&&(a="$$ "+a.trim()+" $$",i=e.one(M.editor_atto.get_selection_parent_node()),s=i.get("text"),o=/\$\$[\S\s]*\$\$/,u=o.exec(s),u&&u.length?(u=u.pop(),s=s.replace(u,"$$"+a+"$$"),i.set("text",s)):M.editor_atto.insert_html_at_focus_point(a),M.editor_atto.text_updated(n))},update_preview:function(t,n){var r=e.one("#atto_equation_equation"),i=r.get("value"),s,o,u="",a="\\square ",f=r.get("selectionStart");f||(f=0);while(i.charAt(f)==="\\"&&f>0)f-=1;var l=/[\w\{\}]/;while(l.test(i.charAt(f))&&f'+this.get_library_html(t)+'"+'
'+"

"+M.util.get_string("editequation_desc","atto_equation")+"

"+'"+'
'+'
'+"
"+'"+"
"+"");return n.one("#atto_equation_submit").on("click",M.atto_equation.set_equation,this,t),n.one("#atto_equation_equation").on("valuechange",M.atto_equation.update_preview,this,t),n.one("#atto_equation_equation").on("keyup",M.atto_equation.update_preview,this,t),n.one("#atto_equation_equation").on("mouseup",M.atto_equation.update_preview,this,t),n.delegate("click",M.atto_equation.select_library_item,"#atto_equation_library button",this,t),n},select_library_item:function(e,t){var n=e.currentTarget.getAttribute("data-tex");e.preventDefault(),input=e.currentTarget.ancestor(".atto_form").one("textarea"),value=input.get("value"),value=value.substring(0,this.lastcursorpos)+n+value.substring(this.lastcursorpos,value.length),input.set("value",value),input.focus();var r=this.lastcursorpos+n.length,i=input.getDOMNode();if(typeof i.selectionStart=="number")i.selectionStart=i.selectionEnd=r;else if(typeof i.createTextRange!="undefined"){var s=i.createTextRange();s.moveToPoint(r),s.select()}M.atto_equation.update_preview(!1,t)},get_library_html:function(t){var n='
',r=0,i=1;n+="",n+="
";for(i=1;i<5;i++){n+='
';var s=this.library["group"+i].split("\n");for(r=0;r$$'+s[r]+"$$");n+="
"}n+="
",n+="
";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"]}); +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){var r;M.atto_equation.dialogue?r=M.atto_equation.dialogue:r=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0,width:"800px"}),r.render(),r.set("bodyContent",M.atto_equation.get_form_content(n)),r.set("headerContent",M.util.get_string("pluginname","atto_equation"));var i=new e.TabView({srcNode:"#atto_equation_library"});i.render(),r.show();var s=M.atto_equation.resolve_equation();s&&e.one("#atto_equation_equation").set("text",s),M.atto_equation.update_preview(!1,n),M.atto_equation.dialogue=r}},init:function(e){var t=M.util.image_url("e/math","core");e.texfilteractive&&(this.contextids[e.elementid]=e.contextid,this.library=e.library,M.editor_atto.add_toolbar_button(e.elementid,"equation",t,e.group,this.display_chooser),M.editor_atto.on("atto:selectionchanged",function(e){M.atto_equation.resolve_equation()?M.editor_atto.add_widget_highlight(e.elementid,"equation"):M.editor_atto.remove_widget_highlight(e.elementid,"equation")}))},resolve_equation:function(){var t=M.editor_atto.get_selection_parent_node(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},set_equation:function(t,n){var r,i,s,o,u,a;t.preventDefault(),M.atto_equation.dialogue.hide(),M.editor_atto.set_selection(M.atto_equation.selection),r=t.currentTarget.ancestor(".atto_form").one("textarea"),a=r.get("value"),a!==""&&(a="$$ "+a.trim()+" $$",i=e.one(M.editor_atto.get_selection_parent_node()),s=i.get("text"),o=/\$\$[\S\s]*\$\$/,u=o.exec(s),u&&u.length?(u=u.pop(),s=s.replace(u,"$$"+a+"$$"),i.set("text",s)):M.editor_atto.insert_html_at_focus_point(a),M.editor_atto.text_updated(n))},update_preview:function(t,n){var r=e.one("#atto_equation_equation"),i=r.get("value"),s,o,u="",a="\\square ",f=r.get("selectionStart");f||(f=0);while(i.charAt(f)==="\\"&&f>0)f-=1;var l=/[\w\{\}]/;while(l.test(i.charAt(f))&&f'+this.get_library_html(t)+'"+'
'+"

"+M.util.get_string("editequation_desc","atto_equation")+"

"+'"+'
'+'
'+"
"+'"+"
"+"");return n.one("#atto_equation_submit").on("click",M.atto_equation.set_equation,this,t),n.one("#atto_equation_equation").on("valuechange",M.atto_equation.update_preview,this,t),n.one("#atto_equation_equation").on("keyup",M.atto_equation.update_preview,this,t),n.one("#atto_equation_equation").on("mouseup",M.atto_equation.update_preview,this,t),n.delegate("click",M.atto_equation.select_library_item,"#atto_equation_library button",this,t),n},select_library_item:function(e,t){var n=e.currentTarget.getAttribute("data-tex");e.preventDefault(),input=e.currentTarget.ancestor(".atto_form").one("textarea"),value=input.get("value"),value=value.substring(0,this.lastcursorpos)+n+value.substring(this.lastcursorpos,value.length),input.set("value",value),input.focus();var r=this.lastcursorpos+n.length,i=input.getDOMNode();if(typeof i.selectionStart=="number")i.selectionStart=i.selectionEnd=r;else if(typeof i.createTextRange!="undefined"){var s=i.createTextRange();s.moveToPoint(r),s.select()}M.atto_equation.update_preview(!1,t)},get_library_html:function(t){var n='
',r=0,i=1;n+="",n+="
";for(i=1;i<5;i++){n+='
';var s=this.library["group"+i].split("\n");for(r=0;r$$'+s[r]+"$$");n+="
"}n+="
",n+="
";var o=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",u={sesskey:M.cfg.sesskey,contextid:this.contextids[t],action:"filtertext",text:n};return preview=e.io(o,{sync:!0,data:u,method:"POST"}),preview.status===200&&(n=preview.responseText),n}}},"@VERSION@",{requires:["node","escape","io","event-valuechange","tabview"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js index 4fee53bca80..c0c14aede45 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js @@ -81,7 +81,7 @@ M.atto_equation = M.atto_equation || { M.editor_atto.focus(elementid); } M.atto_equation.selection = M.editor_atto.get_selection(); - if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) { + if (M.atto_equation.selection) { var dialogue; if (!M.atto_equation.dialogue) { dialogue = new M.core.dialogue({ diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js index 9253c0b5a3c..8fde5344f23 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js @@ -79,7 +79,7 @@ M.atto_equation = M.atto_equation || { M.editor_atto.focus(elementid); } M.atto_equation.selection = M.editor_atto.get_selection(); - if (M.atto_equation.selection !== false && (!M.atto_equation.selection.collapsed)) { + if (M.atto_equation.selection) { var dialogue; if (!M.atto_equation.dialogue) { dialogue = new M.core.dialogue({ diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js index 566b652527d..0dd4d4aee30 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js @@ -145,6 +145,7 @@ M.atto_image = M.atto_image || { dialogue: null, selection: null, currentlyselected : {}, + lastselectedimage : null, init: function(params) { var display_chooser = function(e, elementid) { e.preventDefault(); @@ -183,7 +184,7 @@ M.atto_image = M.atto_image || { // This includes cursor changes, we check if the button should be active or not, based // on the text selection. M.editor_atto.on('atto:selectionchanged', function(e) { - if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) { + if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) { M.editor_atto.add_widget_highlight(e.elementid, 'image'); M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes; } else { @@ -263,8 +264,13 @@ M.atto_image = M.atto_image || { M.atto_image.dialogue.hide(); + M.editor_atto.focus(elementid); if (url !== '') { - M.editor_atto.set_selection(M.atto_image.selection); + if (this.lastselectedimage) { + M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage)); + } else { + M.editor_atto.set_selection(M.atto_image.selection); + } imagehtml = '' + Y.Escape.html(alt) + ' 0) { + if (images) { + images = images.filter('img'); + } + + if (images && images.size()) { image = images.item(0); - for (i in images) { - if (images[i].test('img')) { - image = images[i]; - break; - } - } + this.lastselectedimage = image; + style = image.getAttribute('style'); width = parseInt(image.getAttribute('width'), 10); height = parseInt(image.getAttribute('height'), 10); diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js index 074c97fd6eb..5aec326d218 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,currentlyselected:{},init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t),M.editor_atto.currentlyselected=M.editor_atto.currentlyselected||{},M.editor_atto.currentlyselected[e.elementid]=null,M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?(M.editor_atto.add_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=e.selectedNodes):(M.editor_atto.remove_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=null)})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),s!==""&&(M.editor_atto.set_selection(M.atto_image.selection),c=''+e.Escape.html(o)+'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='
"+''+"
",M.editor_atto.can_show_filepicker(t,"image")&&(s+='"+"
"),s+='"+'"+''+"
",s+=''+'"+"
",s+='"+''+"
",s+='"+''+"
",s+='"+'
",s+='"+''+'
'+"
",s+='"+"
"+"
";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]}); +YUI.add("moodle-atto_image-button",function(e,t){var n={TAGS:"img"},r={INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUMBIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",IMAGEPREVIEW:"atto_image_preview"},i,s;s=function(e,t){this.value=e,this.style=t,this.regex=new RegExp(this._regex_escape(t)+" *: *"+this._regex_escape(e))},s.prototype={value:null,style:null,regex:null,test:function(e){return this.regex.test(e)},_regex_escape:function(e){return e.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1")},apply:function(e){var t=e.getAttribute("style");t!==""&&t.substr(t.length-1,1)!==";"&&(t+=";"),t+=this.style+": "+this.value+";"},to_select_option:function(){var e=M.util.get_string("alignment_"+this.value.replace("-",""),"atto_image");return value=this.style+": "+this.value,'"}},i=[new s("baseline","vertical-align"),new s("sub","vertical-align"),new s("super","vertical-align"),new s("top","vertical-align"),new s("text-top","vertical-align"),new s("middle","vertical-align"),new s("bottom","vertical-align"),new s("text-bottom","vertical-align"),new s("left","float"),new s("right","float")],M.atto_image=M.atto_image||{dialogue:null,selection:null,currentlyselected:{},lastselectedimage:null,init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),M.atto_image.selection=M.editor_atto.get_selection();if(M.atto_image.selection!==!1){var n;M.atto_image.dialogue?n=M.atto_image.dialogue:n=new M.core.dialogue({visible:!1,modal:!0,close:!0,draggable:!0}),n.set("bodyContent",M.atto_image.get_form_content(t)),n.set("headerContent",M.util.get_string("createimage","atto_image")),n.render(),n.centerDialogue(),M.atto_image.dialogue=n,n.show()}},r=M.util.image_url("e/insert_edit_image","core");M.editor_atto.add_toolbar_button(e.elementid,"image",r,e.group,t),M.editor_atto.currentlyselected=M.editor_atto.currentlyselected||{},M.editor_atto.currentlyselected[e.elementid]=null,M.editor_atto.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes,!1)?(M.editor_atto.add_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=e.selectedNodes):(M.editor_atto.remove_widget_highlight(e.elementid,"image"),M.editor_atto.currentlyselected[e.elementid]=null)})},open_filepicker:function(e){var t=this.getAttribute("data-editor");e.preventDefault(),M.editor_atto.show_filepicker(t,"image",M.atto_image.filepicker_callback)},filepicker_callback:function(t){if(t.url!==""){var n=e.one("#"+r.INPUTURL);n.set("value",t.url);var i=new Image;i.onload=function(){e.one("#"+r.INPUTWIDTH).set("value",this.width),e.one("#"+r.INPUTHEIGHT).set("value",this.height),e.one("#"+r.IMAGEPREVIEW).set("src",this.src),e.one("#"+r.IMAGEPREVIEW).setStyle("display","inline")},i.src=t.url}},url_changed:function(){var t=e.one("#"+r.INPUTURL);if(t.get("value")!==""){var n=new Image;n.onload=function(){var t;t=e.one("#"+r.INPUTWIDTH),t.get("value")===""&&t.set("value",this.width),t=e.one("#"+r.INPUTHEIGHT),t.get("value")===""&&t.set("value",this.height),t=e.one("#"+r.IMAGEPREVIEW),t.set("src",this.src),t.setStyle("display","inline")},n.src=t.get("value")}},set_image:function(t,n){var i=t.currentTarget.ancestor(".atto_form"),s=i.one("#"+r.INPUTURL).get("value"),o=i.one("#"+r.INPUTALT).get("value"),u=i.one("#"+r.INPUTWIDTH).get("value"),a=i.one("#"+r.INPUTHEIGHT).get("value"),f=i.one("#"+r.INPUTALIGNMENT).get("value"),l=i.one("#"+r.IMAGEPRESENTATION).get("checked"),c;t.preventDefault();if(o===""&&!l){i.one("#"+r.IMAGEALTWARNING).setStyle("display","block"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!0),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!0);return}i.one("#"+r.IMAGEALTWARNING).setStyle("display","none"),i.one("#"+r.INPUTALT).setAttribute("aria-invalid",!1),i.one("#"+r.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),M.atto_image.dialogue.hide(),M.editor_atto.focus(n),s!==""&&(this.lastselectedimage?M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage)):M.editor_atto.set_selection(M.atto_image.selection),c=''+e.Escape.html(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='
"+''+"
",M.editor_atto.can_show_filepicker +(t,"image")&&(s+='"+"
"),s+='"+'"+''+"
",s+=''+'"+"
",s+='"+''+"
",s+='"+''+"
",s+='"+'
",s+='"+''+'
'+"
",s+='"+"
"+"
";var u=e.Node.create(s);return this._apply_image_properties(u,t),u.one("#"+r.INPUTURL).on("blur",M.atto_image.url_changed,this),u.one("#"+r.INPUTSUMBIT).on("click",M.atto_image.set_image,this,t),M.editor_atto.can_show_filepicker(t,"image")&&u.one("#"+r.IMAGEBROWSER).on("click",M.atto_image.open_filepicker),u},_apply_image_properties:function(e,t){var n=this._get_selected_image_properties(t),i=e.one("#"+r.IMAGEPREVIEW);if(n===!1){i.setStyle("display","none");return}n.align&&n.align.apply(i),n.display&&i.setStyle("display",n.display),n.width&&e.one("#"+r.INPUTWIDTH).set("value",n.width),n.height&&e.one("#"+r.INPUTHEIGHT).set("value",n.height),n.alt&&e.one("#"+r.INPUTALT).set("value",n.alt),n.src&&(e.one("#"+r.INPUTURL).set("value",n.src),i.setAttribute("src",n.src)),n.presentation&&e.one("#"+r.IMAGEPRESENTATION).set("checked","checked")}}},"@VERSION@",{requires:["node","escape"]}); diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js index 566b652527d..0dd4d4aee30 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js @@ -145,6 +145,7 @@ M.atto_image = M.atto_image || { dialogue: null, selection: null, currentlyselected : {}, + lastselectedimage : null, init: function(params) { var display_chooser = function(e, elementid) { e.preventDefault(); @@ -183,7 +184,7 @@ M.atto_image = M.atto_image || { // This includes cursor changes, we check if the button should be active or not, based // on the text selection. M.editor_atto.on('atto:selectionchanged', function(e) { - if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) { + if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) { M.editor_atto.add_widget_highlight(e.elementid, 'image'); M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes; } else { @@ -263,8 +264,13 @@ M.atto_image = M.atto_image || { M.atto_image.dialogue.hide(); + M.editor_atto.focus(elementid); if (url !== '') { - M.editor_atto.set_selection(M.atto_image.selection); + if (this.lastselectedimage) { + M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage)); + } else { + M.editor_atto.set_selection(M.atto_image.selection); + } imagehtml = '' + Y.Escape.html(alt) + ' 0) { + if (images) { + images = images.filter('img'); + } + + if (images && images.size()) { image = images.item(0); - for (i in images) { - if (images[i].test('img')) { - image = images[i]; - break; - } - } + this.lastselectedimage = image; + style = image.getAttribute('style'); width = parseInt(image.getAttribute('width'), 10); height = parseInt(image.getAttribute('height'), 10); diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js index 2b77cc803ce..f0b0e9fb8cd 100644 --- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js @@ -143,6 +143,7 @@ M.atto_image = M.atto_image || { dialogue: null, selection: null, currentlyselected : {}, + lastselectedimage : null, init: function(params) { var display_chooser = function(e, elementid) { e.preventDefault(); @@ -181,7 +182,7 @@ M.atto_image = M.atto_image || { // This includes cursor changes, we check if the button should be active or not, based // on the text selection. M.editor_atto.on('atto:selectionchanged', function(e) { - if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes)) { + if (M.editor_atto.selection_filter_matches(e.elementid, SELECTORS.TAGS, e.selectedNodes, false)) { M.editor_atto.add_widget_highlight(e.elementid, 'image'); M.editor_atto.currentlyselected[e.elementid] = e.selectedNodes; } else { @@ -261,8 +262,13 @@ M.atto_image = M.atto_image || { M.atto_image.dialogue.hide(); + M.editor_atto.focus(elementid); if (url !== '') { - M.editor_atto.set_selection(M.atto_image.selection); + if (this.lastselectedimage) { + M.editor_atto.set_selection(M.editor_atto.get_selection_from_node(this.lastselectedimage)); + } else { + M.editor_atto.set_selection(M.atto_image.selection); + } imagehtml = '' + Y.Escape.html(alt) + ' 0) { + if (images) { + images = images.filter('img'); + } + + if (images && images.size()) { image = images.item(0); - for (i in images) { - if (images[i].test('img')) { - image = images[i]; - break; - } - } + this.lastselectedimage = image; + style = image.getAttribute('style'); width = parseInt(image.getAttribute('width'), 10); height = parseInt(image.getAttribute('height'), 10); diff --git a/lib/editor/atto/thirdpartylibs.xml b/lib/editor/atto/thirdpartylibs.xml new file mode 100644 index 00000000000..06b6014f949 --- /dev/null +++ b/lib/editor/atto/thirdpartylibs.xml @@ -0,0 +1,10 @@ + + + + yui/src/rangy/js/*.* + Rangy + MIT + 1.2.3 + + + diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js index e74821b9625..0da12917fc3 100644 --- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js +++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js @@ -84,7 +84,7 @@ M.editor_atto = M.editor_atto || { 'tfoot', 'ul', 'video'], - + PLACEHOLDER_FONTNAME: 'yui-tmp', ALL_NODES_SELECTOR: '[style],font[face]', FONT_FAMILY: 'fontFamily', @@ -140,13 +140,6 @@ M.editor_atto = M.editor_atto || { */ lastselection : null, - /** - * The number of selection ranges when the selection was last recorded. - * @param lastselectioncount - * @type integer - */ - lastselectioncount : 0, - /** * Set to true after the first editor has been initialised. * @@ -569,22 +562,17 @@ M.editor_atto = M.editor_atto || { * @return bool */ is_active : function(elementid) { - var selection = M.editor_atto.get_selection(); + var range = rangy.createRange(), + editable = M.editor_atto.get_editable_node(elementid), + selection = rangy.getSelection(); - if (selection.length && selection.pop) { - selection = selection.pop(); + if (!selection.rangeCount) { + return false; } - var node = null; - if (selection.parentElement) { - node = Y.one(selection.parentElement()); - } else { - node = Y.one(selection.startContainer); - } + range.selectNode(editable.getDOMNode()); - var editable = M.editor_atto.get_editable_node(elementid); - - return node && editable.contains(node); + return range.intersectsRange(selection.getRangeAt(0)); }, /** @@ -886,23 +874,12 @@ M.editor_atto = M.editor_atto || { /** * Create a cross browser selection object that represents a yui node. * @param Node yui node for the selection - * @return range (browser dependent) + * @return rangy.Range[] */ get_selection_from_node: function(node) { - var range; - - if (window.getSelection) { - range = document.createRange(); - - range.setStartBefore(node.getDOMNode()); - range.setEndAfter(node.getDOMNode()); - return [range]; - } else if (document.selection) { - range = document.body.createTextRange(); - range.moveToElementText(node.getDOMNode()); - return range; - } - return false; + var range = rangy.createRange(); + range.selectNode(node.getDOMNode()); + return [range]; }, /** @@ -913,10 +890,7 @@ M.editor_atto = M.editor_atto || { save_selection : function(event) { var elementid = event.elementid; if (elementid && this.is_active(elementid)) { - var sel = this.get_selection(); - if (sel.length > 0) { - this.selections[elementid] = sel; - } + this.selections[elementid] = this.get_selection(); } }, @@ -936,23 +910,10 @@ M.editor_atto = M.editor_atto || { /** * Get the selection object that can be passed back to set_selection. - * @return Range[]|TextRange[] All browsers except IE8 and less will get Range[], IE8 and less will get TextRange[] + * @return rangy.Range[] */ get_selection : function() { - if (window.getSelection) { - var sel = window.getSelection(); - var ranges = [], i = 0; - for (i = 0; i < sel.rangeCount; i++) { - ranges.push(sel.getRangeAt(i)); - } - return ranges; - } else if (document.selection) { - // IE < 9 - if (document.selection.createRange) { - return [document.selection.createRange()]; - } - } - return false; + return rangy.getSelection().getAllRanges(); }, /** @@ -961,18 +922,7 @@ M.editor_atto = M.editor_atto || { * @return boolean */ selection_contains_node : function(node) { - // TODO: This will break in IE11 . IE11 doesn't provide containsNode and document.selection has been removed. - var range, sel; - if (window.getSelection) { - sel = window.getSelection(); - if (sel.containsNode) { - return sel.containsNode(node.getDOMNode(), true); - } - } - sel = document.selection.createRange(); - range = sel.duplicate(); - range.moveToElementText(node.getDOMNode()); - return sel.inRange(range); + return rangy.getSelection().containsNode(node.getDOMNode(), true); }, /** @@ -982,124 +932,72 @@ M.editor_atto = M.editor_atto || { * @param {String} elementid * @param {String} selector * @param {NodeList} selectednodes (Optional) - for performance we can pass this instead of looking it up. + * @param {Boolean} requireall (Optional) - used to specify that any "any" match is good enough. Defaults to true. * @return {Boolean} */ - selection_filter_matches : function(elementid, selector, selectednodes) { + selection_filter_matches : function(elementid, selector, selectednodes, requireall) { + if ((typeof requireall) === 'undefined') { + requireall = true; + } if (!selectednodes) { // Find this because it was not passed as a param. selectednodes = M.editor_atto.get_selected_nodes(elementid); } - var allmatch = selectednodes.size() > 0; + var allmatch = selectednodes.size() > 0, anymatch = false; selectednodes.each(function(node){ // Check each node, if it doesn't match the tags AND is not within the specified tags then fail this thing. - if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) { - allmatch = false; + if (requireall) { + // Check for at least one failure. + if (!allmatch || !node.ancestor(selector, true, '#' + elementid + "editable")) { + allmatch = false; + } + } else { + // Check for at least one match. + if (!anymatch && node.ancestor(selector, true, '#' + elementid + "editable")) { + anymatch = true; + } } }, this); - return allmatch; + if (requireall) { + return allmatch; + } else { + return anymatch; + } }, /** * Get the deepest possible list of nodes in the current selection. * - * Presently this method contains logic to work out what nodes are "selected" by the user. - * The user selection may take the form of: - * - text : selected text within a single node, such as double clicking a word. - * - A node : the user has clicked an image or media object that is selectable by clicking upon an element. - * - text + nodes: the user has dragged or used shift + arrows to select a body of content that may include - * both text and/or nodes. - * To work out the selected nodes we look at the start and end node. If they are the same then there is only one node - * containing selected content. - * If they don't match we get the common ancestor of the start and end nodes, and then iterate all child nodes recording - * all nodes that appear after the start node and before the end node. - * - * YUI deal with this in a totally different way to us, they have a method getSelected that gets the selected nodes by - * exec'ing a command to add fontname, and then find all nodes that have been affected. They remove the fontname property - * before returning the nodes. - * * @param {String} elementid * @return Y.NodeList */ get_selected_nodes : function(elementid) { - var nodes = [], - results = new Y.NodeList(), - points = [], + var results = new Y.NodeList(), + nodes, + selection, range, - i, - sel, - start, - end, - parent, - values; + node, + i; - // The window.getSelection method is available from IE9 onwards and for all other browsers. - if (window.getSelection) { - sel = window.getSelection(); - for (i = 0; i < sel.rangeCount; i++) { - range = sel.getRangeAt(i); - start = new Y.Node(range.startContainer); - end = new Y.Node(range.endContainer); - if (start.compareTo(end)) { - // The start and end node are the same node. Only one node is selected. Easy! - nodes.push(end); - } else { - // The start and end node are not the same, we have 2..n nodes selected, we'll need to work them out. - // For the time being record them as a pair of points along with the common ancestor. - // First up we need to check the order of the nodes, we need them in the order they appear in the DOM - // however the selection may have been rtl or ltr and in the case of rtl we need to switch the order. - values = this._fix_selected_nodes_from_range( - start, - range.startOffset, - end, - range.endOffset - ); - // Ok start and end are in order. Yay! - start = values[0]; - end = values[2]; - // We calculate the common ancestor here as we should be able to get it from the range object. - if (range.commonAncestorContainer) { - parent = range.commonAncestorContainer; - } else if (range.parentElement) { - parent = range.parentElement(); - } else { - parent = this._get_common_parent_from_nodes(start, end); - } - points.push([ - start, - end, - parent - ]); - } - } - } else if (document.selection && document.selection.createRange) { - // The document.selection.createRange is available in IE only, versions 5.5 through 10 (not IE11). - // However s window.getSelection exists in IE9+ this code will only execute in IE 5.5 -> IE 8. - // http://msdn.microsoft.com/en-us/library/ie/ms535869(v=vs.85).aspx - range = document.selection.createRange(); - // Create range will return either TextRange object from the current text selection, or a controlRange - // collection from the control selection. - if (document.selection.type === "Control") { - // Its a control collection, well this is easy, its an array of Nodes. All the work has been done for us. - for (i = 0; i < range.length; i++) { - nodes.push(new Y.Node(range(i))); - } - } else { - // Its a TextRange, in which case its a text element (nodeType 3) and its parent will be the node containing the - // selection. - nodes.push(new Y.Node(range.parentElement())); - } + selection = rangy.getSelection(); + + if (selection.rangeCount) { + range = selection.getRangeAt(0); + } else { + // Empty range. + range = rangy.createRange(); } - // Ok we have two array as this point. - // - Nodes is an array containing nodes that are currently highlighted by selection. - // - Points is an array containing start, end, and parent nodes. - // For each item in the points array we need to find all nodes containing selected content. - for (i in points) { - nodes = nodes.concat(this._get_all_nodes_between.apply(this, points[i])); + + if (range.collapsed) { + range.selectNode(range.commonAncestorContainer); } - // Finally for each node we've determined is highlighted we need to check it belongs to the current editable area. - for (i in nodes) { - if (results.indexOf(nodes[i]) === -1 && nodes[i].ancestor('#' + elementid + "editable")) { - results.push(nodes[i]); + + nodes = range.getNodes(); + + for (i = 0; i < nodes.length; i++) { + node = Y.one(nodes[i]); + if (node.ancestor('#' + elementid + "editable")) { + results.push(node); } } return results; @@ -1116,47 +1014,25 @@ M.editor_atto = M.editor_atto || { * @returns {Boolean} */ _has_selection_changed : function(e) { - var sel, - i, + var selection = rangy.getSelection(), range, - bits = [], - selectedstr = '', - selectioncount = 0; - if (window.getSelection) { - // Hoorah - working with the alternative is horrid.. - sel = window.getSelection(); - selectioncount = sel.rangeCount; - for (i = 0; i < selectioncount; i++) { - range = sel.getRangeAt(i); - bits.push(range.startOffset); - bits.push(range.endOffset); - bits.push(this._get_node_identifier(range.startContainer)); - bits.push(this._get_node_identifier(range.endContainer)); - bits.push('&'); - } - } else if (document.selection && document.selection.createRange) { - range = document.selection.createRange(); - if (range !== null) { - if (document.selection.type === "Control") { - // Is a controlRange: http://msdn.microsoft.com/en-us/library/ie/hh826021(v=vs.85).aspx - selectioncount = range.length; - for (i = 0; i < selectioncount; i++) { - bits.push(this._get_node_identifier(range(i))); - } - } else { - // Is a TextRange: http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx - selectioncount = 1; - bits.push(this._get_node_identifier(range.parentElement())); - } + changed = false; + + if (selection.rangeCount) { + range = selection.getRangeAt(0); + } else { + // Empty range. + range = rangy.createRange(); + } + + if (this.lastselection) { + if (!this.lastselection.equals(range)) { + changed = true; + return this._fire_selectionchanged(e); } } - selectedstr = bits.join('|'); - if (this.lastselection !== selectedstr) { - this.lastselection = selectedstr; - this.lastselectioncount = selectioncount; - return this._fire_selectionchanged(e); - } - return false; + this.lastselection = range; + return changed; }, /** @@ -1182,230 +1058,25 @@ M.editor_atto = M.editor_atto || { }); }, - /** - * Returns the best identifier for the given node that can be found. May not be unique. - * - * @method _get_node_identifier - * @private - * @param {Node} node - * @returns {string} - */ - _get_node_identifier : function(node) { - var uid = (node.nodeType === 3) ? node.length : node.id; - return uid || node.className || node.nodeName; - }, - - /** - * Fixes the selection range, normalising it, ordering the start and end by dom position, and correcting for noteType and offset - * - * Note about offsets: - * If the container is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the - * start of the container to the boundary point of the Range. - * For other Node types, the offset is the number of child nodes between the start of the container and the boundary - * point of the Range. - * - * @method _fix_selected_nodes_from_range - * @private - * @param {Node} a - * @param {integer} aoffset - * @param {Node} b - * @param {integer} boffset - * @returns {[Node, integer, Node, integer]} - */ - _fix_selected_nodes_from_range : function(a, aoffset, b, boffset) { - var first, - firstoffset, - second, - secondoffset, - searchfunc, - acheckend = true, - bcheckend = true, - checkend = true; - - if (a.get('nodeType') === 1) { - a = a.get('childNodes').item(aoffset); - acheckend = false; - } - if (b.get('nodeType') === 1 && boffset > 0) { - b = b.get('childNodes').item(boffset - 1); - bcheckend = false; - } - if (a._yuid === b._yuid) { - if (aoffset < boffset) { - first = a; - firstoffset = aoffset; - second = b; - secondoffset = boffset; - checkend = bcheckend; - } else { - first = b; - firstoffset = boffset; - second = a; - secondoffset = aoffset; - checkend = acheckend; - } - } else { - if (a.contains(b)) { - first = a; - firstoffset = aoffset; - second = b; - secondoffset = boffset; - checkend = bcheckend; - } else if (b.contains(a)) { - first = b; - firstoffset = boffset; - second = a; - secondoffset = aoffset; - checkend = acheckend; - } else { - searchfunc = function find_first(nodes, x, y) { - var i, result; - for (i in nodes) { - if (nodes[i] === x.getDOMNode()) { - return x; - } else if (nodes[i] === y.getDOMNode()) { - return y; - } else if (nodes[i].nodeType === 1) { - result = find_first(nodes[i].childNodes, x, y); - if (result !== false) { - return result; - } - } - } - return false; - }; - first = searchfunc(this._get_common_parent_from_nodes(a, b).get('childNodes'), a, b) || a; - if (first === a) { - firstoffset = aoffset; - second = b; - secondoffset = boffset; - checkend = bcheckend; - } else { - firstoffset = boffset; - second = a; - secondoffset = aoffset; - checkend = acheckend; - } - } - if (checkend && second.get('nodeType') === 3 && secondoffset === 0 && second.previous('*', true)) { - second = second.previous(); - } - } - return [first, firstoffset, second, secondoffset]; - }, - - /** - * Returns an array containing all of the nodes in the Dom between the two reference nodes. - * - * @method _get_all_nodes_between - * @private - * @param {Node} a - * @param {Node} b - * @param {Node} parent The common parent of a and b - * @returns {Node[]} - */ - _get_all_nodes_between : function(a, b, parent) { - var startfound = false, - endfound = false, - nodes = [], - parenthasselectedtext = false; - if (a === b || a.contains(b)) { - return [a]; - } - if (b.contains(a)) { - return [b]; - } - if (!parent) { - return [a, b]; - } - - nodes.push(a); - parent.get('childNodes').each(function(node) { - if (endfound) { - return; - } - if (!startfound && node === a) { - startfound = true; - } else if (startfound && !endfound) { - if (node === b) { - endfound = true; - } else { - if (node.get('nodeType') === 3) { - parenthasselectedtext = true; - } else { - nodes.push(node); - nodes = nodes.compact(node.all('*')); - } - } - } - }); - nodes.push(b); - - if (parenthasselectedtext) { - nodes.unshift(parent); - } - - return nodes; - }, - - /** - * Returns the common parent of the two given nodes. - * - * @method _get_common_parent_from_nodes - * @private - * @param {Node} a - * @param {Node} b - * @returns {Node} - */ - _get_common_parent_from_nodes : function(a, b) { - var aparents = a.ancestors('*', false, '.' + CSS.CONTENT), - bparents = b.ancestors('*', false, '.' + CSS.CONTENT), - node; - while ((node = aparents.pop())) { - if (bparents.indexOf(node) !== -1) { - return node; - } - } - return a.ancestor('.' + CSS.CONTENT); - }, - /** * Get the dom node representing the common anscestor of the selection nodes. * @return DOMNode or false */ get_selection_parent_node : function() { - var selection = M.editor_atto.get_selection(); - if (selection.length) { - selection = selection.pop(); + var selection = rangy.getSelection(); + if (selection.rangeCount) { + return selection.getRangeAt(0).commonAncestorContainer; } - - if (selection.commonAncestorContainer) { - return selection.commonAncestorContainer; - } else if (selection.parentElement) { - return selection.parentElement(); - } - // No selection return false; }, /** * Set the current selection. Used to restore a selection. + * @param rangy.Range[] ranges - List of ranges in selection. */ - set_selection : function(selection) { - var sel, i; - - if (window.getSelection) { - sel = window.getSelection(); - sel.removeAllRanges(); - for (i = 0; i < selection.length; i++) { - sel.addRange(selection[i]); - } - } else if (document.selection) { - // IE < 9 - if (selection.select) { - selection.select(); - } - } + set_selection : function(ranges) { + var selection = rangy.getSelection(); + selection.setRanges(ranges); }, /** @@ -1552,19 +1223,20 @@ M.editor_atto = M.editor_atto || { /** * Inserts the given HTML into the editable content at the currently focused point. - * @param {String} html + * @param String html */ insert_html_at_focus_point: function(html) { // We check document.selection here as the insertHTML exec command wan't available in IE until version 11. // In IE document.selection was removed at from version 11. Making it an easy affordable check. - if (document.selection && document.selection.createRange) { - var range = document.selection.createRange(); - if (range.pasteHTML) { - range.pasteHTML(html); - } - } else { - // All other browsers are great! - document.execCommand('insertHTML', false, html); + var selection = rangy.getSelection(), + range, + node = Y.Node.create(html); + if (selection.rangeCount) { + range = selection.getRangeAt(0); + } + if (range) { + range.deleteContents(); + range.insertNode(node.getDOMNode()); } }, @@ -1775,6 +1447,7 @@ Y.NodeList.importMethod(Y.Node.prototype, "cleanHTML"); "event-simulate", "event-custom", "yui-throttle", - "moodle-core-notification-dialogue" + "moodle-core-notification-dialogue", + "moodle-editor_atto-rangy" ] }); diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js index 9d345e74c13..669e8d0afb6 100644 --- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js +++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js @@ -1,4 +1,3 @@ -YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n'),l.append(c)),v=M.util.image_url("t/expanded","moodle"),p=e.Node.create('"),c.append(p),h=l.getAttribute("aria-activedescendant"),h||(p.setAttribute("tabindex","0"),l.setAttribute("aria-activedescendant",p.generateID())),M.editor_atto.widgets[d]=d;var m=e.Node.create('
'),g=0,y={};for(g=0;g'+y.text+""+"")),M.editor_atto.buttonhandlers[n+"_action_"+g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",closeButton:!1,center:!1,render:!0});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.headerNode.hide()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create('
'),a.append(f)),l=e.Node.create('"),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&n.pop&&(n=n.pop());var r=null;n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer);var i=M.editor_atto.get_editable_node(t);return r&&i.contains(r)},focus:function(e){M.editor_atto.get_editable_node(e).focus()},update_from_textarea:function(t){var n=M.editor_atto.get_editable_node(t),r=M.editor_atto.get_textarea_node(t);n.setHTML(""),n.append(r.get("value")),n.cleanHTML(),n.getHTML()===""&&(e.UA.ie&&e.UA.ie<10?n.setHTML("

"):n.setHTML("


"))},init:function(t){var n=this.firstinit;this.firstinit=!1;var r=e.Node.create('
'),i=e.Node.create('
'),s=e.Node.create('")),M.editor_atto.buttonhandlers[n+"_action_"+g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",closeButton:!1,center:!1,render:!0});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.headerNode.hide()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create('
'),a.append(f)),l=e.Node.create('"),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(e){var t=rangy.createRange(),n=M.editor_atto.get_editable_node(e),r=rangy.getSelection();return r.rangeCount?(t.selectNode(n.getDOMNode()),t.intersectsRange(r.getRangeAt(0))):!1},focus:function(e){M.editor_atto.get_editable_node(e).focus()},update_from_textarea:function(t){var n=M.editor_atto.get_editable_node(t),r=M.editor_atto.get_textarea_node(t);n.setHTML(""),n.append(r.get("value")),n.cleanHTML(),n.getHTML()===""&&(e.UA.ie&&e.UA.ie<10?n.setHTML("

"):n.setHTML("


"))},init:function(t){var n=this.firstinit;this.firstinit=!1;var r=e.Node.create('
'),i=e.Node.create('
'),s=e.Node.create('