(function (Flow, window, document, undefined) { 'use strict'; var extend = Flow.extend; var each = Flow.each; function addEvent(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } } function removeEvent(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } function removeElement(element) { element.parentNode.removeChild(element); } function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } /** * Not resumable file upload library, for IE7-IE9 browsers * @name FustyFlow * @param [opts] * @param {bool} [opts.singleFile] * @param {string} [opts.fileParameterName] * @param {Object|Function} [opts.query] * @param {Object} [opts.headers] * @param {string} [opts.target] * @param {Function} [opts.generateUniqueIdentifier] * @param {bool} [opts.matchJSON] * @constructor */ function FustyFlow(opts) { // Shortcut of "r instanceof Flow" this.support = false; this.files = []; this.events = []; this.defaults = { simultaneousUploads: 3, fileParameterName: 'file', query: {}, target: '/', generateUniqueIdentifier: null, matchJSON: false }; var $ = this; this.inputChangeEvent = function (event) { var input = event.target || event.srcElement; removeEvent(input, 'change', $.inputChangeEvent); var newClone = input.cloneNode(false); // change current input with new one input.parentNode.replaceChild(newClone, input); // old input will be attached to hidden form $.addFile(input, event); // reset new input newClone.value = ''; addEvent(newClone, 'change', $.inputChangeEvent); }; this.opts = Flow.extend({}, this.defaults, opts || {}); } FustyFlow.prototype = { on: Flow.prototype.on, off: Flow.prototype.off, fire: Flow.prototype.fire, cancel: Flow.prototype.cancel, assignBrowse: function (domNodes) { if (typeof domNodes.length == 'undefined') { domNodes = [domNodes]; } each(domNodes, function (domNode) { var input; if (domNode.tagName === 'INPUT' && domNode.type === 'file') { input = domNode; } else { input = document.createElement('input'); input.setAttribute('type', 'file'); extend(domNode.style, { display: 'inline-block', position: 'relative', overflow: 'hidden', verticalAlign: 'top' }); extend(input.style, { position: 'absolute', top: 0, right: 0, fontFamily: 'Arial', // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118 fontSize: '118px', margin: 0, padding: 0, opacity: 0, filter: 'alpha(opacity=0)', cursor: 'pointer' }); domNode.appendChild(input); } // When new files are added, simply append them to the overall list addEvent(input, 'change', this.inputChangeEvent); }, this); }, assignDrop: function () { // not supported }, unAssignDrop: function () { // not supported }, isUploading: function () { var uploading = false; each(this.files, function (file) { if (file.isUploading()) { uploading = true; return false; } }); return uploading; }, upload: function () { // Kick off the queue var files = 0; each(this.files, function (file) { if (file.progress() == 1 || file.isPaused()) { return; } if (file.isUploading()) { files++; return; } if (files++ >= this.opts.simultaneousUploads) { return false; } if (files == 1) { this.fire('uploadStart'); } file.send(); }, this); if (!files) { this.fire('complete'); } }, pause: function () { each(this.files, function (file) { file.pause(); }); }, resume: function () { each(this.files, function (file) { file.resume(); }); }, progress: function () { var totalDone = 0; var totalFiles = 0; each(this.files, function (file) { totalDone += file.progress(); totalFiles++; }); return totalFiles > 0 ? totalDone / totalFiles : 0; }, addFiles: function (elementsList, event) { var files = []; each(elementsList, function (element) { // is domElement ? if (element.nodeType === 1 && element.value) { var f = new FustyFlowFile(this, element); if (this.fire('fileAdded', f, event)) { files.push(f); } } }, this); if (this.fire('filesAdded', files, event)) { each(files, function (file) { if (this.opts.singleFile && this.files.length > 0) { this.removeFile(this.files[0]); } this.files.push(file); }, this); } this.fire('filesSubmitted', files, event); }, addFile: function (file, event) { this.addFiles([file], event); }, generateUniqueIdentifier: function (element) { var custom = this.opts.generateUniqueIdentifier; if (typeof custom === 'function') { return custom(element); } return 'xxxxxxxx-xxxx-yxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }, getFromUniqueIdentifier: function (uniqueIdentifier) { var ret = false; each(this.files, function (f) { if (f.uniqueIdentifier == uniqueIdentifier) ret = f; }); return ret; }, removeFile: function (file) { for (var i = this.files.length - 1; i >= 0; i--) { if (this.files[i] === file) { this.files.splice(i, 1); } } }, getSize: function () { // undefined }, timeRemaining: function () { // undefined }, sizeUploaded: function () { // undefined } }; function FustyFlowFile(flowObj, element) { this.flowObj = flowObj; this.element = element; this.name = element.value && element.value.replace(/.*(\/|\\)/, ""); this.relativePath = this.name; this.uniqueIdentifier = flowObj.generateUniqueIdentifier(element); this.iFrame = null; this.finished = false; this.error = false; this.paused = false; var $ = this; this.iFrameLoaded = function (event) { // when we remove iframe from dom // the request stops, but in IE load // event fires if (!$.iFrame || !$.iFrame.parentNode) { return; } $.finished = true; try { // fixing Opera 10.53 if ($.iFrame.contentDocument && $.iFrame.contentDocument.body && $.iFrame.contentDocument.body.innerHTML == "false") { // In Opera event is fired second time // when body.innerHTML changed from false // to server response approx. after 1 sec // when we upload file with iframe return; } } catch (error) { //IE may throw an "access is denied" error when attempting to access contentDocument $.error = true; $.abort(); $.flowObj.fire('fileError', $, error); return; } // iframe.contentWindow.document - for IE<7 var doc = $.iFrame.contentDocument || $.iFrame.contentWindow.document; var innerHtml = doc.body.innerHTML; if ($.flowObj.opts.matchJSON) { innerHtml = /(\{.*\})/.exec(innerHtml)[0]; } $.abort(); $.flowObj.fire('fileSuccess', $, innerHtml); $.flowObj.upload(); }; this.bootstrap(); } FustyFlowFile.prototype = { getExtension: Flow.FlowFile.prototype.getExtension, getType: function () { // undefined }, send: function () { if (this.finished) { return; } var o = this.flowObj.opts; var form = this.createForm(); var params = o.query; if (isFunction(params)) { params = params(this); } params[o.fileParameterName] = this.element; params['flowFilename'] = this.name; params['flowRelativePath'] = this.relativePath; params['flowIdentifier'] = this.uniqueIdentifier; this.addFormParams(form, params); addEvent(this.iFrame, 'load', this.iFrameLoaded); form.submit(); removeElement(form); }, abort: function (noupload) { if (this.iFrame) { this.iFrame.setAttribute('src', 'java' + String.fromCharCode(115) + 'cript:false;'); removeElement(this.iFrame); this.iFrame = null; !noupload && this.flowObj.upload(); } }, cancel: function () { this.flowObj.removeFile(this); this.abort(); }, retry: function () { this.bootstrap(); this.flowObj.upload(); }, bootstrap: function () { this.abort(true); this.finished = false; this.error = false; }, timeRemaining: function () { // undefined }, sizeUploaded: function () { // undefined }, resume: function () { this.paused = false; this.flowObj.upload(); }, pause: function () { this.paused = true; this.abort(); }, isUploading: function () { return this.iFrame !== null; }, isPaused: function () { return this.paused; }, isComplete: function () { return this.progress() === 1; }, progress: function () { if (this.error) { return 1; } return this.finished ? 1 : 0; }, createIframe: function () { var iFrame = (/MSIE (6|7|8)/).test(navigator.userAgent) ? document.createElement('