Created
December 31, 2011 14:32
-
-
Save anonymous/1544144 to your computer and use it in GitHub Desktop.
File input UI like Gmail
This file contains 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
goog.provide('arielarea.ui.FileInput'); | |
goog.require('goog.array'); | |
goog.require('goog.string'); | |
goog.require('goog.dom'); | |
goog.require('goog.events'); | |
goog.require('goog.events.EventTarget'); | |
goog.require('goog.events.FileDropHandler'); | |
goog.require('goog.Timer'); | |
goog.require('goog.ui.Component'); | |
goog.require('goog.userAgent'); | |
/** | |
* @constructor | |
* @extends {goog.ui.Component} | |
*/ | |
arielarea.ui.FileInput = function (opt_domHelper) { | |
goog.base(this, opt_domHelper); | |
this.eh_ = new goog.events.EventHandler(this); | |
this.window_ = window; | |
}; | |
goog.inherits(arielarea.ui.FileInput, goog.ui.Component); | |
arielarea.ui.FileInput.prototype.decorateInternal = function (element) { | |
this.elem_ = element; | |
this.inputName_ = element.name; | |
this.form_ = goog.dom.getAncestorByTagNameAndClass(element, 'form'); | |
var dzElem = goog.dom.getElementByClass('arielarea-dropzone', this.form_); | |
this.dropZone_ = new arielarea.ui.FileInput.DropZone(dzElem, this); | |
this.appendDropZone(this.dropZone_); | |
this.eh_.listen(this.dropZone_, goog.events.FileDropHandler.EventType.DROP, function (e) { | |
var files = e.getBrowserEvent().dataTransfer.files; | |
this.handleDroppedFiles_(files, this.dropZone_); | |
}); | |
this.eh_.listen(this.elem_, goog.events.EventType.CHANGE, function (e) { | |
var files = e.target.files; | |
this.handleDroppedFiles_(files, this.dropZone_); | |
e.target.value = null; | |
e.stopPropagation(); | |
e.preventDefault(); | |
}); | |
var fiElem = goog.dom.getElementByClass('arielarea-fileitem', this.form_); | |
this.renderer_ = new arielarea.ui.FileInput.Renderer(fiElem); | |
this.showOnEnter(); | |
this.hideOnLeave(); | |
this.updateFilesOnRemove(); | |
}; | |
arielarea.ui.FileInput.prototype.disposeInternal = function () { | |
if (this.eh_) { | |
this.eh_.dispose(); | |
this.eh_ = null; | |
} | |
this.clearFiles(); | |
arielarea.ui.FileInput.superClass_.disposeInternal.call(this); | |
}; | |
arielarea.ui.FileInput.prototype.appendDropZone = function (dropZone) { | |
goog.dom.insertSiblingBefore(dropZone.getElement(), this.elem_); | |
}; | |
arielarea.ui.FileInput.prototype.setRenderer = function (renderer) { | |
this.renderer_ = renderer; | |
}; | |
arielarea.ui.FileInput.prototype.getRenderer = function () { | |
return this.renderer_; | |
}; | |
arielarea.ui.FileInput.prototype.getInputName = function () { | |
return this.inputName_; | |
}; | |
arielarea.ui.FileInput.prototype.showOnEnter = function () { | |
this.eh_.listenOnce(this.window_, goog.events.EventType.DRAGENTER, function (e) { | |
this.dndContainsFiles_ = this.containsFiles_(e); | |
if (this.dndContainsFiles_) { | |
this.showDropZone(); | |
} | |
}); | |
}; | |
/* | |
* This logic is copied from @link {goog.events.FileDropHandler#onDocDragEnter_} | |
*/ | |
arielarea.ui.FileInput.prototype.containsFiles_ = function (dragEvent) { | |
var dt = dragEvent.getBrowserEvent().dataTransfer; | |
// Check whether the drag event contains files. | |
var dndContainsFiles = !!(dt && | |
((dt.types && | |
(goog.array.contains(dt.types, 'Files') || | |
goog.array.contains(dt.types, 'public.file-url'))) || | |
(dt.files && dt.files.length > 0))); | |
return dndContainsFiles; | |
}; | |
arielarea.ui.FileInput.prototype.showDropZone = function () { | |
goog.style.showElement(this.elem_, false); | |
this.dropZone_.show(true); | |
this.shown_ = true; | |
}; | |
arielarea.ui.FileInput.prototype.hideDropZone = function () { | |
this.dropZone_.show(false); | |
goog.style.showElement(this.elem_, true); | |
this.shown_ = false; | |
this.showOnEnter(); | |
}; | |
arielarea.ui.FileInput.prototype.hideOnLeave = function () { | |
if (goog.userAgent.GECKO) { | |
this.hideAfterWait(); | |
} else { | |
this.eh_.listen(this.window_, goog.events.EventType.DRAGLEAVE, function (e) { | |
var event = e.getBrowserEvent(); | |
if (this.isOutOfWindow_(event)) { | |
this.hideDropZone(); | |
} | |
}); | |
} | |
}; | |
arielarea.ui.FileInput.prototype.hideAfterWait = function () { | |
this.eh_.listen(this.window_, goog.events.EventType.DRAGOVER, function (e) { | |
goog.Timer.clear(this.hideTimer_); | |
this.hideTimer_ = null; | |
this.hideTimer_ = goog.Timer.callOnce(goog.bind(function () { | |
this.hideDropZone(); | |
this.hideTimer_ = null; | |
}, this), 750); | |
if (!this.shown_ && this.dndContainsFiles_) { | |
this.showDropZone(); | |
} | |
}, true); | |
}; | |
arielarea.ui.FileInput.prototype.handleDroppedFiles_ = function (files, dropZone) { | |
var inputName = this.getInputName(); | |
goog.array.forEach(files, function (file) { | |
if (this.validateFile_(file)) { | |
var fileItem = new arielarea.ui.FileInput.FileItem(file, inputName); | |
dropZone.add(fileItem); | |
this.renderer_.renderFile(fileItem, this.elem_, this.eh_); | |
} | |
}, this); | |
this.hideDropZone(); | |
}; | |
arielarea.ui.FileInput.prototype.updateFilesOnRemove = function () { | |
this.eh_.listen(this.renderer_, arielarea.ui.FileInput.EventType.REMOVE, function (e) { | |
this.handleRemovedFile_(e.fileItem, this.dropZone_); | |
}); | |
}; | |
arielarea.ui.FileInput.prototype.handleRemovedFile_ = function (fileItem, dropZone) { | |
if (fileItem) { | |
dropZone.remove(fileItem); | |
} | |
}; | |
arielarea.ui.FileInput.prototype.getFiles = function () { | |
var files = this.dropZone_.getFiles(); | |
return files; | |
}; | |
arielarea.ui.FileInput.prototype.clearFiles = function () { | |
this.dropZone_.clearFiles(); | |
}; | |
arielarea.ui.FileInput.prototype.validateFile_ = function (file) { | |
if (file.size === 0) { | |
return false; | |
} | |
if (file.type) { | |
return true; | |
} | |
var fileName = file.name || file.fileName; | |
var ext = (function getExtension (fileName) { | |
if (!fileName) { | |
return; | |
} | |
if (!goog.string.contains(fileName, '.')) { | |
return; | |
} | |
return /[^.]+$/.exec(fileName)[0]; | |
}(fileName)); | |
if (ext) { | |
file.extension = ext; | |
} | |
return true; | |
}; | |
arielarea.ui.FileInput.prototype.isOutOfWindow_ = function (event) { | |
var x = event.clientX; | |
var y = event.clientY; | |
if (x <= 0 || y <= 0) { | |
return true; | |
} | |
var size = goog.style.getSize(this.window_); | |
return x >= size.width || y >= size.height; | |
}; | |
arielarea.ui.FileInput.EventType = { | |
DROP: 'drop', | |
REMOVE: 'remove' | |
}; | |
/** | |
* @constructor | |
* @extends {goog.events.FileDropHandler} | |
*/ | |
arielarea.ui.FileInput.DropZone = function (element, owner) { | |
var elem = (element) ? element.cloneNode(true) : goog.dom.createDom( | |
'div', { | |
'class': 'arielarea-dropzone', | |
'style': 'display: none;' | |
}); | |
goog.base(this, elem, true); | |
this.owner_ = owner; | |
this.elem_ = elem; | |
this.files_ = []; | |
}; | |
goog.inherits(arielarea.ui.FileInput.DropZone, goog.events.FileDropHandler); | |
arielarea.ui.FileInput.DropZone.prototype.getElement = function () { | |
return this.elem_; | |
}; | |
arielarea.ui.FileInput.DropZone.prototype.getFiles = function () { | |
return this.files_; | |
}; | |
arielarea.ui.FileInput.DropZone.prototype.show = function (show) { | |
goog.style.showElement(this.getElement(), show); | |
}; | |
arielarea.ui.FileInput.DropZone.prototype.add = function (fileItem) { | |
this.files_.push(fileItem); | |
}; | |
arielarea.ui.FileInput.DropZone.prototype.remove = function (fileItem) { | |
goog.array.remove(this.files_, fileItem); | |
fileItem.dispose(); | |
}; | |
arielarea.ui.FileInput.DropZone.prototype.clearFiles = function () { | |
goog.array.forEach(this.files_, function (fileItem) { | |
fileItem.dispose(); | |
}); | |
this.files = []; | |
}; | |
/** | |
* @constructor | |
* @extends {goog.Disposable} | |
*/ | |
arielarea.ui.FileInput.FileItem = function (file, inputName) { | |
goog.base(this); | |
this.file_ = file; | |
this.fileName_ = file.name || file.fileName; | |
this.inputName_ = inputName; | |
this.elem_; | |
this.owner_; | |
}; | |
goog.inherits(arielarea.ui.FileInput.FileItem, goog.Disposable); | |
arielarea.ui.FileInput.FileItem.prototype.disposeInternal = function () { | |
goog.dom.removeNode(this.element); | |
this.elem_ = null; | |
this.file_ = null; | |
}; | |
arielarea.ui.FileInput.FileItem.prototype.getFileName = function () { | |
return this.fileName_; | |
}; | |
arielarea.ui.FileInput.FileItem.prototype.setElement = function (element) { | |
this.elem_ = element; | |
}; | |
/** | |
* @constructor | |
* @extends {goog.events.EventTarget} | |
*/ | |
arielarea.ui.FileInput.Renderer = function (template) { | |
goog.base(this); | |
this.template_ = template; | |
goog.style.showElement(template, false); | |
}; | |
goog.inherits(arielarea.ui.FileInput.Renderer, goog.events.EventTarget); | |
arielarea.ui.FileInput.Renderer.prototype.getFileTemplate = function () { | |
return this.template_; | |
}; | |
arielarea.ui.FileInput.Renderer.prototype.renderFile = function ( | |
fileItem, anchorElement, eventHandler) { | |
var fileName = fileItem.getFileName(); | |
var newElem = this.getFileTemplate().cloneNode(true); | |
fileItem.setElement(newElem); | |
var itemName = goog.dom.getElementByClass('arielarea-itemname', newElem); | |
goog.dom.setTextContent(itemName, fileName); | |
var removeButton = goog.dom.getElementByClass('arielarea-removebtn', newElem); | |
eventHandler.listen(removeButton, goog.events.EventType.CLICK, function () { | |
goog.dom.removeNode(newElem); | |
this.dispatchEvent({ | |
type: arielarea.ui.FileInput.EventType.REMOVE, | |
fileItem: fileItem | |
}); | |
}, false, this); | |
goog.dom.insertSiblingAfter(newElem, anchorElement); | |
goog.style.showElement(newElem, true); | |
return newElem; | |
}; |
This file contains 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
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Sample of arielarea.ui.FileInput</title> | |
<script src="/path/to/closure/goog/base.js"></script> | |
<script> | |
goog.require('goog.array'); | |
goog.require('goog.string'); | |
goog.require('goog.dom'); | |
goog.require('goog.events'); | |
goog.require('goog.events.EventTarget'); | |
goog.require('goog.events.FileDropHandler'); | |
goog.require('goog.Timer'); | |
goog.require('goog.ui.Component'); | |
goog.require('goog.userAgent'); | |
</script> | |
<script src="/path/to/fileinput.js"></script> | |
<style> | |
.arielarea-dropzone { | |
width: 80%; | |
height: 80px; | |
border: 5px solid #bababa; | |
background: #dedede; | |
text-align: center; | |
line-height: 80px; | |
font-size: 32px; | |
color: #787878; | |
} | |
.arielarea-fileitem { | |
position: relative; | |
width: 200px; | |
height: 20px; | |
margin: 3px; | |
padding-left: 2px; | |
border: 3px solid #cacaca; | |
background: #fafafa; | |
text-align: left; | |
line-height: 20px; | |
color: #252525; | |
overflow: hidden; | |
} | |
.arielarea-removebtn { | |
position: absolute; | |
background: #f66767; | |
top: 0px; | |
right: 0px; | |
width: 20px; | |
height: 20px; | |
text-align: center; | |
line-height: 20px; | |
} | |
.arielarea-removebtn:hover { | |
cursor: pointer; | |
background: #f69797; | |
color: #989898; | |
} | |
</style> | |
</head> | |
<body> | |
<form> | |
<input type="file" name="fileinput-sample" id="fileinput-sample" class="arielarea-fileinput"> | |
<div class="arielarea-dropzone" style="display: none;">Drop files here</div> | |
<div class="arielarea-fileitem" style="display: none;"> | |
<div class="arielarea-itemname"></div> | |
<div class="arielarea-removebtn">x</div> | |
</div> | |
</form> | |
<script> | |
(function () { | |
var fi = new arielarea.ui.FileInput(); | |
fi.decorate(goog.dom.getElement("fileinput-sample")); | |
}()); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment