[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 /** 2 * @requires javelin-install 3 * javelin-util 4 * javelin-request 5 * javelin-dom 6 * javelin-uri 7 * phabricator-file-upload 8 * @provides phabricator-drag-and-drop-file-upload 9 * @javelin 10 */ 11 12 JX.install('PhabricatorDragAndDropFileUpload', { 13 14 construct : function(node) { 15 this._node = node; 16 }, 17 18 events : [ 19 'didBeginDrag', 20 'didEndDrag', 21 'willUpload', 22 'progress', 23 'didUpload', 24 'didError'], 25 26 statics : { 27 isSupported : function() { 28 // TODO: Is there a better capability test for this? This seems okay in 29 // Safari, Firefox and Chrome. 30 31 return !!window.FileList; 32 }, 33 isPasteSupported : function() { 34 // TODO: Needs to check if event.clipboardData is available. 35 // Works in Chrome, doesn't work in Firefox 10. 36 return !!window.FileList; 37 } 38 }, 39 40 members : { 41 _node : null, 42 _depth : 0, 43 _updateDepth : function(delta) { 44 if (this._depth === 0 && delta > 0) { 45 this.invoke('didBeginDrag'); 46 } 47 48 this._depth += delta; 49 50 if (this._depth === 0 && delta < 0) { 51 this.invoke('didEndDrag'); 52 } 53 }, 54 55 start : function() { 56 57 // TODO: move this to JX.DOM.contains()? 58 function contains(container, child) { 59 do { 60 if (child === container) { 61 return true; 62 } 63 child = child.parentNode; 64 } while (child); 65 66 return false; 67 } 68 69 // Firefox has some issues sometimes; implement this click handler so 70 // the user can recover. See T5188. 71 JX.DOM.listen( 72 this._node, 73 'click', 74 null, 75 JX.bind(this, function (e) { 76 if (this._depth) { 77 e.kill(); 78 // Force depth to 0. 79 this._updateDepth(-this._depth); 80 } 81 })); 82 83 // We track depth so that the _node may have children inside of it and 84 // not become unselected when they are dragged over. 85 JX.DOM.listen( 86 this._node, 87 'dragenter', 88 null, 89 JX.bind(this, function(e) { 90 if (contains(this._node, e.getTarget())) { 91 this._updateDepth(1); 92 } 93 })); 94 95 JX.DOM.listen( 96 this._node, 97 'dragleave', 98 null, 99 JX.bind(this, function(e) { 100 if (contains(this._node, e.getTarget())) { 101 this._updateDepth(-1); 102 } 103 })); 104 105 JX.DOM.listen( 106 this._node, 107 'dragover', 108 null, 109 function(e) { 110 // NOTE: We must set this, or Chrome refuses to drop files from the 111 // download shelf. 112 e.getRawEvent().dataTransfer.dropEffect = 'copy'; 113 e.kill(); 114 }); 115 116 JX.DOM.listen( 117 this._node, 118 'drop', 119 null, 120 JX.bind(this, function(e) { 121 e.kill(); 122 123 var files = e.getRawEvent().dataTransfer.files; 124 for (var ii = 0; ii < files.length; ii++) { 125 this._sendRequest(files[ii]); 126 } 127 128 // Force depth to 0. 129 this._updateDepth(-this._depth); 130 })); 131 132 if (JX.PhabricatorDragAndDropFileUpload.isPasteSupported()) { 133 JX.DOM.listen( 134 this._node, 135 'paste', 136 null, 137 JX.bind(this, function(e) { 138 var clipboard = e.getRawEvent().clipboardData; 139 if (!clipboard) { 140 return; 141 } 142 143 // If there's any text on the clipboard, just let the event fire 144 // normally, choosing the text over any images. See T5437 / D9647. 145 var text = clipboard.getData('text/plain').toString(); 146 if (text.length) { 147 return; 148 } 149 150 // Safari and Firefox have clipboardData, but no items. They 151 // don't seem to provide a way to get image data directly yet. 152 if (!clipboard.items) { 153 return; 154 } 155 156 for (var ii = 0; ii < clipboard.items.length; ii++) { 157 var item = clipboard.items[ii]; 158 if (!/^image\//.test(item.type)) { 159 continue; 160 } 161 var spec = item.getAsFile(); 162 // pasted files don't have a name; see 163 // https://code.google.com/p/chromium/issues/detail?id=361145 164 if (!spec.name) { 165 spec.name = 'pasted_file'; 166 } 167 this._sendRequest(spec); 168 } 169 })); 170 } 171 }, 172 _sendRequest : function(spec) { 173 var file = new JX.PhabricatorFileUpload() 174 .setName(spec.name) 175 .setTotalBytes(spec.size) 176 .setStatus('uploading') 177 .update(); 178 179 this.invoke('willUpload', file); 180 181 var up_uri = JX.$U(this.getURI()) 182 .setQueryParam('name', file.getName()) 183 .setQueryParam('__upload__', 1); 184 185 if (this.getViewPolicy()) { 186 up_uri.setQueryParam('viewPolicy', this.getViewPolicy()); 187 } 188 189 up_uri = up_uri.toString(); 190 191 var onupload = JX.bind(this, function(r) { 192 if (r.error) { 193 file 194 .setStatus('error') 195 .setError(r.error) 196 .update(); 197 198 this.invoke('didError', file); 199 } else { 200 file 201 .setID(r.id) 202 .setPHID(r.phid) 203 .setURI(r.uri) 204 .setMarkup(r.html) 205 .setStatus('done') 206 .update(); 207 208 this.invoke('didUpload', file); 209 } 210 }); 211 212 var req = new JX.Request(up_uri, onupload); 213 214 var onerror = JX.bind(this, function(error) { 215 file.setStatus('error'); 216 217 if (error) { 218 file.setError(error.code + ': ' + error.info); 219 } else { 220 var xhr = req.getTransport(); 221 if (xhr.responseText) { 222 file.setError('Server responded: ' + xhr.responseText); 223 } 224 } 225 226 file.update(); 227 this.invoke('didError', file); 228 }); 229 230 var onprogress = JX.bind(this, function(progress) { 231 file 232 .setTotalBytes(progress.total) 233 .setUploadedBytes(progress.loaded) 234 .update(); 235 236 this.invoke('progress', file); 237 }); 238 239 req.listen('error', onerror); 240 req.listen('uploadprogress', onprogress); 241 242 req 243 .setRawData(spec) 244 .send(); 245 } 246 }, 247 properties: { 248 URI : null, 249 activatedClass : null, 250 viewPolicy : null 251 } 252 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |