-
-
Save willbailey/302532 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//= require "util" | |
// This code is MIT licensed: http://creativecommons.org/licenses/MIT/ | |
// Zenbe Inc (2009). | |
// Ensure our Zenbe namespaces exist. | |
window.zen = window.zen || {}; | |
window.zen.util = window.zen.util || {}; | |
/** | |
* The DropManager class provides a pleasant API for observing HTML5 drag-n-drop | |
* events, cleaning up the data that they return, and triggering the appropriate | |
* callbacks. With the drag and drop API so far, there are generally three types | |
* of data we're interested in: HTML, URLs, and plain text. On drop, your | |
* callback will receive a pre-processed drop object with corresponding 'url', | |
* 'html' and 'text' properties. | |
* | |
* To use it, instantiate a new DropManager, passing in an element to use as a | |
* drop target, as well as an (optional) callbacks object, with (optional) | |
* callbacks for drop, dragenter, dragover and dragleave: | |
* | |
* new zen.util.DropManager($('drop_target'), { | |
* drop : function(event, dropData) { ... process data here ... } | |
* }); | |
* | |
*/ | |
window.zen.util.DropManager = Class.create({ | |
SUPPORTED_TYPES : ['text/uri-list', 'text/x-moz-url', 'text/html', | |
'text/plain', 'Url', 'Text', 'Files'], | |
IMAGE_LINK_DETECTOR : (/^\s*<a.+?<img.+?src=['"](\S+?)['"].*?>\s*<\/a>\s*$/i), | |
URL_DETECTOR : (/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/), | |
/** | |
* Registers all of the drop handlers that we'll need for dealing with | |
* drop-related actions on the target element. | |
*/ | |
initialize : function(element, callbacks) { | |
this.enabled = true; | |
this.callbacks = callbacks || {}; | |
this.el = element; | |
this.el.observe('dragenter', this._boundDragEnter = this._onDragEnter.bind(this)); | |
this.el.observe('dragover', this._boundDragOver = this._onDragOver.bind(this)); | |
this.el.observe('dragleave', this._boundDragLeave = this._onDragLeave.bind(this)); | |
this.el.observe('drop', this._boundDrop = this._onDrop.bind(this)); | |
}, | |
/** | |
* Removes all of the drop handlers from the target element. | |
*/ | |
destroy : function() { | |
this.el.stopObserving('dragenter', this._boundDragEnter); | |
this.el.stopObserving('dragover', this._boundDragOver); | |
this.el.stopObserving('dragleave', this._boundDragLeave); | |
this.el.stopObserving('drop', this._boundDrop); | |
}, | |
/** | |
* Turn the DropManager's callbacks on and off. If disabled, none of your | |
* passed-in callbacks will be fired until the DropManager is re-enabled. | |
*/ | |
setEnabled : function(enabled) { | |
this.enabled = enabled; | |
}, | |
/** | |
* Does the DropManager support the drop event? Returns true if we're enabled, | |
* and we support at least one of the drop's mime types. | |
* @function ? | |
*/ | |
supports : function(ev) { | |
if (ev.dataTransfer.files) return true; | |
if (!this.enabled) return false; | |
var types = $A(ev.dataTransfer.types); | |
if (!types.any()) return true; // IE doesn't give us any types whatsoever. | |
return this.SUPPORTED_TYPES.any(function(t){ return types.include(t); }); | |
}, | |
/** | |
* Internal function to post-process the drop data to pull out HTML image links | |
* (<a><img></a>). The desired url is probably for the image, not the link. | |
* In addition, some versions of webkit pass perfectly good urls as just | |
* text/plain -- pull 'em out. | |
*/ | |
_postProcess : function(data) { | |
var textPart = data.html || data.text; | |
var match = textPart && textPart.match(this.IMAGE_LINK_DETECTOR); | |
if (match) data.url = match[1]; | |
match = data.text && !data.url && data.text.match(this.URL_DETECTOR); | |
if (match) data.url = data.text; | |
return data; | |
}, | |
/** | |
* On drop, we pull out our preferred and supported mime-types, process them | |
* for links, and return the resulting drop object to the provided callback. | |
* @function ? | |
*/ | |
_onDrop : function(ev) { | |
ev.preventDefault(); | |
if (!this.supports(ev)) return false; | |
var dt = ev.dataTransfer; | |
var data; | |
if (dt.files) { | |
data = {files : []}; | |
// check after each file is loaded to see if more remain | |
var callback = function(file, base64FileData){ | |
data.files.push({ | |
file_name : file.name || file.fileName, | |
data : base64FileData | |
}); | |
if ($A(dt.files).all(function(f){return f._completed;})) { | |
if (this.callbacks.drop) this.callbacks.drop(ev, data); | |
} | |
}.bind(this); | |
// process each file asynchronously | |
for (var i=0; i<dt.files.length; i++) { | |
this._handleFile(dt.files[i], callback); | |
} | |
} else { | |
try { | |
data = { | |
url : dt.getData('text/uri-list') || dt.getData('text/x-moz-url'), | |
html : dt.getData('text/html').stripScripts(), | |
text : dt.getData('text/plain') | |
}; | |
} catch(e) { // IE doesn't support mime types yet. | |
data = { | |
url : dt.getData('Url'), | |
text : dt.getData('Text') | |
}; | |
} | |
data = this._postProcess(data); | |
if (this.callbacks.drop) this.callbacks.drop(ev, data); | |
} | |
}, | |
/** | |
* On drag enter, enable dropping on the drop target. | |
* @function ? | |
*/ | |
_onDragEnter : function(ev) { | |
ev.preventDefault(); | |
if (!this.supports(ev)) return false; | |
try { // IE Doesn't allow this property to be set ... but Webkit requires it. | |
ev.dataTransfer.effectAllowed = 'all'; | |
ev.dataTransfer.dropEffect = 'copy'; | |
} catch(e){} | |
if (this.callbacks.dragenter) this.callbacks.dragenter(ev); | |
}, | |
/** | |
* On drag over, enable dropping on the drop target. | |
* @function ? | |
*/ | |
_onDragOver : function(ev) { | |
ev.preventDefault(); | |
// if (!this.supports(ev)) return false; | |
try { // IE Doesn't allow this property to be set ... but Webkit requires it. | |
ev.dataTransfer.effectAllowed = 'all'; | |
ev.dataTransfer.dropEffect = 'copy'; | |
} catch(e){} | |
if (this.callbacks.dragover) this.callbacks.dragover(ev); | |
}, | |
/** | |
* Not much to do for drag leave. | |
* @function ? | |
*/ | |
_onDragLeave : function(ev) { | |
ev.preventDefault(); | |
if (!this.supports(ev)) return false; | |
if (this.callbacks.dragleave) this.callbacks.dragleave(ev); | |
}, | |
/** | |
* read a dropped file | |
* @function ? | |
*/ | |
_handleFile : function(file, callback) { | |
var binaryReader = new FileReader(); | |
binaryReader.onloadend = function() { | |
file._completed=true; | |
callback(file, this._base64Encode(binaryReader.result)); | |
}.bind(this); | |
console.log(file); | |
binaryReader.readAsBinaryString(file); | |
}, | |
/** | |
* base64 encode | |
* @function ? | |
*/ | |
_base64Encode : function(text){ | |
if (/([^\u0000-\u00ff])/.test(text)){ | |
throw new Error("Can't base64 encode non-ASCII characters."); | |
} | |
var digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", | |
i = 0, | |
cur, prev, byteNum, | |
result=[]; | |
while(i < text.length){ | |
cur = text.charCodeAt(i); | |
byteNum = i % 3; | |
switch(byteNum){ | |
case 0: //first byte | |
result.push(digits.charAt(cur >> 2)); | |
break; | |
case 1: //second byte | |
result.push(digits.charAt((prev & 3) << 4 | (cur >> 4))); | |
break; | |
case 2: //third byte | |
result.push(digits.charAt((prev & 0x0f) << 2 | (cur >> 6))); | |
result.push(digits.charAt(cur & 0x3f)); | |
break; | |
} | |
prev = cur; | |
i++; | |
} | |
if (byteNum == 0){ | |
result.push(digits.charAt((prev & 3) << 4)); | |
result.push("=="); | |
} else if (byteNum == 1){ | |
result.push(digits.charAt((prev & 0x0f) << 2)); | |
result.push("="); | |
} | |
return result.join(""); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment