diff --git a/lib/editor/atto/plugins/image/lang/en/atto_image.php b/lib/editor/atto/plugins/image/lang/en/atto_image.php index 63b58df908688..1c68f5227d443 100644 --- a/lib/editor/atto/plugins/image/lang/en/atto_image.php +++ b/lib/editor/atto/plugins/image/lang/en/atto_image.php @@ -42,4 +42,5 @@ $string['preview'] = 'Preview'; $string['saveimage'] = 'Save image'; $string['size'] = 'Size'; -$string['width'] = 'Width'; +$string['uploading'] = 'Uploading, please wait...'; +$string['width'] = 'Width'; \ No newline at end of file diff --git a/lib/editor/atto/plugins/image/lib.php b/lib/editor/atto/plugins/image/lib.php index 32b8745241896..00b3915b2b246 100644 --- a/lib/editor/atto/plugins/image/lib.php +++ b/lib/editor/atto/plugins/image/lib.php @@ -49,6 +49,7 @@ function atto_image_strings_for_js() { 'presentationoraltrequired', 'size', 'width', + 'uploading', ); $PAGE->requires->strings_for_js($strings, 'atto_image'); diff --git a/lib/editor/atto/plugins/image/version.php b/lib/editor/atto/plugins/image/version.php index 7aca6066de550..e0a95e9df7ca3 100644 --- a/lib/editor/atto/plugins/image/version.php +++ b/lib/editor/atto/plugins/image/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2014111000; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2014112800; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2014110400; // Requires this Moodle version. $plugin->component = 'atto_image'; // Full name of the plugin (used for diagnostics). diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js index 48a917009c63a..511c6a8b7b7e9 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 @@ -165,6 +165,7 @@ var CSS = { '{{#if presentation}}role="presentation" {{/if}}' + 'style="{{alignment}}{{margin}}{{customstyle}}"' + '{{#if classlist}}class="{{classlist}}" {{/if}}' + + '{{#if id}}id="{{id}}" {{/if}}' + '/>'; Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { @@ -206,6 +207,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi _rawImageDimensions: null, initializer: function() { + this.addButton({ icon: 'e/insert_edit_image', callback: this._displayDialogue, @@ -213,6 +215,121 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi tagMatchRequiresAll: false }); this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this); + this.editor.on('drop', this._handleDragDrop, this); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @private + */ + _handleDragDrop: function(e) { + + var self = this, + host = this.get('host'), + template = Y.Handlebars.compile(IMAGETEMPLATE); + + host.saveSelection(); + e = e._event; + + // Only handle the event if an image file was dropped in. + if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length && /^image\//.test(e.dataTransfer.files[0].type)) { + + var options = host.get('filepickeroptions').image, + savepath = (options.savepath === undefined) ? '/' : options.savepath, + formData = new FormData(), + timestamp = 0, + uploadid = "", + xhr = new XMLHttpRequest(), + imagehtml = "", + keys = Object.keys(options.repositories); + + e.preventDefault(); + e.stopPropagation(); + formData.append('repo_upload_file', e.dataTransfer.files[0]); + formData.append('itemid', options.itemid); + + // List of repositories is an object rather than an array. This makes iteration more awkward. + for (var i = 0; i < keys.length; i++) { + if (options.repositories[keys[i]].type === 'upload') { + formData.append('repo_id', options.repositories[keys[i]].id); + break; + } + } + formData.append('env', options.env); + formData.append('sesskey', M.cfg.sesskey); + formData.append('client_id', options.client_id); + formData.append('savepath', savepath); + formData.append('ctx_id', options.context.id); + + // Insert spinner as a placeholder. + timestamp = new Date().getTime(); + uploadid = 'moodleimage_' + Math.round(Math.random() * 100000) + '-' + timestamp; + host.focus(); + host.restoreSelection(); + imagehtml = template({ + url: M.util.image_url("i/loading_small", 'moodle'), + alt: M.util.get_string('uploading', COMPONENTNAME), + id: uploadid + }); + host.insertContentAtFocusPoint(imagehtml); + self.markUpdated(); + + // Kick off a XMLHttpRequest. + xhr.onreadystatechange = function() { + var placeholder = self.editor.one('#' + uploadid), + result, + file, + newhtml, + newimage; + + if (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { + if (placeholder) { + placeholder.remove(true); + } + return new M.core.ajaxException(result); + } + + file = result; + if (result.event && result.event === 'fileexists') { + // A file with this name is already in use here - rename to avoid conflict. + // Chances are, it's a different image (stored in a different folder on the user's computer). + // If the user wants to reuse an existing image, they can copy/paste it within the editor. + file = result.newfile; + } + + // Replace placeholder with actual image. + newhtml = template({ + url: file.url, + presentation: true + }); + newimage = Y.Node.create(newhtml); + if (placeholder) { + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); + } + self.markUpdated(); + } + } else { + alert(M.util.get_string('servererror', 'moodle')); + if (placeholder) { + placeholder.remove(true); + } + } + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + } + return false; + }, /** 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 d346265ff8a82..8cf00acb8de7d 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,3 @@ -YUI.add("moodle-atto_image-button",function(e,t){var n={RESPONSIVE:"img-responsive",INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUBMIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTSIZE:"atto_image_size",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",INPUTCONSTRAIN:"atto_image_constrain",INPUTCUSTOMSTYLE:"atto_image_customstyle",IMAGEPREVIEW:"atto_image_preview",IMAGEPREVIEWBOX:"atto_image_preview_box"},r={INPUTURL:"."+n.INPUTURL},i=[{name:"text-top",str:"alignment_top",value:"vertical-align",margin:"0 .5em"},{name:"middle",str:"alignment_middle",value:"vertical-align",margin:"0 .5em"},{name:"text-bottom",str:"alignment_bottom",value:"vertical-align",margin:"0 .5em",isDefault:!0},{name:"left",str:"alignment_left",value:"float",margin:"0 .5em 0 0"},{name:"right",str:"alignment_right",value:"float",margin:"0 0 0 .5em"},{name:"customstyle",str:"customstyle",value:"style"}],s={ISPERCENT:/\d+%/},o="atto_image",u='

