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 6e9752ffc49..bed9c394068 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 @@ -243,6 +243,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }); this.editor.delegate('dblclick', this._displayDialogue, 'img', this); this.editor.delegate('click', this._handleClick, 'img', this); + this.editor.on('paste', this._handlePaste, this); this.editor.on('drop', this._handleDragDrop, this); // e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers. @@ -259,118 +260,189 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e - * @return mixed + * @return {boolean} false if we handled the event, else true. * @private */ _handleDragDrop: function(e) { + if (!e._event || !e._event.dataTransfer) { + // Drop not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.dataTransfer); + }, + + /** + * Handles paste events where - if the thing being pasted is an image. + * + * @method _handlePaste + * @param {EventFacade} e + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePaste: function(e) { + if (!e._event || !e._event.clipboardData) { + // Paste not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.clipboardData); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @param {DataTransfer} dataTransfer + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePasteOrDropHelper: function(e, dataTransfer) { + + var items = dataTransfer.items, + didUpload = false; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.kind !== 'file') { + continue; + } + if (!this._isImage(item.type)) { + continue; + } + this._uploadImage(item.getAsFile()); + didUpload = true; + } + + if (didUpload) { + // We handled this. + e.preventDefault(); + e.stopPropagation(); + return false; + } else { + // Let someone else try to handle it. + return true; + } + }, + + /** + * Is this file an image? + * + * @method _isImage + * @param {string} mimeType the file's mime type. + * @return {boolean} true if the file has an image mimeType. + * @private + */ + _isImage: function(mimeType) { + return mimeType.indexOf('image/') === 0; + }, + + /** + * Used by _handleDragDrop and _handlePaste to upload an image and insert it. + * + * @method _uploadImage + * @param {File} fileToSave + * @private + */ + _uploadImage: function(fileToSave) { 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. - var handlesDataTransfer = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length); - if (handlesDataTransfer && /^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); - 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); + formData.append('repo_upload_file', fileToSave); + formData.append('itemid', options.itemid); - 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; - } + // 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); + } + 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(); + // 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; + // 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 (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { if (placeholder) { - placeholder.replace(newimage); - } else { - self.editor.appendChild(newimage); + placeholder.remove(true); } - self.markUpdated(); + throw new M.core.ajaxException(result); } - } else { - Y.use('moodle-core-notification-alert', function() { - new M.core.alert({message: M.util.get_string('servererror', 'moodle')}); + + 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.remove(true); + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); } + self.markUpdated(); + } + } else { + Y.use('moodle-core-notification-alert', function() { + new M.core.alert({message: 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; - } - -}, + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + }, /** * Handle a click on an image. 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 94511b8d106..fea59fe97ef 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,3 +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",ALIGNSETTINGS:"atto_image_button"},r={INPUTURL:"."+n.INPUTURL},i=[{name:"verticalAlign",str:"alignment_top",value:"text-top",margin:"0 0.5em"},{name:"verticalAlign",str:"alignment_middle",value:"middle",margin:"0 0.5em"},{name:"verticalAlign",str:"alignment_bottom",value:"text-bottom",margin:"0 0.5em",isDefault:!0},{name:"float",str:"alignment_left",value:"left",margin:"0 0.5em 0 0"},{name:"float",str:"alignment_right",value:"right",margin:"0 0 0 0.5em"}],s={ISPERCENT:/\d+%/},o="atto_image",u='
{{#if showFilepicker}}
{{else}}
{{/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._displayDialogue,"img",this),this.editor.delegate("click",this._handleClick,"img",this),this.editor.on("drop",this._handleDragDrop,this),this.editor.on("dragover",function(e){e.preventDefault()},this),this.editor.on("dragenter",function(e){e.preventDefault()},this)},_handleDragDrop:function(t){var n=this,r=this.get("host"),i=e.Handlebars.compile(a);r.saveSelection(),t=t._event;var s=t.dataTransfer&&t.dataTransfer.files&&t.dataTransfer.files.length;if(s&&/^image\//.test(t.dataTransfer.files[0].type)){var u=r.get("filepickeroptions").image,f=u.savepath===undefined?"/":u.savepath,l=new FormData,c=0,h="",p=new XMLHttpRequest,d="",v=Object.keys(u.repositories);t.preventDefault(),t.stopPropagation(),l.append("repo_upload_file",t.dataTransfer.files[0]),l.append("itemid",u.itemid);for(var m=0;m");return r.setStyle("margin",n.margin),t.getStyle("margin")!==r.getStyle("margin")?!1:(t.addClass(this._getAlignmentClass(n.value)),t.setStyle(n.name,null),t.setStyle("margin",null),!0)},this),t):t},_getAlignmentClass:function(e){return n.ALIGNSETTINGS+"_"+e},_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",ALIGNSETTINGS:"atto_image_button"},r={INPUTURL:"."+n.INPUTURL},i=[{name:"verticalAlign",str:"alignment_top",value:"text-top",margin:"0 0.5em"},{name:"verticalAlign",str:"alignment_middle",value:"middle",margin:"0 0.5em"},{name:"verticalAlign",str:"alignment_bottom",value:"text-bottom",margin:"0 0.5em",isDefault:!0},{name:"float",str:"alignment_left",value:"left",margin:"0 0.5em 0 0"},{name:"float",str:"alignment_right",value:"right",margin:"0 0 0 0.5em"}],s={ISPERCENT:/\d+%/},o="atto_image",u='
{{#if showFilepicker}}
{{else}}
{{/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._displayDialogue,"img",this),this.editor.delegate("click",this._handleClick,"img",this),this.editor.on("paste",this._handlePaste,this),this.editor.on("drop",this._handleDragDrop,this),this.editor.on("dragover",function(e){e.preventDefault()},this),this.editor.on("dragenter",function(e){e.preventDefault()},this)},_handleDragDrop:function(e){return!e._event||!e._event.dataTransfer?!0:this._handlePasteOrDropHelper(e,e._event.dataTransfer)},_handlePaste:function(e){return!e._event||!e._event.clipboardData?!0:this._handlePasteOrDropHelper(e,e._event.clipboardData)},_handlePasteOrDropHelper:function(e,t){var n=t.items,r=!1;for(var i=0;i");return r.setStyle("margin",n.margin),t.getStyle("margin")!==r.getStyle("margin")?!1:(t.addClass(this._getAlignmentClass(n.value)),t.setStyle(n.name,null),t.setStyle("margin",null),!0)},this),t):t},_getAlignmentClass:function(e){return n.ALIGNSETTINGS+"_"+e},_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"]}); 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 93e07c3dd42..5c65d96a7db 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 @@ -243,6 +243,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }); this.editor.delegate('dblclick', this._displayDialogue, 'img', this); this.editor.delegate('click', this._handleClick, 'img', this); + this.editor.on('paste', this._handlePaste, this); this.editor.on('drop', this._handleDragDrop, this); // e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers. @@ -259,118 +260,189 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e - * @return mixed + * @return {boolean} false if we handled the event, else true. * @private */ _handleDragDrop: function(e) { + if (!e._event || !e._event.dataTransfer) { + // Drop not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.dataTransfer); + }, + + /** + * Handles paste events where - if the thing being pasted is an image. + * + * @method _handlePaste + * @param {EventFacade} e + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePaste: function(e) { + if (!e._event || !e._event.clipboardData) { + // Paste not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.clipboardData); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @param {DataTransfer} dataTransfer + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePasteOrDropHelper: function(e, dataTransfer) { + + var items = dataTransfer.items, + didUpload = false; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.kind !== 'file') { + continue; + } + if (!this._isImage(item.type)) { + continue; + } + this._uploadImage(item.getAsFile()); + didUpload = true; + } + + if (didUpload) { + // We handled this. + e.preventDefault(); + e.stopPropagation(); + return false; + } else { + // Let someone else try to handle it. + return true; + } + }, + + /** + * Is this file an image? + * + * @method _isImage + * @param {string} mimeType the file's mime type. + * @return {boolean} true if the file has an image mimeType. + * @private + */ + _isImage: function(mimeType) { + return mimeType.indexOf('image/') === 0; + }, + + /** + * Used by _handleDragDrop and _handlePaste to upload an image and insert it. + * + * @method _uploadImage + * @param {File} fileToSave + * @private + */ + _uploadImage: function(fileToSave) { 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. - var handlesDataTransfer = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length); - if (handlesDataTransfer && /^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); - 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); + formData.append('repo_upload_file', fileToSave); + formData.append('itemid', options.itemid); - 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; - } + // 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); + } + 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(); + // 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; + // 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 (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { if (placeholder) { - placeholder.replace(newimage); - } else { - self.editor.appendChild(newimage); + placeholder.remove(true); } - self.markUpdated(); + throw new M.core.ajaxException(result); } - } else { - Y.use('moodle-core-notification-alert', function() { - new M.core.alert({message: M.util.get_string('servererror', 'moodle')}); + + 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.remove(true); + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); } + self.markUpdated(); + } + } else { + Y.use('moodle-core-notification-alert', function() { + new M.core.alert({message: 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; - } - -}, + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + }, /** * Handle a click on an image. 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 0289f06f195..7aa557af357 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 @@ -241,6 +241,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }); this.editor.delegate('dblclick', this._displayDialogue, 'img', this); this.editor.delegate('click', this._handleClick, 'img', this); + this.editor.on('paste', this._handlePaste, this); this.editor.on('drop', this._handleDragDrop, this); // e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers. @@ -257,118 +258,189 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e - * @return mixed + * @return {boolean} false if we handled the event, else true. * @private */ _handleDragDrop: function(e) { + if (!e._event || !e._event.dataTransfer) { + // Drop not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.dataTransfer); + }, + + /** + * Handles paste events where - if the thing being pasted is an image. + * + * @method _handlePaste + * @param {EventFacade} e + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePaste: function(e) { + if (!e._event || !e._event.clipboardData) { + // Paste not fully supported in this browser. + return true; + } + + return this._handlePasteOrDropHelper(e, e._event.clipboardData); + }, + + /** + * Handle a drag and drop event with an image. + * + * @method _handleDragDrop + * @param {EventFacade} e + * @param {DataTransfer} dataTransfer + * @return {boolean} false if we handled the event, else true. + * @private + */ + _handlePasteOrDropHelper: function(e, dataTransfer) { + + var items = dataTransfer.items, + didUpload = false; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.kind !== 'file') { + continue; + } + if (!this._isImage(item.type)) { + continue; + } + this._uploadImage(item.getAsFile()); + didUpload = true; + } + + if (didUpload) { + // We handled this. + e.preventDefault(); + e.stopPropagation(); + return false; + } else { + // Let someone else try to handle it. + return true; + } + }, + + /** + * Is this file an image? + * + * @method _isImage + * @param {string} mimeType the file's mime type. + * @return {boolean} true if the file has an image mimeType. + * @private + */ + _isImage: function(mimeType) { + return mimeType.indexOf('image/') === 0; + }, + + /** + * Used by _handleDragDrop and _handlePaste to upload an image and insert it. + * + * @method _uploadImage + * @param {File} fileToSave + * @private + */ + _uploadImage: function(fileToSave) { 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. - var handlesDataTransfer = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length); - if (handlesDataTransfer && /^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); - 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); + formData.append('repo_upload_file', fileToSave); + formData.append('itemid', options.itemid); - 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; - } + // 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); + } + 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(); + // 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; + // 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 (xhr.readyState === 4) { + if (xhr.status === 200) { + result = JSON.parse(xhr.responseText); + if (result) { + if (result.error) { if (placeholder) { - placeholder.replace(newimage); - } else { - self.editor.appendChild(newimage); + placeholder.remove(true); } - self.markUpdated(); + throw new M.core.ajaxException(result); } - } else { - Y.use('moodle-core-notification-alert', function() { - new M.core.alert({message: M.util.get_string('servererror', 'moodle')}); + + 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.remove(true); + placeholder.replace(newimage); + } else { + self.editor.appendChild(newimage); } + self.markUpdated(); + } + } else { + Y.use('moodle-core-notification-alert', function() { + new M.core.alert({message: 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; - } - -}, + } + }; + xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true); + xhr.send(formData); + }, /** * Handle a click on an image.