'use strict'; /*@ Ox.FileInput File Input options Options disabled If true, the element is disabled maxFiles Maximum number of files (or -1 for unlimited) maxLines Maximum number of lines to display (or -1 for unlimited) maxSize Maximum total file size in bytes (or -1 for unlimited) value Value (array of file objects) width Width in px self Shared private variable ([options[, self]]) -> File Input change change @*/ Ox.FileInput = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ disabled: false, maxFiles: -1, maxLines: -1, maxSize: -1, value: [], width: 256 }) .options(options || {}) .update({ disabled: function() { that[self.options.disabled ? 'addClass' : 'removeClass']('OxDisabled'); self.$button.options({disabled: self.options.disabled}); self.$input && self.$input[self.options.disabled ? 'hide' : 'show'](); } }) .addClass('OxFileInput' + (self.options.disabled ? ' OxDisabled' : '')) .css({width: self.options.width + 'px'}); self.multiple = self.options.maxFiles != 1; self.size = getSize(); self.$bar = Ox.Bar({size: 14}) .css( Ox.extend({ width: self.options.width - 2 + 'px' }, self.multiple && self.options.value.length ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 } : {}) ) .appendTo(that); self.$title = $('
') .css({ float: 'left', width: self.options.width - 102 + 'px', paddingLeft: '4px', overflow: 'hidden', textOverflow: 'ellipsis' }) .html(getTitleText()) .appendTo(self.$bar); self.$size = $('
') .css({ float: 'left', width: '64px', height: '14px', paddingRight: '16px', textAlign: 'right' }) .html(getSizeText()) .appendTo(self.$bar); self.$button = Ox.Button({ disabled: self.options.disabled, style: 'symbol', title: self.multiple || self.options.value.length == 0 ? 'add' : 'close', type: 'image' }) .attr({ title: self.multiple || self.options.value.length == 0 ? '' : 'Clear' }) .css({ float: 'left', marginTop: '-1px' }) .bindEvent({ click: clearFile }) .appendTo(self.$bar); if (self.multiple || self.options.value.length == 0) { self.$input = renderInput(); self.options.disabled && self.$input.hide(); } if (self.multiple) { self.$files = $('
') .addClass('OxFiles') .css({ width: self.options.width - 2 + 'px', height: getHeight() }) .appendTo(that); self.options.value.length == 0 && self.$files.hide(); self.$list = Ox.TableList({ columns: [ { id: 'name', visible: true, width: self.options.width - 94 }, { align: 'right', format: function(value) { return Ox.formatValue(value, 'B'); }, id: 'size', visible: true, width: 64 }, { align: 'right', format: function(value, data) { return Ox.Button({ style: 'symbol', title: 'close', type: 'image' }) .attr({title: 'Remove File'}) .css({margin: '-1px -4px 0 0'}) .bindEvent({ click: function() { self.$list.options({selected: [value]}); removeFiles([value]); } }); }, id: 'id', unique: true, visible: true, width: 28 } ], items: getItems(), sort: [{key: 'name', operator: '+'}] }) .css({ left: 0, top: 0, width: self.options.width - 2 + 'px', height: '64px' }) .bindEvent({ 'delete': function(data) { removeFiles(data.ids); } }) .appendTo(self.$files); } function addFiles(e) { var filelist = e.target.files, files = []; Ox.loop(filelist.length, function(i) { files.push(filelist.item(i)); }); files.sort(self.options.maxSize == -1 ? function(a, b) { a = a.name.toLowerCase(); b = b.name.toLowerCase(); return a < b ? -1 : a > b ? 1 : 0; } : function(a, b) { // if there is a max size, // try to add small files first return a.size - b.size; }).forEach(function(file) { if (!exists(file) && ( self.options.maxFiles == -1 || self.options.value.length < self.options.maxFiles ) && ( self.options.maxSize == -1 || self.size + file.size < self.options.maxSize )) { self.options.value.push(file); self.size += file.size; } }); self.$title.html(getTitleText()); self.$size.html(getSizeText()); if (self.multiple) { self.$bar.css({ borderBottomLeftRadius: 0, borderBottomRightRadius: 1 }); self.$files.css({height: getHeight()}).show(); self.$list.options({items: getItems()}); if ( self.options.value.length == self.options.maxFiles || self.size == self.options.maxSize ) { self.$button.options({disabled: true}); } self.$input = renderInput(); } else { self.$button.options({title: 'close'}).attr({title: 'Clear'}); self.$input.remove(); } that.triggerEvent('change', {value: self.options.value}); } function clearFile() { self.options.value = []; self.size = 0; self.$title.html(getTitleText()); self.$size.html(getSizeText()); self.$button.options({title: 'add'}).attr({title: ''}); self.$input = renderInput(); that.triggerEvent('change', {value: self.options.value}); } function exists(file) { return self.options.value.some(function(f) { return f.name == file.name && f.size == file.size && Ox.isEqual(f.lastModifiedDate, file.lastModifiedDate); }); } function getHeight() { return ( self.options.maxLines == -1 ? self.options.value.length : Math.min(self.options.value.length, self.options.maxLines) ) * 16 + 'px'; } function getItems() { return self.options.value.map(function(file, i) { return {name: file.name, size: file.size, id: i}; }); } function getSize() { return self.options.value.reduce(function(prev, curr) { return prev + curr.size; }, 0); } function getSizeText() { return self.size ? Ox.formatValue(self.size, 'B') : ''; } function getTitleText() { var length = self.options.value.length return length == 0 ? 'No file' + (self.multiple ? 's' : '') + ' selected' : self.multiple ? length + ' file' + (length == 1 ? '' : 's') : self.options.value[0].name; } function removeFiles(ids) { self.options.value = self.options.value.filter(function(v, i) { return ids.indexOf(i) == -1; }); self.size = getSize(); if (self.options.value.length == 0) { self.$bar.css({ borderBottomLeftRadius: '8px', borderBottomRightRadius: '8px' }); self.$files.hide(); } self.$title.html(getTitleText()); self.$size.html(getSizeText()); self.$list.options({items: getItems(), selected: []}); self.$files.css({height: getHeight()}); that.triggerEvent('change', {value: self.options.value}); } function renderInput() { self.$input && self.$input.remove(); return $('') .attr( Ox.extend({ title: self.multiple ? 'Add Files' : 'Select File', type: 'file' }, self.multiple ? { multiple: true } : {}) ) .css({ float: 'left', width: '16px', height: '14px', margin: '-1px -7px 0 -16px', opacity: 0 }) .on({ change: addFiles }) .appendTo(self.$bar); } return that; };