{{#if showFilepicker}}{{/if}}

x

',a='{{alt}}';e.namespace("M.atto_image").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_selectedImage:null,_form:null,_rawImageDimensions:null,initializer:function(){this.addButton({icon:"e/insert_edit_image",callback:this._displayDialogue,tags:"img",tagMatchRequiresAll:!1}),this.editor.delegate("dblclick",this._handleDoubleClick,"img",this)},_handleDoubleClick:function(e){var t=e.target,n=this.get("host").getSelectionFromNode(t);this.get("host").setSelection(n),this._displayDialogue()},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;this._rawImageDimensions=null;var e=this.getDialogue({headerContent:M.util.get_string("imageproperties",o),width:"480px",focusAfterHide:!0,focusOnShowSelector:r.INPUTURL});e.set("bodyContent",this._getDialogueContent()).show()},_loadPreviewImage:function(e){var t=new Image,r=this;t.onerror=function(){var e=r._form.one("."+n.IMAGEPREVIEW);e.setStyles({display:"none"}),r.getDialogue().centerDialogue()},t.onload=function(){var e,t,i,o,u;r._rawImageDimensions={width:this.width,height:this.height},e=r._form.one("."+n.INPUTWIDTH),t=e.get("value"),t===""&&(e.set("value",this.width),t=""+this.width),e=r._form.one("."+n.INPUTHEIGHT),i=e.get("value"),i===""&&(e.set("value",this.height),i=""+this.height),e=r._form.one("."+n.IMAGEPREVIEW),e.setAttribute("src",this.src),e.setStyles({display:"inline"}),e=r._form.one("."+n.INPUTCONSTRAIN),t.match(s.ISPERCENT)&&i.match(s.ISPERCENT)?e.set("checked",t===i):(this.width===0&&(this.width=1),this.height===0&&(this.height=1),o=Math.round(1e3*parseInt(t,10)/this.width),u=Math.round(1e3*parseInt(i,10)/this.height),e.set("checked",o===u)),r._autoAdjustSize(r),r.getDialogue().centerDialogue()},t.src=e},_getDialogueContent:function(){var t=e.Handlebars.compile(u),r=this.get("host").canShowFilepicker("image"),s=e.Node.create(t({elementid:this.get("host").get("elementid"),CSS:n,component:o,showFilepicker:r,alignments:i}));return this._form=s,this._applyImageProperties(this._form),this._form.one("."+n.INPUTURL).on("blur",this._urlChanged,this),this._form.one("."+n.IMAGEPRESENTATION).on("change",this._updateWarning,this),this._form.one("."+n.INPUTALT).on("change",this._updateWarning,this),this._form.one("."+n.INPUTWIDTH -).on("blur",this._autoAdjustSize,this),this._form.one("."+n.INPUTHEIGHT).on("blur",this._autoAdjustSize,this,!0),this._form.one("."+n.INPUTCONSTRAIN).on("change",function(e){e.target.get("checked")&&this._autoAdjustSize(e)},this),this._form.one("."+n.INPUTURL).on("blur",this._urlChanged,this),this._form.one("."+n.INPUTSUBMIT).on("click",this._setImage,this),r&&this._form.one("."+n.IMAGEBROWSER).on("click",function(){this.get("host").showFilepicker("image",this._filepickerCallback,this)},this),s},_autoAdjustSize:function(e,t){t=t||!1;var r=this._form.one("."+n.INPUTWIDTH),i="width",o=this._form.one("."+n.INPUTHEIGHT),u="height",a=this._form.one("."+n.INPUTCONSTRAIN),f=r.get("value"),l=o.get("value"),c=this._form.one("."+n.IMAGEPREVIEW),h,p;if(!this._rawImageDimensions)return;f===""&&(f=this._rawImageDimensions[i],r.set("value",f),f=r.get("value")),c.setStyles({width:null,height:null});if(!a.get("checked"))f.match(s.ISPERCENT)?(h=parseInt(f,10),p=this._rawImageDimensions.width/100*h,c.setStyle("width",p+"px")):c.setStyle("width",f+"px"),l.match(s.ISPERCENT)?(h=parseInt(l,10),p=this._rawImageDimensions.height/100*h,c.setStyle("height",p+"px")):c.setStyle("height",l+"px");else{if(t){var d;d=r,r=o,o=d,d=i,i=u,u=d,d=f,f=l,l=d}f.match(s.ISPERCENT)?(l=f,h=parseInt(f,10),p=this._rawImageDimensions.width/100*h,c.setStyle("width",p),p=this._rawImageDimensions.height/100*h,c.setStyle("height",p)):(l=Math.round(f/this._rawImageDimensions[i]*this._rawImageDimensions[u]),t?c.setStyles({width:l,height:f}):c.setStyles({width:f,height:l})),o.set("value",l)}},_filepickerCallback:function(e){if(e.url!==""){var t=this._form.one("."+n.INPUTURL);t.set("value",e.url),this._form.one("."+n.INPUTWIDTH).set("value",""),this._form.one("."+n.INPUTHEIGHT).set("value",""),this._loadPreviewImage(e.url)}},_applyImageProperties:function(e){var t=this._getSelectedImageProperties(),r=e.one("."+n.IMAGEPREVIEW),s;if(t===!1){r.setStyle("display","none");for(s in i)i[s].isDefault===!0&&(css=i[s].value+":"+i[s].name+";",e.one("."+n.INPUTALIGNMENT).set("value",css));e.one("."+n.INPUTALIGNMENT).getDOMNode().options.remove(i.length-1);return}t.align?(e.one("."+n.INPUTALIGNMENT).set("value",t.align),e.one("."+n.INPUTALIGNMENT).getDOMNode().options.remove(i.length-1)):e.one("."+n.INPUTALIGNMENT).set("value","style:customstyle;"),t.customstyle&&e.one("."+n.INPUTCUSTOMSTYLE).set("value",t.customstyle),t.width&&e.one("."+n.INPUTWIDTH).set("value",t.width),t.height&&e.one("."+n.INPUTHEIGHT).set("value",t.height),t.alt&&e.one("."+n.INPUTALT).set("value",t.alt),t.src&&(e.one("."+n.INPUTURL).set("value",t.src),this._loadPreviewImage(t.src)),t.presentation&&e.one("."+n.IMAGEPRESENTATION).set("checked","checked"),this._autoAdjustSize()},_getSelectedImageProperties:function(){var e={src:null,alt:null,width:null,height:null,align:"",presentation:!1},t=this.get("host").getSelectedNodes(),n,r,o,u,a;t&&(t=t.filter("img"));if(t&&t.size()){image=t.item(0),this._selectedImage=image,u=image.getAttribute("style"),e.customstyle=u,u=u.replace(/ /g,""),r=image.getAttribute("width"),r.match(s.ISPERCENT)||(r=parseInt(r,10)),o=image.getAttribute("height"),o.match(s.ISPERCENT)||(o=parseInt(o,10)),r!==0&&(e.width=r),o!==0&&(e.height=o);for(n in i){a=i[n].value+":"+i[n].name+";";if(u.indexOf(a)!==-1){margin="margin:"+i[n].margin+";",margin=margin.replace(/ /g,"");if(u.indexOf(margin)!==-1){e.align=a;break}}}return e.src=image.getAttribute("src"),e.alt=image.getAttribute("alt")||"",e.presentation=image.get("role")==="presentation",e}return this._selectedImage=null,!1},_urlChanged:function(){var e=this._form.one("."+n.INPUTURL);e.get("value")!==""&&this._loadPreviewImage(e.get("value"))},_setImage:function(t){var r=this._form,o=r.one("."+n.INPUTURL).get("value"),u=r.one("."+n.INPUTALT).get("value"),f=r.one("."+n.INPUTWIDTH).get("value"),l=r.one("."+n.INPUTHEIGHT).get("value"),c=r.one("."+n.INPUTALIGNMENT).get("value"),h="",p=r.one("."+n.IMAGEPRESENTATION).get("checked"),d=r.one("."+n.INPUTCONSTRAIN).get("checked"),v,m="",g,y=[],b=this.get("host");t.preventDefault();if(this._updateWarning())return;b.focus();if(o!==""){this._selectedImage?b.setSelection(b.getSelectionFromNode(this._selectedImage)):b.setSelection(this._currentSelection);if(c==="style:customstyle;")c="",m=r.one("."+n.INPUTCUSTOMSTYLE).get("value");else for(g in i)css=i[g].value+":"+i[g].name+";",c===css&&(h=" margin: "+i[g].margin+";");d&&y.push(n.RESPONSIVE);if(!f.match(s.ISPERCENT)&&isNaN(parseInt(f,10))){r.one("."+n.INPUTWIDTH).focus();return}if(!l.match(s.ISPERCENT)&&isNaN(parseInt(l,10))){r.one("."+n.INPUTHEIGHT).focus();return}template=e.Handlebars.compile(a),v=template({url:o,alt:u,width:f,height:l,presentation:p,alignment:c,margin:h,customstyle:m,classlist:y.join(" ")}),this.get("host").insertContentAtFocusPoint(v),this.markUpdated()}this.getDialogue({focusAfterHide:null}).hide()},_updateWarning:function(){var e=this._form,t=!0,r=e.one("."+n.INPUTALT).get("value"),i=e.one("."+n.IMAGEPRESENTATION).get("checked");return r===""&&!i?(e.one("."+n.IMAGEALTWARNING).setStyle("display","block"),e.one("."+n.INPUTALT).setAttribute("aria-invalid",!0),e.one("."+n.IMAGEPRESENTATION).setAttribute("aria-invalid",!0),t=!0):(e.one("."+n.IMAGEALTWARNING).setStyle("display","none"),e.one("."+n.INPUTALT).setAttribute("aria-invalid",!1),e.one("."+n.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),t=!1),this.getDialogue().centerDialogue(),t}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]}); +YUI.add("moodle-atto_image-button",function(e,t){var n={RESPONSIVE:"img-responsive",INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUBMIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTSIZE:"atto_image_size",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",INPUTCONSTRAIN:"atto_image_constrain",INPUTCUSTOMSTYLE:"atto_image_customstyle",IMAGEPREVIEW:"atto_image_preview",IMAGEPREVIEWBOX:"atto_image_preview_box"},r={INPUTURL:"."+n.INPUTURL},i=[{name:"text-top",str:"alignment_top",value:"vertical-align",margin:"0 .5em"},{name:"middle",str:"alignment_middle",value:"vertical-align",margin:"0 .5em"},{name:"text-bottom",str:"alignment_bottom",value:"vertical-align",margin:"0 .5em",isDefault:!0},{name:"left",str:"alignment_left",value:"float",margin:"0 .5em 0 0"},{name:"right",str:"alignment_right",value:"float",margin:"0 0 0 .5em"},{name:"customstyle",str:"customstyle",value:"style"}],s={ISPERCENT:/\d+%/},o="atto_image",u='

{{#if showFilepicker}}{{/if}}

x

',a='{{alt}}';e.namespace("M.atto_image").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_selectedImage:null,_form:null,_rawImageDimensions:null,initializer:function(){this.addButton({icon:"e/insert_edit_image",callback:this._displayDialogue,tags:"img",tagMatchRequiresAll:!1}),this.editor.delegate("dblclick",this._handleDoubleClick,"img",this),this.editor.on("drop",this._handleDragDrop,this)},_handleDragDrop:function(t){var n=this,r=this.get("host"),i=e.Handlebars.compile(a);r.saveSelection(),t=t._event;if(t.dataTransfer&&t.dataTransfer.files&&t.dataTransfer.files.length&&/^image\//.test(t.dataTransfer.files[0].type)){var s=r.get("filepickeroptions").image,u=s.savepath===undefined?"/":s.savepath,f=new FormData,l=0,c="",h=new XMLHttpRequest,p="",d=Object.keys(s.repositories);t.preventDefault(),t.stopPropagation(),f.append("repo_upload_file",t.dataTransfer.files[0]),f.append("itemid",s.itemid);for(var v=0;v'; Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { @@ -206,6 +207,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi _rawImageDimensions: null, initializer: function() { + this.addButton({ icon: 'e/insert_edit_image', callback: this._displayDialogue, @@ -213,6 +215,121 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi tagMatchRequiresAll: false }); this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this); + this.editor.on('drop', this._handleDragDrop, this); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @private + */ + _handleDragDrop: function(e) { + + var self = this, + host = this.get('host'), + template = Y.Handlebars.compile(IMAGETEMPLATE); + + host.saveSelection(); + e = e._event; + + // Only handle the event if an image file was dropped in. + if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length && /^image\//.test(e.dataTransfer.files[0].type)) { + + var options = host.get('filepickeroptions').image, + savepath = (options.savepath === undefined) ? '/' : options.savepath, + formData = new FormData(), + timestamp = 0, + uploadid = "", + xhr = new XMLHttpRequest(), + imagehtml = "", + keys = Object.keys(options.repositories); + + e.preventDefault(); + e.stopPropagation(); + formData.append('repo_upload_file', e.dataTransfer.files[0]); + formData.append('itemid', options.itemid); + + // List of repositories is an object rather than an array. This makes iteration more awkward. + for (var i = 0; i < keys.length; i++) { + if (options.repositories[keys[i]].type === 'upload') { + formData.append('repo_id', options.repositories[keys[i]].id); + break; + } + } + formData.append('env', options.env); + formData.append('sesskey', M.cfg.sesskey); + formData.append('client_id', options.client_id); + formData.append('savepath', savepath); + formData.append('ctx_id', options.context.id); + + // Insert spinner as a placeholder. + timestamp = new Date().getTime(); + uploadid = 'moodleimage_' + Math.round(Math.random() * 100000) + '-' + timestamp; + host.focus(); + host.restoreSelection(); + imagehtml = template({ + url: M.util.image_url("i/loading_small", 'moodle'), + alt: M.util.get_string('uploading', COMPONENTNAME), + id: uploadid + }); + host.insertContentAtFocusPoint(imagehtml); + self.markUpdated(); + + // Kick off a XMLHttpRequest. + xhr.onreadystatechange = function() { + var placeholder = self.editor.one('#' + uploadid), + result, + file, + newhtml, + newimage; + + if (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { + if (placeholder) { + placeholder.remove(true); + } + return new M.core.ajaxException(result); + } + + file = result; + if (result.event && result.event === 'fileexists') { + // A file with this name is already in use here - rename to avoid conflict. + // Chances are, it's a different image (stored in a different folder on the user's computer). + // If the user wants to reuse an existing image, they can copy/paste it within the editor. + file = result.newfile; + } + + // Replace placeholder with actual image. + newhtml = template({ + url: file.url, + presentation: true + }); + newimage = Y.Node.create(newhtml); + if (placeholder) { + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); + } + self.markUpdated(); + } + } else { + alert(M.util.get_string('servererror', 'moodle')); + if (placeholder) { + placeholder.remove(true); + } + } + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + } + return false; + }, /** 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 b8f286b005b5b..30711122c3ff5 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 @@ -163,6 +163,7 @@ var CSS = { '{{#if presentation}}role="presentation" {{/if}}' + 'style="{{alignment}}{{margin}}{{customstyle}}"' + '{{#if classlist}}class="{{classlist}}" {{/if}}' + + '{{#if id}}id="{{id}}" {{/if}}' + '/>'; Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], { @@ -204,6 +205,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi _rawImageDimensions: null, initializer: function() { + this.addButton({ icon: 'e/insert_edit_image', callback: this._displayDialogue, @@ -211,6 +213,121 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi tagMatchRequiresAll: false }); this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this); + this.editor.on('drop', this._handleDragDrop, this); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @private + */ + _handleDragDrop: function(e) { + + var self = this, + host = this.get('host'), + template = Y.Handlebars.compile(IMAGETEMPLATE); + + host.saveSelection(); + e = e._event; + + // Only handle the event if an image file was dropped in. + if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length && /^image\//.test(e.dataTransfer.files[0].type)) { + + var options = host.get('filepickeroptions').image, + savepath = (options.savepath === undefined) ? '/' : options.savepath, + formData = new FormData(), + timestamp = 0, + uploadid = "", + xhr = new XMLHttpRequest(), + imagehtml = "", + keys = Object.keys(options.repositories); + + e.preventDefault(); + e.stopPropagation(); + formData.append('repo_upload_file', e.dataTransfer.files[0]); + formData.append('itemid', options.itemid); + + // List of repositories is an object rather than an array. This makes iteration more awkward. + for (var i = 0; i < keys.length; i++) { + if (options.repositories[keys[i]].type === 'upload') { + formData.append('repo_id', options.repositories[keys[i]].id); + break; + } + } + formData.append('env', options.env); + formData.append('sesskey', M.cfg.sesskey); + formData.append('client_id', options.client_id); + formData.append('savepath', savepath); + formData.append('ctx_id', options.context.id); + + // Insert spinner as a placeholder. + timestamp = new Date().getTime(); + uploadid = 'moodleimage_' + Math.round(Math.random() * 100000) + '-' + timestamp; + host.focus(); + host.restoreSelection(); + imagehtml = template({ + url: M.util.image_url("i/loading_small", 'moodle'), + alt: M.util.get_string('uploading', COMPONENTNAME), + id: uploadid + }); + host.insertContentAtFocusPoint(imagehtml); + self.markUpdated(); + + // Kick off a XMLHttpRequest. + xhr.onreadystatechange = function() { + var placeholder = self.editor.one('#' + uploadid), + result, + file, + newhtml, + newimage; + + if (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { + if (placeholder) { + placeholder.remove(true); + } + return new M.core.ajaxException(result); + } + + file = result; + if (result.event && result.event === 'fileexists') { + // A file with this name is already in use here - rename to avoid conflict. + // Chances are, it's a different image (stored in a different folder on the user's computer). + // If the user wants to reuse an existing image, they can copy/paste it within the editor. + file = result.newfile; + } + + // Replace placeholder with actual image. + newhtml = template({ + url: file.url, + presentation: true + }); + newimage = Y.Node.create(newhtml); + if (placeholder) { + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); + } + self.markUpdated(); + } + } else { + alert(M.util.get_string('servererror', 'moodle')); + if (placeholder) { + placeholder.remove(true); + } + } + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + } + return false; + }, /**