Cable-Desktop/js/views/file_input_view.js
lilia 77caa63321 Normalize views' template fetching pattern
Typically, a view can specify its templateName and then use the default
render method on Whisper.View, except in some special cases like message
view or message detail where other operations are performed during
render.

// FREEBIE
2015-12-09 18:58:52 -08:00

262 lines
9.2 KiB
JavaScript

/*
* vim: ts=4:sw=4:expandtab
*/
(function () {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.FileSizeToast = Whisper.ToastView.extend({
templateName: 'file-size-modal'
});
Whisper.FileInputView = Backbone.View.extend({
tagName: 'span',
className: 'file-input',
initialize: function(options) {
this.$input = this.$('input[type=file]');
this.thumb = new Whisper.AttachmentPreviewView();
this.$el.addClass('file-input');
this.window = options.window;
},
events: {
'change': 'previewImages',
'click .close': 'deleteFiles',
'click .choose-file': 'open',
'drop': 'openDropped',
'dragover': 'showArea',
'dragleave': 'hideArea'
},
open: function() {
// hack
if (this.window && this.window.chrome && this.window.chrome.fileSystem) {
this.window.chrome.fileSystem.chooseEntry({type: 'openFile'}, function(entry) {
if (!entry) {
return;
}
entry.file(function(file) {
this.file = file;
this.previewImages();
}.bind(this));
}.bind(this));
} else {
this.$input.click();
}
},
addThumb: function(src) {
this.$('.avatar').hide();
this.thumb.src = src;
this.$('.attachment-previews').append(this.thumb.render().el);
this.thumb.$('img')[0].onload = function() {
this.$el.trigger('force-resize');
}.bind(this);
},
autoScale: function(file) {
if (file.type.split('/')[0] !== 'image' || file.type === 'image/gif') {
// nothing to do
return Promise.resolve(file);
}
return new Promise(function(resolve, reject) {
var url = URL.createObjectURL(file);
var img = document.createElement('img');
img.onerror = reject;
img.onload = function () {
URL.revokeObjectURL(url);
var maxSize = 420 * 1024;
var maxHeight = 1280;
var maxWidth = 1280;
if (img.width <= maxWidth && img.height <= maxHeight &&
file.size <= maxSize) {
resolve(file);
return;
}
// loadImage.scale -> components/blueimp-load-image
var canvas = loadImage.scale(img, {
canvas: true, maxWidth: maxWidth, maxHeight: maxHeight
});
var quality = 0.95;
var i = 4;
var blob;
do {
i = i - 1;
// dataURLtoBlob -> components/blueimp-canvas-to-blob
blob = dataURLtoBlob(
canvas.toDataURL('image/jpeg', quality)
);
quality = quality * maxSize / blob.size;
if (quality < 50) {
quality = 50;
}
} while (i > 0 && blob.size > maxSize);
resolve(blob);
};
img.src = url;
});
},
previewImages: function() {
this.clearForm();
var file = this.file || this.$input.prop('files')[0];
if (!file) { return; }
var type = file.type.split('/')[0];
switch (type) {
case 'audio': this.addThumb('/images/audio.png'); break;
case 'video': this.addThumb('/images/video.png'); break;
case 'image':
this.oUrl = URL.createObjectURL(file);
this.addThumb(this.oUrl);
break;
}
this.autoScale(file).then(function(blob) {
var limitKb = 1000000;
var blobType = file.type === 'image/gif' ? 'gif' : type;
switch (blobType) {
case 'image':
limitKb = 420; break;
case 'gif':
limitKb = 5000; break;
case 'audio':
limitKb = 100000; break;
case 'video':
limitKb = 100000; break;
}
if ((blob.size/1024).toFixed(4) >= limitKb) {
var units = ['kB','MB','GB'];
var u = -1;
var limit = limitKb * 1000;
do {
limit /= 1000;
++u;
} while (limit >= 1000 && u < units.length - 1);
var toast = new Whisper.FileSizeToast({
model: {limit: limit, units: units[u]}
});
toast.$el.insertAfter(this.$el);
toast.render();
this.deleteFiles();
}
}.bind(this));
},
hasFiles: function() {
var files = this.file ? [this.file] : this.$input.prop('files');
return files && files.length && files.length > 0;
},
getFiles: function() {
var promises = [];
var files = this.file ? [this.file] : this.$input.prop('files');
for (var i = 0; i < files.length; i++) {
promises.push(this.getFile(files[i]));
}
this.clearForm();
return Promise.all(promises);
},
getFile: function(file) {
file = file || this.file || this.$input.prop('files')[0];
if (file === undefined) { return Promise.resolve(); }
return this.autoScale(file).then(this.readFile);
},
getThumbnail: function() {
// Scale and crop an image to 256px square
var size = 256;
var file = this.file || this.$input.prop('files')[0];
if (file === undefined || file.type.split('/')[0] !== 'image' || file.type === 'image/gif') {
// nothing to do
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
var url = URL.createObjectURL(file);
var img = document.createElement('img');
img.onerror = reject;
img.onload = function () {
URL.revokeObjectURL(url);
// loadImage.scale -> components/blueimp-load-image
// scale, then crop.
var canvas = loadImage.scale(img, {
canvas: true, maxWidth: size, maxHeight: size,
cover: true, minWidth: size, minHeight: size
});
canvas = loadImage.scale(canvas, {
canvas: true, maxWidth: size, maxHeight: size,
crop: true, minWidth: size, minHeight: size
});
// dataURLtoBlob -> components/blueimp-canvas-to-blob
var blob = dataURLtoBlob(canvas.toDataURL('image/png'));
resolve(blob);
};
img.src = url;
}).then(this.readFile);
},
readFile: function(file) {
var contentType = file.type;
return new Promise(function(resolve, reject) {
var FR = new FileReader();
FR.onload = function(e) {
resolve({data: e.target.result, contentType: contentType});
};
FR.readAsArrayBuffer(file);
});
},
clearForm: function() {
if (this.oUrl) {
URL.revokeObjectURL(this.oUrl);
this.oUrl = null;
}
this.thumb.remove();
this.$('.avatar').show();
this.$el.trigger('force-resize');
},
deleteFiles: function(e) {
if (e) { e.stopPropagation(); }
this.clearForm();
this.$input.wrap('<form>').parent('form').trigger('reset');
this.$input.unwrap();
this.file = null;
},
openDropped: function(e) {
if (e.originalEvent.dataTransfer.types[0] != 'Files') return;
e.stopPropagation();
e.preventDefault();
this.file = e.originalEvent.dataTransfer.files[0];
this.previewImages();
this.$el.removeClass('dropoff');
},
showArea: function(e) {
if (e.originalEvent.dataTransfer.types[0] != 'Files') return;
e.stopPropagation();
e.preventDefault();
this.$el.addClass('dropoff');
},
hideArea: function(e) {
if (e.originalEvent.dataTransfer.types[0] != 'Files') return;
e.stopPropagation();
e.preventDefault();
this.$el.removeClass('dropoff');
}
});
})();