CaffGeek/MBACNationals

View on GitHub
Web.Admin/z-scripts/ng-file-upload-shim.js

Summary

Maintainability
F
3 wks
Test Coverage
/**!
 * AngularJS file upload/drop directive and service with progress and abort
 * FileAPI Flash shim for old browsers not supporting FormData 
 * @author  Danial  <danial.farid@gmail.com>
 * @version 4.2.1
 */

(function() {

var hasFlash = function() {
    try {
      var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
      if (fo) return true;
    } catch(e) {
      if (navigator.mimeTypes['application/x-shockwave-flash'] != undefined) return true;
    }
    return false;
}

function patchXHR(fnName, newFn) {
    window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
};

if ((window.XMLHttpRequest && !window.FormData) || (window.FileAPI && FileAPI.forceLoad)) {
    var initializeUploadListener = function(xhr) {
        if (!xhr.__listeners) {
            if (!xhr.upload) xhr.upload = {};
            xhr.__listeners = [];
            var origAddEventListener = xhr.upload.addEventListener;
            xhr.upload.addEventListener = function(t, fn, b) {
                xhr.__listeners[t] = fn;
                origAddEventListener && origAddEventListener.apply(this, arguments);
            };
        }
    }
    
    patchXHR('open', function(orig) {
        return function(m, url, b) {
            initializeUploadListener(this);
            this.__url = url;
            try {
                orig.apply(this, [m, url, b]);
            } catch (e) {
                if (e.message.indexOf('Access is denied') > -1) {
                    this.__origError = e;
                    orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]);
                }
            }
        }
    });

    patchXHR('getResponseHeader', function(orig) {
        return function(h) {
            return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h]));
        };
    });

    patchXHR('getAllResponseHeaders', function(orig) {
        return function() {
            return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this));
        }
    });

    patchXHR('abort', function(orig) {
        return function() {
            return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this));
        }
    });

    patchXHR('setRequestHeader', function(orig) {
        return function(header, value) {
            if (header === '__setXHR_') {
                initializeUploadListener(this);
                var val = value(this);
                // fix for angular < 1.2.0
                if (val instanceof Function) {
                    val(this);
                }
            } else {
                this.__requestHeaders = this.__requestHeaders || {};
                this.__requestHeaders[header] = value;
                orig.apply(this, arguments);
            }
        }
    });
    
    function redefineProp(xhr, prop, fn) {
        try {
            Object.defineProperty(xhr, prop, {get: fn});
        } catch (e) {/*ignore*/}
    }

    patchXHR('send', function(orig) {
        return function() {
            var xhr = this;
            if (arguments[0] && arguments[0].__isFileAPIShim) {
                var formData = arguments[0];
                var config = {
                    url: xhr.__url,
                    jsonp: false, //removes the callback form param
                    cache: true, //removes the ?fileapiXXX in the url
                    complete: function(err, fileApiXHR) {
                        xhr.__completed = true;
                        if (!err && xhr.__listeners['load']) 
                            xhr.__listeners['load']({type: 'load', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
                        if (!err && xhr.__listeners['loadend']) 
                            xhr.__listeners['loadend']({type: 'loadend', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
                        if (err === 'abort' && xhr.__listeners['abort']) 
                            xhr.__listeners['abort']({type: 'abort', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true});
                        if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function() {return (fileApiXHR.status == 0 && err && err !== 'abort') ? 500 : fileApiXHR.status});
                        if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function() {return fileApiXHR.statusText});
                        redefineProp(xhr, 'readyState', function() {return 4});
                        if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function() {return fileApiXHR.response});
                        var resp = fileApiXHR.responseText || (err && fileApiXHR.status == 0 && err !== 'abort' ? err : undefined);
                        redefineProp(xhr, 'responseText', function() {return resp});
                        redefineProp(xhr, 'response', function() {return resp});
                        if (err) redefineProp(xhr, 'err', function() {return err});
                        xhr.__fileApiXHR = fileApiXHR;
                        if (xhr.onreadystatechange) xhr.onreadystatechange();
                        if (xhr.onload) xhr.onload();
                    },
                    progress: function(e) {
                        e.target = xhr;
                        xhr.__listeners['progress'] && xhr.__listeners['progress'](e);
                        xhr.__total = e.total;
                        xhr.__loaded = e.loaded;
                        if (e.total === e.loaded) {
                            // fix flash issue that doesn't call complete if there is no response text from the server  
                            var _this = this
                            setTimeout(function() {
                                if (!xhr.__completed) {
                                    xhr.getAllResponseHeaders = function(){};
                                    _this.complete(null, {status: 204, statusText: 'No Content'});
                                }
                            }, FileAPI.noContentTimeout || 10000);
                        }
                    },
                    headers: xhr.__requestHeaders
                }
                config.data = {};
                config.files = {}
                for (var i = 0; i < formData.data.length; i++) {
                    var item = formData.data[i];
                    if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) {
                        config.files[item.key] = item.val;
                    } else {
                        config.data[item.key] = item.val;
                    }
                }

                setTimeout(function() {
                    if (!hasFlash()) {
                        throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
                    }
                    xhr.__fileApiXHR = FileAPI.upload(config);
                }, 1);
            } else {
                if (this.__origError) {
                    throw this.__origError;
                }
                orig.apply(xhr, arguments);
            }
        }
    });
    window.XMLHttpRequest.__isFileAPIShim = true;

    function isInputTypeFile(elem) {
        return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file';
    }
    
    window.FormData = FormData = function() {
        return {
            append: function(key, val, name) {
                if (val.__isFileAPIBlobShim) {
                    val = val.data[0];
                }
                this.data.push({
                    key: key,
                    val: val,
                    name: name
                });
            },
            data: [],
            __isFileAPIShim: true
        };
    };

    window.Blob = Blob = function(b) {
        return {
            data: b,
            __isFileAPIBlobShim: true
        };
    };

    (function () {
        //load FileAPI
        if (!window.FileAPI) {
            window.FileAPI = {};
        }
        if (FileAPI.forceLoad) {
            FileAPI.html5 = false;
        }
        
        if (!FileAPI.upload) {
            var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src;
            if (window.FileAPI.jsUrl) {
                jsUrl = window.FileAPI.jsUrl;
            } else if (window.FileAPI.jsPath) {
                basePath = window.FileAPI.jsPath;
            } else {
                for (i = 0; i < allScripts.length; i++) {
                    src = allScripts[i].src;
                    index = src.search(/\/ng\-file\-upload[\-a-zA-z0-9\.]*\.js/)
                    if (index > -1) {
                        basePath = src.substring(0, index + 1);
                        break;
                    }
                }
            }

            if (FileAPI.staticPath == null) FileAPI.staticPath = basePath;
            script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js');
            document.getElementsByTagName('head')[0].appendChild(script);
            FileAPI.hasFlash = hasFlash();
        }
    })();
    
    FileAPI.ngfFixIE = function(elem, createFileElemFn, bindAttr, changeFn, resetModel) {
        if (!hasFlash()) {
            throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
        }
        var makeFlashInput = function(evt) {
            if (elem.attr('disabled')) {
                elem.__ngf_elem__.removeClass('js-fileapi-wrapper');
            } else {
                var fileElem = elem.__ngf_elem__;
                if (!fileElem) {
                    fileElem = elem.__ngf_elem__ = createFileElemFn();
                    fileElem.addClass('js-fileapi-wrapper');
                    if (!isInputTypeFile(elem)) {
//                        if (fileElem.parent().css('position') === '' || fileElem.parent().css('position') === 'static') {
//                            fileElem.parent().css('position', 'relative');
//                        }
//                        elem.parent()[0].insertBefore(fileElem[0], elem[0]);
//                        elem.css('overflow', 'hidden');
                    }
                    setTimeout(function() {
                        fileElem.bind('mouseenter', makeFlashInput);
                    }, 10);
                    fileElem.bind('change', function(evt) {
                        fileApiChangeFn.apply(this, [evt]);
                        changeFn.apply(this, [evt]);
//                        alert('change' +  evt);
                    });
                } else {
                    bindAttr(elem.__ngf_elem__);
                }
                if (!isInputTypeFile(elem)) {
                    fileElem.css('position', 'absolute')
                            .css('top', getOffset(elem[0]).top + 'px').css('left', getOffset(elem[0]).left + 'px')
                            .css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px')
                            .css('filter', 'alpha(opacity=0)').css('display', elem.css('display'))
                            .css('overflow', 'hidden').css('z-index', '900000')
                            .css('visibility', 'visible');
                }
            }
            function getOffset(obj) {
                var left, top;
                left = top = 0;
                if (obj.offsetParent) {
                    do {
                        left += obj.offsetLeft;
                        top  += obj.offsetTop;
                    } while (obj = obj.offsetParent);
                }
                return {
                    left : left,
                    top : top
                };
            };
        };

        elem.bind('mouseenter', makeFlashInput);

        var fileApiChangeFn = function(evt) {
            var files = FileAPI.getFiles(evt);
            //just a double check for #233
            for (var i = 0; i < files.length; i++) {
                if (files[i].size === undefined) files[i].size = 0;
                if (files[i].name === undefined) files[i].name = 'file';
                if (files[i].type === undefined) files[i].type = 'undefined';
            }
            if (!evt.target) {
                evt.target = {};
            }
            evt.target.files = files;
            // if evt.target.files is not writable use helper field
            if (evt.target.files != files) {
                evt.__files_ = files;
            }
            (evt.__files_ || evt.target.files).item = function(i) {
                return (evt.__files_ || evt.target.files)[i] || null;
            };
        };
    };

    FileAPI.disableFileInput = function(elem, disable) {
        if (disable) {
            elem.removeClass('js-fileapi-wrapper')
        } else {
            elem.addClass('js-fileapi-wrapper');
        }
    };
}


