garyhodgson (owner)

Fork Of

Revisions

gist: 205455 Download_button fork
public
Public Clone URL: git://gist.github.com/205455.git
Embed All Files: show embed
DropManager.js #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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'],
  
  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 (!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;
    try {
      var 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.
      var 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);
  }
  
});