hellojs-xiaotian
Version:
A clientside Javascript library for standardizing requests to OAuth2 web services (and OAuth1 - with a shim)
693 lines (568 loc) • 16 kB
JavaScript
//
// FilePicker
// An extension to Hello.js
if( !("ui" in hello) ){
hello.ui = {};
}
hello.ui.filePicker = function(options, callback){
var prefix = 'fp-',
win = window,
doc = window.document,
x,
i=0;
// Sanitize input vars
if(typeof(options)==='function'){
callback = options;
options = {};
}
var main_el = create('div', {'class':prefix + "main"});
var head_el = create('div', {'class':prefix + "head"}, main_el);
var path_el = create('div', {'class':prefix + "path"}, head_el);
var upload_btn = create('button', {'class':prefix + "upload_btn", "text":'Upload'}, path_el);
var center_el = create('div', {'class':prefix + "center"}, main_el );
var nav_el = create('div', {'class':prefix + "nav"}, center_el);
var body_el = create('div', {'class':prefix + "body"}, center_el);
var select_el = create('ul', {'class':prefix + "local", id:prefix + "local"}, body_el);
var camera_el = create('div', {'class':prefix + "camera",id:prefix + "camera"}, body_el);
var video_el = create('video', {'autoplay':true}, camera_el);
var snap_btn = create('button', {}, camera_el);
var footer_el = create('div', {'class':prefix + "footer"}, main_el);
var info_el = create('span', {'class':prefix + "info", 'text':'none'}, footer_el);
var done_el = create('button', {'class':prefix + "button", 'text':'Done'}, footer_el);
var cancel_el = create('button', {'class':prefix + "cancel", 'text':'Cancel'}, footer_el);
var frame_el;
// Container
if(!options.element){
frame_el = options.element = create('div', {'class':prefix+"container"}, document.body);
addEvent( options.element, 'click', function(e){
if(e.target===options.element){
close();
}
});
}
options.element.appendChild(main_el);
var global_counter = 0,
ref = [],
current_bucket = select_el,
current_network = null,
current_folder = "local",
upl; // Upload input file
// Add Provider buttons
var services = {
"local" : "Local Computer"
};
var networks = hello.init();
for(x in networks) if(networks.hasOwnProperty(x)&&"id" in networks[x]){
switch(x){
case "google":
services[x + ":/me/files"] = "Drive";
services[x + ":/me/albums"] = "Picasa";
break;
case "windows":
services[x + ":/me/files"] = "SkyDrive";
break;
default:
services[x + ":/me/files"] = x.replace(/^\w/, ucword );
}
}
// Camera?
// Does this browser support WebRTC?
if(!win.navigator.getUserMedia){
win.navigator.getUserMedia = (win.navigator.webkitGetUserMedia || win.navigator.mozGetUserMedia || win.navigator.msGetUserMedia);
}
// URL?
if(!window.URL){
window.URL = window.webkitURL;
}
if(window.URL && win.navigator.getUserMedia){
services["camera"] = "Camera";
}
for(x in services){if(services.hasOwnProperty(x)){
var btn = create('button', {
'data-path' : x,
'name' : x.match(/:/) ? services[x] : '',
'html' : services[x],
}, nav_el);
if(x.indexOf(":")>-1){
btn.className = x.replace(/\:.*$/,'');
}
}}
// Add Control to all buttons
var btns = nav_el.children;
for(i=0;i<btns.length;i++){
btns[i].onclick = selectBucket;
}
function selectBucket(){
if(this.className.indexOf(prefix+"selected")===-1){
this.className += ' '+prefix+"selected";
}
if(this.getAttribute('data-path') !== "local"){
upload_btn.style.display = 'none';
}
else{
upload_btn.style.display = 'inline-block';
}
for(var i=0;i<btns.length;i++){
if(btns[i]!==this){
btns[i].className = btns[i].className.replace(prefix+"selected", '');
}
}
getBucket(this.getAttribute('data-path'), this.getAttribute('name'));
}
// Trigger first button
btns[0].onclick();
//
// Buttons
//
info_el.onclick = function(){
btns[0].onclick();
};
// Trigger upload into the current path
addEvent(upload_btn, 'click', function(){
// insert a form control and trigger the native picker
if(!upl){
upl = doc.createElement('input');
upl.type='file';
upl.multiple=true;
var frm = doc.createElement('form');
frm.style.opacity = 0;
frm.style.position = 'absolute';
frm.style.left = '-2000px';
frm.style.top = 0;
frm.appendChild(upl);
doc.body.appendChild(frm);
// listen to the onchange event
// This will carry with it a reference to the file which the user has selected
upl.addEventListener('change', function(e){
if(!(e.target&&e.target.files)){
return;
}
// Pass to our custom function for reading the file
uploadFileList(e.target.files);
},false);
}
// Create and Fire a mouse click event
var clickEvent = doc.createEvent ("MouseEvent");
clickEvent.initMouseEvent ("click", true, true, window, 0,
event.screenX, event.screenY, event.clientX, event.clientY,
event.ctrlKey, event.altKey, event.shiftKey, event.metaKey,
0, null);
// Lets ensure we are focussed on the element for which we are triggering the click event
upl.focus();
upl.dispatchEvent(clickEvent);
});
// Click
cancel_el.onclick = function(){
// Deselect all the selected
for(var i=0;i<ref.length;i++){
// set to false
ref[i].toggleSelect(false);
}
close();
callback({
cancelled : true,
files : []
});
};
done_el.onclick = function(){
var r = {files:[]};
// Find all the selected elements
for(var i=0;i<ref.length;i++){
// set to true
if(ref[i].selected){
r.files.push(ref[i].item);
}
}
// Close
close();
callback(r);
};
if(camera_el){
var video_el = camera_el.getElementsByTagName('video')[0],
snap_el = video_el.nextSibling;
addEvent( video_el, 'click', function(){
win.navigator.getUserMedia({video:true}, function(stream){
// Create Object
video_el.src = win.URL ? win.URL.createObjectURL(stream) : stream;
}, function(e){
console.log(e);
});
});
addEvent(snap_el, 'click', function(){
var blob = elementToBlob(video_el, "snapshot.png");
// Create a new fileRef
var pointer = new fileRef({
name : "snapshot.png",
type : blob.type,
file : blob
}, current_network, snap_el, true);
// }, current_network, current_bucket, true);
ref.push( pointer );
readFile(blob, function(dataURL){
// Update list
pointer.update({
picture : dataURL,
thumbnail : dataURL
});
});
});
}
//
// DRAG and DROP
//
// stop FireFox from replacing the whole page with the file.
main_el.ondragover = function () { main_el.className=prefix+"dragover"; return false; };
main_el.ondragend = main_el.ondragleave = function () { main_el.className=""; return false; };
// Add drop handler
main_el.ondrop = function (e) {
main_el.className="";
e.preventDefault();
e = e || window.event;
var files = (e.files || e.dataTransfer.files);
if(files){
uploadFileList(files);
}
};
// Creates a new Div if one doesn't exist already and loads the resource into the page
function getBucket(path, name, parent){
// Does it already exist?
var id = prefix + path.replace(/\W/g,''),
tab = doc.getElementById(id),
label = doc.getElementById("label"+id);
// Network
var network = path.replace(/\:.*/, '');
// Set current path
if(!label&&name){
label = doc.createElement('a');
label.innerHTML = name;
label.id = "label"+id;
if(parent){
label.setAttribute('data-parent', parent);
}
label.onclick = function(){
getBucket(path, name, parent);
};
path_el.appendChild(label);
}
if(!tab){
// create a new tab
tab = doc.createElement('ul');
tab.id = id;
tab.innerHTML = "<span class='"+prefix+"loading' data-message='Waiting for signin'><span></span></span>";
body_el.appendChild(tab);
// Remove loading
var span = tab.getElementsByTagName('span')[0];
// Login
hello.login(network, function(auth){
if(!auth||auth.error){
span.setAttribute('data-message', "Signin failed, try again");
span.setAttribute('data-animate', false);
span.onclick = function(){
tab.parentNode.removeChild(tab);
getBucket(path, name, parent);
};
return;
}
span.setAttribute('data-message', "Loading files");
// API
hello.api(path, function(r){
span.parentNode.removeChild(span);
// Add files to the tab
for(var i=0;i<r.data.length;i++){
ref.push( new fileRef(r.data[i], network, tab) );
}
});
});
}
// Hide the others
// LISTS
var a = body_el.children;
for(var i=0;i<a.length;i++){
a[i].style.display = "none";
}
// LABELS
a = path_el.getElementsByTagName('a');
for(i=a.length-1;i>=0;--i){
if(parent&&a[i].id === "label"+parent){
//parent = a[i].getAttribute('data-parent');
a[i].style.display = "inline-block";
}
else if(a[i].id === "label"+id){
a[i].style.display = "inline-block";
}
else{
a[i].style.display = "none";
}
}
// Update global
current_folder = path;
current_bucket = tab;
current_network = network;
// show this
tab.style.display = 'block';
// Update the upload btn
upload_btn.innerHTML = name?"Upload to " + name.replace(/^[a-z]/,function(r){return r.toUpperCase();}):'Upload';
}
//
// Each item is stored as a fileRef instance in the ref array
// This function controls the element which it is applied upon.
// Controlling when the elements update for generated content
//
function fileRef(item, network, bucket, selected){
this.els = [];
this.selected = false;
this.item = {};
// Item contents change
this.update = function(item){
if(!item){
return this.item;
}
else {
// Merge the two together
this.item = hello.utils.merge(this.item, item);
}
// Loop through the elements and update their content
for(var i=0;i<this.els.length;i++){
this.els[i].getElementsByTagName('span')[0].innerHTML = this.item.name;
if(this.item.thumbnail){
this.els[i].getElementsByTagName('img')[0].src = this.item.thumbnail;
}
}
};
// Create an element in the given bucket,
// adding on click event handlers
this.create = function(bucket){
var item = this.item;
var self = this;
var el = doc.createElement('li');
el.title = item.name;
el.innerHTML = "<img "+(item.thumbnail?"src='"+item.thumbnail+"'":"")+"/>"
+"<span>"+(item.name||'')+"</span>";
el.onclick = function(){
// Is this a folder?
if(item.type==='folder'||item.type==='album'){
getBucket(network+":"+(item.files?item.files:item.id+"/files"), item.name, bucket.id );
}
else {
// Toggle selected
self.toggleSelect();
}
};
// Add to bucket
bucket.appendChild(el);
// Add to local store
this.els.push(el);
return el;
};
this.toggleSelect = function(bool){
if(bool===this.selected){
// nothing
return;
}
// else change
// toggle
this.selected = !this.selected;
// Selected
if(this.selected){
// Does the element exist within the local bucket
var local = false;
for(i=0;i<this.els.length;i++){
if( this.els[i].parentNode === select_el ){
local=true;
break;
}
}
if(!local){
// create it
this.create(select_el);
}
}
// Loop through the elements and update their content
for(i=0;i<this.els.length;i++){
this.els[i].className = this.selected ? "select" : "";
}
// get the number of selected items in ref and update
info_el.innerHTML = ( this.selected ? ++global_counter : --global_counter );
};
// Create the initial element
this.update(item);
this.create(bucket);
// if this is selected
if(selected){
this.toggleSelect();
}
}
// Upload file list
// Take all the photos in the FileList and create items with them
function uploadFileList(files){
// FileList
if(!("FileList" in window) || (files instanceof File) || (files instanceof Blob)){
// Make an Array
files = [files];
}
var max = 20, len = files.length;
if(len>max){
var bool = confirm("You man only upload "+max+" files at a time");
if(!bool){
return;
}
len = max;
}
// Loop through the array and place the items on the page
for(var i=0;i<len;i++){
createRef(files[i]);
}
}
// Do we upload them to the endpoint? Or load them into the page?
function createRef(file){
// Create a new fileRef
var pointer = new fileRef({
name : file.name,
type : file.type,
file : file
}, current_network, current_bucket, true);
ref.push( pointer );
if(current_folder==="local"){
readFile(file, function(dataURL){
// Update list
pointer.update({
thumbnail : dataURL,
picture : dataURL
});
});
}
else{
// Upload the files to the current directory
hello.api(current_folder, "post", {file: file}, function(r){
// Once the files have been successfully upload lets update the pointer
pointer.update({
id : r.id,
thumbnail : r.source,
picture : r.source
});
});
}
}
// readFile
// Perform one operation at a time, to get a nice gradule load
function readFile(file, callback){
// Run this sequentially
sync(function(){
// Create a new FileReader Object
var reader = new FileReader();
// Set an onload handler because we load files into it Asynchronously
reader.onload = function(e){
// Print onto Canvas
callback( this.result );
// Run the next one;
sync();
};
reader.readAsDataURL(file);
});
}
// Function
// Load
var sync_list = [];
function sync(func){
if(func){
// Add to the end
sync_list.push(func);
}
else{
// remove first one
sync_list.shift();
}
if(sync_list.length===1||(sync_list.length>0&&!func)){
// Remove it and run it.
sync_list[0]();
}
}
function elementToBlob(o,name,type){
// Create a canvas tag
// is canvas
if(o instanceof win.HTMLVideoElement){
var c = doc.createElement('canvas'),
ctx = c.getContext('2d');
c.width = parseInt(video_el.width||640, 10);
c.height = parseInt(video_el.height||480, 10);
ctx.drawImage(o, 0, 0, c.width, c.height);
o = c;
type = "image/jpeg";
}
// is canvas
if(o instanceof win.HTMLCanvasElement){
// turn into DataURL
o = o.toDataURL(type||'image/png');
name = name || "Canvas";
}
var binary;
if(typeof(o)==='string'&&o.match(/^data\:/)){
name = name || "DataURI";
binary = atob(o.split(',')[1]);
type = o.slice(o.indexOf(':')+1, o.indexOf(';') );
}
// Does the browser support the native Blob interface and Typed Arrays'?
if("Blob" in window && "Uint8Array" in window){
// convert the item to a native Blob object.
var a = [];
for(var i = 0; i < binary.length; i++) {
a.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(a)], {type: type});
}
else {
throw "Cannot create blob";
}
}
function close(){
// is this a popup?
win.close();
var container = frame_el || main_el;
// Hide the DOM element
if(container&&container.parentNode){
container.parentNode.removeChild(container);
}
}
function addEvent(obj, eventName, listener) { //function to add event
if (obj.addEventListener) {
obj.addEventListener(eventName, listener, false);
} else {
obj.attachEvent("on" + eventName, listener);
}
}
function remEvent(obj, eventName, listener) { //function to add event
if (obj.removeEventListener) {
obj.removeEventListener(eventName, listener);
} else {
obj.detachEvent("on" + eventName, listener);
}
}
function create(type,attr,parent){
var el = doc.createElement(type);
for(var x in attr){
if(x==='text'||x==='html'){
el.innerHTML = attr[x];
}
else{
el.setAttribute(x,attr[x]);
}
}
if(parent){
parent.appendChild(el);
}
return el;
}
function ucword(m){
return m.toUpperCase();
}
addEvent(doc, "keydown", function self(e){
if(e.keyCode===27){
cancel_el.onclick();
remEvent(doc, "keydown", self);
}
else if (e.keyCode === 13){
done_el.onclick();
remEvent(doc, "keydown", self);
}
});
}