if (!window.FileReader) {
    window.FileReader = function() {
        var _this = this, loadStarted = false;
        this.listeners = {};
        this.addEventListener = function(type, fn) {
            _this.listeners[type] = _this.listeners[type] || [];
            _this.listeners[type].push(fn);
        };
        this.removeEventListener = function(type, fn) {
            _this.listeners[type] && _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1);
        };
        this.dispatchEvent = function(evt) {
            var list = _this.listeners[evt.type];
            if (list) {
                for (var i = 0; i < list.length; i++) {
                    list[i].call(_this, evt);
                }
            }
        };
        this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null;

        var constructEvent = function(type, evt) {
            var e = {type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error};
            if (evt.result != null) e.target.result = evt.result;
            return e;
        };
        var listener = function(evt) {
            if (!loadStarted) {
                loadStarted = true;
                _this.onloadstart && _this.onloadstart(constructEvent('loadstart', evt));
            }
            if (evt.type === 'load') {
                _this.onloadend && _this.onloadend(constructEvent('loadend', evt));
                var e = constructEvent('load', evt);
                _this.onload && _this.onload(e);
                _this.dispatchEvent(e);
            } else if (evt.type === 'progress') {
                var e = constructEvent('progress', evt);
                _this.onprogress && _this.onprogress(e);
                _this.dispatchEvent(e);
            } else {
                var e = constructEvent('error', evt);
                _this.onerror && _this.onerror(e);
                _this.dispatchEvent(e);
            }
        };
        this.readAsArrayBuffer = function(file) {
            FileAPI.readAsBinaryString(file, listener);
        }
        this.readAsBinaryString = function(file) {
            FileAPI.readAsBinaryString(file, listener);
        }
        this.readAsDataURL = function(file) {
            FileAPI.readAsDataURL(file, listener);
        }
        this.readAsText = function(file) {
            FileAPI.readAsText(file, listener);
        }
    }
}
})();