|
|
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ !function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each(
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</tex return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h
/** * http://github.com/valums/file-uploader
* * Multiple file upload component with progress-bar, drag-and-drop. * © 2010 Andrew Valums ( andrew(at)valums.com ) * * Licensed under GNU GPL 2 or later and GNU LGPL 2 or later, see license.txt. */
//
// Helper functions
//
var qq = qq || {};
/** * Adds all missing properties from second obj to first obj */ qq.extend = function(first, second){ for (var prop in second){ first[prop] = second[prop]; } };
/** * Searches for a given element in the array, returns -1 if it is not present. * @param {Number} [from] The index at which to begin the search */ qq.indexOf = function(arr, elt, from){ if (arr.indexOf) return arr.indexOf(elt, from); from = from || 0; var len = arr.length; if (from < 0) from += len;
for (; from < len; from++){ if (from in arr && arr[from] === elt){ return from; } } return -1; }; qq.getUniqueId = (function(){ var id = 0; return function(){ return id++; }; })();
//
// Events
qq.attach = function(element, type, fn){ if (element.addEventListener){ element.addEventListener(type, fn, false); } else if (element.attachEvent){ element.attachEvent('on' + type, fn); } }; qq.detach = function(element, type, fn){ if (element.removeEventListener){ element.removeEventListener(type, fn, false); } else if (element.attachEvent){ element.detachEvent('on' + type, fn); } };
qq.preventDefault = function(e){ if (e.preventDefault){ e.preventDefault(); } else{ e.returnValue = false; } };
//
// Node manipulations
/** * Insert node a before node b. */ qq.insertBefore = function(a, b){ b.parentNode.insertBefore(a, b); }; qq.remove = function(element){ element.parentNode.removeChild(element); };
qq.contains = function(parent, descendant){ // compareposition returns false in this case
if (parent == descendant) return true; if (parent.contains){ return parent.contains(descendant); } else { return !!(descendant.compareDocumentPosition(parent) & 8); } };
/** * Creates and returns element from html string * Uses innerHTML to create an element */ qq.toElement = (function(){ var div = document.createElement('div'); return function(html){ div.innerHTML = html; var element = div.firstChild; div.removeChild(element); return element; }; })();
//
// Node properties and attributes
/** * Sets styles for an element. * Fixes opacity in IE6-8. */ qq.css = function(element, styles){ if (styles.opacity != null){ if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){ styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')'; } } qq.extend(element.style, styles); }; qq.hasClass = function(element, name){ var re = new RegExp('(^| )' + name + '( |$)'); return re.test(element.className); }; qq.addClass = function(element, name){ if (!qq.hasClass(element, name)){ element.className += ' ' + name; } }; qq.removeClass = function(element, name){ var re = new RegExp('(^| )' + name + '( |$)'); element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, ""); }; qq.setText = function(element, text){ element.innerText = text; element.textContent = text; };
//
// Selecting elements
qq.children = function(element){ var children = [], child = element.firstChild;
while (child){ if (child.nodeType == 1){ children.push(child); } child = child.nextSibling; }
return children; };
qq.getByClass = function(element, className){ if (element.querySelectorAll){ return element.querySelectorAll('.' + className); }
var result = []; var candidates = element.getElementsByTagName("*"); var len = candidates.length;
for (var i = 0; i < len; i++){ if (qq.hasClass(candidates[i], className)){ result.push(candidates[i]); } } return result; };
/** * obj2url() takes a json-object as argument and generates * a querystring. pretty much like jQuery.param() * * how to use: * * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');` * * will result in: * * `http://any.url/upload?otherParam=value&a=b&c=d` * * @param Object JSON-Object * @param String current querystring-part * @return String encoded querystring */ qq.obj2url = function(obj, temp, prefixDone){ var uristrings = [], prefix = '&', add = function(nextObj, i){ var nextTemp = temp ? (/\[\]$/.test(temp)) // prevent double-encoding
? temp : temp+'['+i+']' : i; if ((nextTemp != 'undefined') && (i != 'undefined')) { uristrings.push( (typeof nextObj === 'object') ? qq.obj2url(nextObj, nextTemp, true) : (Object.prototype.toString.call(nextObj) === '[object Function]') ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj()) : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj) ); } };
if (!prefixDone && temp) { prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?'; uristrings.push(temp); uristrings.push(qq.obj2url(obj)); } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) { // we wont use a for-in-loop on an array (performance)
for (var i = 0, len = obj.length; i < len; ++i){ add(obj[i], i); } } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){ // for anything else but a scalar, we will use for-in-loop
for (var i in obj){ add(obj[i], i); } } else { uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj)); }
return uristrings.join(prefix) .replace(/^&/, '') .replace(/%20/g, '+'); };
//
//
// Uploader Classes
//
//
var qq = qq || {}; /** * Creates upload button, validates upload, but doesn't create file list or dd. */ qq.FileUploaderBasic = function(o){ this._options = { // set to true to see the server response
debug: false, action: '/upload', params: {}, button: null, multiple: true, maxConnections: 3, // validation
allowedExtensions: [], sizeLimit: 2147483648, minSizeLimit: 0, // events
// return false to cancel submit
onSubmit: function(id, fileName){ document.getElementById("toggleme").style.display = "block";}, onProgress: function(id, fileName, loaded, total){}, onComplete: function(id, fileName, responseJSON){}, onCancel: function(id, fileName){}, // messages
messages: { typeError: "{file} has invalid extension. Only {extensions} are allowed.", //sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
sizeError: "test", minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.", emptyError: "{file} is empty, please select files again without it.", onLeave: "The files are being uploaded, if you leave now the upload will be cancelled." }, showMessage: function(message){ if (message == "test") {
if (confirm("Uploading a large file, redirecting to a stable uploader. \nCancel if you currently have files already uploaded.")) { document.getElementById("html5").style.display = "none"; document.getElementById("flash").style.display = "block";
//window.location = "/stable";
} } else { alert(message); } } }; qq.extend(this._options, o); // number of files being uploaded
this._filesInProgress = 0; this._handler = this._createUploadHandler(); if (this._options.button){ this._button = this._createUploadButton(this._options.button); } this._preventLeaveInProgress(); }; qq.FileUploaderBasic.prototype = { setParams: function(params){ this._options.params = params; }, getInProgress: function(){ return this._filesInProgress; }, _createUploadButton: function(element){ var self = this; return new qq.UploadButton({ element: element, multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(), onChange: function(input){ self._onInputChange(input); } }); }, _createUploadHandler: function(){ var self = this, handlerClass; if(qq.UploadHandlerXhr.isSupported()){ handlerClass = 'UploadHandlerXhr'; } else { handlerClass = 'UploadHandlerForm'; }
var handler = new qq[handlerClass]({ debug: this._options.debug, action: this._options.action, maxConnections: this._options.maxConnections, onProgress: function(id, fileName, loaded, total){ self._onProgress(id, fileName, loaded, total); self._options.onProgress(id, fileName, loaded, total); }, onComplete: function(id, fileName, result){ self._onComplete(id, fileName, result); self._options.onComplete(id, fileName, result); }, onCancel: function(id, fileName){ self._onCancel(id, fileName); self._options.onCancel(id, fileName); } });
return handler; }, _preventLeaveInProgress: function(){ var self = this; qq.attach(window, 'beforeunload', function(e){ if (!self._filesInProgress){return;} var e = e || window.event; // for ie, ff
e.returnValue = self._options.messages.onLeave; // for webkit
return self._options.messages.onLeave; }); }, _onSubmit: function(id, fileName){ this._filesInProgress++; }, _onProgress: function(id, fileName, loaded, total){ }, _onComplete: function(id, fileName, result){ this._filesInProgress--; if (result.error){ this._options.showMessage(result.error); } }, _onCancel: function(id, fileName){ this._filesInProgress--; }, _onInputChange: function(input){ if (this._handler instanceof qq.UploadHandlerXhr){ this._uploadFileList(input.files); } else { if (this._validateFile(input)){ this._uploadFile(input); } } this._button.reset(); }, _uploadFileList: function(files){ for (var i=0; i<files.length; i++){ if ( !this._validateFile(files[i])){ return; } } for (var i=0; i<files.length; i++){ this._uploadFile(files[i]); } }, _uploadFile: function(fileContainer){ var id = this._handler.add(fileContainer); var fileName = this._handler.getName(id); if (this._options.onSubmit(id, fileName) !== false){ this._onSubmit(id, fileName); this._handler.upload(id, this._options.params); } }, _validateFile: function(file){ var name, size; if (file.value){ // it is a file input
// get input value and remove path to normalize
name = file.value.replace(/.*(\/|\\)/, ""); } else { // fix missing properties in Safari
name = file.fileName != null ? file.fileName : file.name; size = file.fileSize != null ? file.fileSize : file.size; } if (! this._isAllowedExtension(name)){ this._error('typeError', name); return false; } else if (size === 0){ this._error('emptyError', name); return false; } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){ this._error('sizeError', name); return false; } else if (size && size < this._options.minSizeLimit){ this._error('minSizeError', name); return false; } return true; }, _error: function(code, fileName){ var message = this._options.messages[code]; function r(name, replacement){ message = message.replace(name, replacement); } r('{file}', this._formatFileName(fileName)); r('{extensions}', this._options.allowedExtensions.join(', ')); r('{sizeLimit}', this._formatSize(this._options.sizeLimit)); r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit)); this._options.showMessage(message); }, _formatFileName: function(name){ if (name.length > 33){ name = name.slice(0, 19) + '...' + name.slice(-13); } return name; }, _isAllowedExtension: function(fileName){ var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : ''; var allowed = this._options.allowedExtensions; if (!allowed.length){return true;} for (var i=0; i<allowed.length; i++){ if (allowed[i].toLowerCase() == ext){ return true;} } return false; }, _formatSize: function(bytes){ var i = -1; do { bytes = bytes / 1024; i++; } while (bytes > 99); return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i]; } }; /** * Class that creates upload widget with drag-and-drop and file list * @inherits qq.FileUploaderBasic */ qq.FileUploader = function(o){ // call parent constructor
qq.FileUploaderBasic.apply(this, arguments); // additional options
qq.extend(this._options, { element: null, // if set, will be used instead of qq-upload-list in template
listElement: null, template: '<div class="qq-uploader">' + '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' + '<div class="qq-upload-button">Click or Drop file(s)</div>' +
'<div id="toggleme" class="qq-upload-list"></div>' +
'</div>',
// template for one item in file list
fileTemplate: '<li>' + '<span class="qq-upload-file"></span>' + '<span class="qq-upload-spinner"></span>' + '<span class="qq-upload-size"></span>' + '<a class="qq-upload-cancel" href="#">Cancel</a>' + '<span class="qq-upload-failed-text">Failed</span>' + '</li>', classes: { // used to get elements from templates
button: 'qq-upload-button', drop: 'qq-upload-drop-area', dropActive: 'qq-upload-drop-area-active', list: 'qq-upload-list', file: 'qq-upload-file', spinner: 'qq-upload-spinner', size: 'qq-upload-size', cancel: 'qq-upload-cancel',
// added to list item when upload completes
// used in css to hide progress spinner
success: 'qq-upload-success', fail: 'qq-upload-fail' } }); // overwrite options with user supplied
qq.extend(this._options, o);
this._element = this._options.element; this._element.innerHTML = this._options.template; this._listElement = this._options.listElement || this._find(this._element, 'list'); this._classes = this._options.classes; this._button = this._createUploadButton(this._find(this._element, 'button')); this._bindCancelEvent(); this._setupDragDrop(); };
// inherit from Basic Uploader
qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype);
qq.extend(qq.FileUploader.prototype, { /** * Gets one of the elements listed in this._options.classes **/ _find: function(parent, type){ var element = qq.getByClass(parent, this._options.classes[type])[0]; if (!element){ throw new Error('element not found ' + type); } return element; }, _setupDragDrop: function(){ var self = this, dropArea = this._find(this._element, 'drop');
var dz = new qq.UploadDropZone({ element: dropArea, onEnter: function(e){ qq.addClass(dropArea, self._classes.dropActive); e.stopPropagation(); }, onLeave: function(e){ e.stopPropagation(); }, onLeaveNotDescendants: function(e){ qq.removeClass(dropArea, self._classes.dropActive); }, onDrop: function(e){ dropArea.style.display = 'none'; qq.removeClass(dropArea, self._classes.dropActive); self._uploadFileList(e.dataTransfer.files); } }); dropArea.style.display = 'none';
qq.attach(document, 'dragenter', function(e){ if (!dz._isValidFileDrag(e)) return; dropArea.style.display = 'block'; }); qq.attach(document, 'dragleave', function(e){ if (!dz._isValidFileDrag(e)) return; var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); // only fire when leaving document out
if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){ dropArea.style.display = 'none'; } }); }, _onSubmit: function(id, fileName){ qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments); this._addToList(id, fileName); }, _onProgress: function(id, fileName, loaded, total){ qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
var item = this._getItemByFileId(id); var size = this._find(item, 'size'); size.style.display = 'inline'; var text; if (loaded != total){ text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total); } else { text = this._formatSize(total); } qq.setText(size, text); }, _onComplete: function(id, fileName, result){ qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
// mark completed
var item = this._getItemByFileId(id); qq.remove(this._find(item, 'cancel')); qq.remove(this._find(item, 'spinner')); if (result){ qq.addClass(item, this._classes.success); item.innerHTML = '<a target="_blank" href="' + result.url + '">' + result.url + '</a> <br />' } else { qq.addClass(item, this._classes.fail); } }, _addToList: function(id, fileName){ var item = qq.toElement(this._options.fileTemplate); item.qqFileId = id;
var fileElement = this._find(item, 'file'); qq.setText(fileElement, this._formatFileName(fileName)); this._find(item, 'size').style.display = 'none';
this._listElement.appendChild(item); }, _getItemByFileId: function(id){ var item = this._listElement.firstChild; // there can't be txt nodes in dynamically created list
// and we can use nextSibling
while (item){ if (item.qqFileId == id) return item; item = item.nextSibling; } }, /** * delegate click event for cancel link **/ _bindCancelEvent: function(){ var self = this, list = this._listElement; qq.attach(list, 'click', function(e){ e = e || window.event; var target = e.target || e.srcElement; if (qq.hasClass(target, self._classes.cancel)){ qq.preventDefault(e); var item = target.parentNode; self._handler.cancel(item.qqFileId); qq.remove(item); } }); } }); qq.UploadDropZone = function(o){ this._options = { element: null, onEnter: function(e){}, onLeave: function(e){}, // is not fired when leaving element by hovering descendants
onLeaveNotDescendants: function(e){}, onDrop: function(e){} }; qq.extend(this._options, o); this._element = this._options.element; this._disableDropOutside(); this._attachEvents(); };
qq.UploadDropZone.prototype = { _disableDropOutside: function(e){ // run only once for all instances
if (!qq.UploadDropZone.dropOutsideDisabled ){
qq.attach(document, 'dragover', function(e){ if (e.dataTransfer){ e.dataTransfer.dropEffect = 'none'; e.preventDefault(); } }); qq.UploadDropZone.dropOutsideDisabled = true; } }, _attachEvents: function(){ var self = this; qq.attach(self._element, 'dragover', function(e){ if (!self._isValidFileDrag(e)) return; var effect = e.dataTransfer.effectAllowed; if (effect == 'move' || effect == 'linkMove'){ e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
} else { e.dataTransfer.dropEffect = 'copy'; // for Chrome
} e.stopPropagation(); e.preventDefault(); }); qq.attach(self._element, 'dragenter', function(e){ if (!self._isValidFileDrag(e)) return; self._options.onEnter(e); }); qq.attach(self._element, 'dragleave', function(e){ if (!self._isValidFileDrag(e)) return; self._options.onLeave(e); var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); // do not fire when moving a mouse over a descendant
if (qq.contains(this, relatedTarget)) return; self._options.onLeaveNotDescendants(e); }); qq.attach(self._element, 'drop', function(e){ if (!self._isValidFileDrag(e)) return; e.preventDefault(); self._options.onDrop(e); }); }, _isValidFileDrag: function(e){ var dt = e.dataTransfer, // do not check dt.types.contains in webkit, because it crashes safari 4
isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
// dt.effectAllowed is none in Safari 5
// dt.types.contains check is for firefox
return dt && dt.effectAllowed != 'none' && (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files'))); } };
qq.UploadButton = function(o){ this._options = { element: null, // if set to true adds multiple attribute to file input
multiple: false, // name attribute of file input
name: 'file', onChange: function(input){}, hoverClass: 'qq-upload-button-hover', focusClass: 'qq-upload-button-focus' }; qq.extend(this._options, o); this._element = this._options.element; // make button suitable container for input
qq.css(this._element, { position: 'relative', overflow: 'hidden', // Make sure browse button is in the right side
// in Internet Explorer
direction: 'ltr' }); this._input = this._createInput(); };
qq.UploadButton.prototype = { /* returns file input element */ getInput: function(){ return this._input; }, /* cleans/recreates the file input */ reset: function(){ if (this._input.parentNode){ qq.remove(this._input); } qq.removeClass(this._element, this._options.focusClass); this._input = this._createInput(); }, _createInput: function(){ var input = document.createElement("input"); if (this._options.multiple){ input.setAttribute("multiple", "multiple"); } input.setAttribute("type", "file"); input.setAttribute("name", this._options.name); qq.css(input, { position: 'absolute', // in Opera only 'browse' button
// is clickable and it is located at
// the right side of the input
right: 0, top: 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, cursor: 'pointer', opacity: 0 }); this._element.appendChild(input);
var self = this; qq.attach(input, 'change', function(){ self._options.onChange(input); }); qq.attach(input, 'mouseover', function(){ qq.addClass(self._element, self._options.hoverClass); }); qq.attach(input, 'mouseout', function(){ qq.removeClass(self._element, self._options.hoverClass); }); qq.attach(input, 'focus', function(){ qq.addClass(self._element, self._options.focusClass); }); qq.attach(input, 'blur', function(){ qq.removeClass(self._element, self._options.focusClass); });
// IE and Opera, unfortunately have 2 tab stops on file input
// which is unacceptable in our case, disable keyboard access
if (window.attachEvent){ // it is IE or Opera
input.setAttribute('tabIndex', "-1"); }
return input; } };
/** * Class for uploading files, uploading itself is handled by child classes */ qq.UploadHandlerAbstract = function(o){ this._options = { debug: false, action: '/testest', // maximum number of concurrent uploads
maxConnections: 999, onProgress: function(id, fileName, loaded, total){}, onComplete: function(id, fileName, response){}, onCancel: function(id, fileName){} }; qq.extend(this._options, o); this._queue = []; // params for files in queue
this._params = []; }; qq.UploadHandlerAbstract.prototype = { log: function(str){ if (this._options.debug && window.console) console.log('[uploader] ' + str); }, /** * Adds file or file input to the queue * @returns id **/ add: function(file){}, /** * Sends the file identified by id and additional query params to the server */ upload: function(id, params){ var len = this._queue.push(id);
var copy = {}; qq.extend(copy, params); this._params[id] = copy; // if too many active uploads, wait...
if (len <= this._options.maxConnections){ this._upload(id, this._params[id]); } }, /** * Cancels file upload by id */ cancel: function(id){ this._cancel(id); this._dequeue(id); }, /** * Cancells all uploads */ cancelAll: function(){ for (var i=0; i<this._queue.length; i++){ this._cancel(this._queue[i]); } this._queue = []; }, /** * Returns name of the file identified by id */ getName: function(id){}, /** * Returns size of the file identified by id */ getSize: function(id){}, /** * Returns id of files being uploaded or * waiting for their turn */ getQueue: function(){ return this._queue; }, /** * Actual upload method */ _upload: function(id){}, /** * Actual cancel method */ _cancel: function(id){}, /** * Removes element from queue, starts upload of next */ _dequeue: function(id){ var i = qq.indexOf(this._queue, id); this._queue.splice(i, 1); var max = this._options.maxConnections; if (this._queue.length >= max && i < max){ var nextId = this._queue[max-1]; this._upload(nextId, this._params[nextId]); } } };
/** * Class for uploading files using form and iframe * @inherits qq.UploadHandlerAbstract */ qq.UploadHandlerForm = function(o){ qq.UploadHandlerAbstract.apply(this, arguments); this._inputs = {}; }; // @inherits qq.UploadHandlerAbstract
qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
qq.extend(qq.UploadHandlerForm.prototype, { add: function(fileInput){ fileInput.setAttribute('name', 'qqfile'); var id = 'qq-upload-handler-iframe' + qq.getUniqueId(); this._inputs[id] = fileInput; // remove file input from DOM
if (fileInput.parentNode){ qq.remove(fileInput); } return id; }, getName: function(id){ // get input value and remove path to normalize
return this._inputs[id].value.replace(/.*(\/|\\)/, ""); }, _cancel: function(id){ this._options.onCancel(id, this.getName(id)); delete this._inputs[id];
var iframe = document.getElementById(id); if (iframe){ // to cancel request set src to something else
// we use src="javascript:false;" because it doesn't
// trigger ie6 prompt on https
iframe.setAttribute('src', 'javascript:false;');
qq.remove(iframe); } }, _upload: function(id, params){ var input = this._inputs[id]; if (!input){ throw new Error('file with passed id was not added, or already uploaded or cancelled'); }
var fileName = this.getName(id); var iframe = this._createIframe(id); var form = this._createForm(iframe, params); form.appendChild(input);
var self = this; this._attachLoadEvent(iframe, function(){ self.log('iframe loaded'); var response = self._getIframeContentJSON(iframe);
self._options.onComplete(id, fileName, response); self._dequeue(id); delete self._inputs[id]; // timeout added to fix busy state in FF3.6
setTimeout(function(){ qq.remove(iframe); }, 1); });
form.submit(); qq.remove(form); return id; }, _attachLoadEvent: function(iframe, callback){ qq.attach(iframe, 'load', function(){ // when we remove iframe from dom
// the request stops, but in IE load
// event fires
if (!iframe.parentNode){ return; }
// 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; }
callback(); }); }, /** * Returns json object received by iframe from server. */ _getIframeContentJSON: function(iframe){ // iframe.contentWindow.document - for IE<7
var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document, response; this.log("converting iframe's innerHTML to JSON"); this.log("innerHTML = " + doc.body.innerHTML); try { response = eval("(" + doc.body.innerHTML + ")"); } catch(err){ response = {}; }
return response; }, /** * Creates iframe with unique name */ _createIframe: function(id){ // We can't use following code as the name attribute
// won't be properly registered in IE6, and new window
// on form submit will open
// var iframe = document.createElement('iframe');
// iframe.setAttribute('name', id);
var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />'); // src="javascript:false;" removes ie6 prompt on https
iframe.setAttribute('id', id);
iframe.style.display = 'none'; document.body.appendChild(iframe);
return iframe; }, /** * Creates form, that will be submitted to iframe */ _createForm: function(iframe, params){ // We can't use the following code in IE6
// var form = document.createElement('form');
// form.setAttribute('method', 'post');
// form.setAttribute('enctype', 'multipart/form-data');
// Because in this case file won't be attached to request
var form = qq.toElement('<form method="post" enctype="multipart/form-data"></form>');
var queryString = qq.obj2url(params, this._options.action);
form.setAttribute('action', queryString); form.setAttribute('target', iframe.name); form.style.display = 'none'; document.body.appendChild(form);
return form; } });
/** * Class for uploading files using xhr * @inherits qq.UploadHandlerAbstract */ qq.UploadHandlerXhr = function(o){ qq.UploadHandlerAbstract.apply(this, arguments);
this._files = []; this._xhrs = []; // current loaded size in bytes for each file
this._loaded = []; };
// static method
qq.UploadHandlerXhr.isSupported = function(){ var input = document.createElement('input'); input.type = 'file'; return ( 'multiple' in input && typeof File != "undefined" && typeof (new XMLHttpRequest()).upload != "undefined" ); };
// @inherits qq.UploadHandlerAbstract
qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype)
qq.extend(qq.UploadHandlerXhr.prototype, { /** * Adds file to the queue * Returns id to use with upload, cancel **/ add: function(file){ if (!(file instanceof File)){ throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)'); } return this._files.push(file) - 1; }, getName: function(id){ var file = this._files[id]; // fix missing name in Safari 4
return file.fileName != null ? file.fileName : file.name; }, getSize: function(id){ var file = this._files[id]; return file.fileSize != null ? file.fileSize : file.size; }, /** * Returns uploaded bytes for file identified by id */ getLoaded: function(id){ return this._loaded[id] || 0; }, /** * Sends the file identified by id and additional query params to the server * @param {Object} params name-value string pairs */ _upload: function(id, params){ var file = this._files[id], name = this.getName(id), size = this.getSize(id); this._loaded[id] = 0; var xhr = this._xhrs[id] = new XMLHttpRequest(); var self = this; xhr.upload.onprogress = function(e){ if (e.lengthComputable){ self._loaded[id] = e.loaded; self._options.onProgress(id, name, e.loaded, e.total); } };
xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ self._onComplete(id, xhr); } };
// build query string
params = params || {}; params['qqfile'] = name; var queryString = qq.obj2url(params, this._options.action);
xhr.open("POST", queryString, true); xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhr.setRequestHeader("X-File-Name", encodeURIComponent(name)); xhr.setRequestHeader("Content-Type", "application/octet-stream"); xhr.setRequestHeader("Accept", "application/json"); xhr.send(file); }, _onComplete: function(id, xhr){ // the request was aborted/cancelled
if (!this._files[id]) return; var name = this.getName(id); var size = this.getSize(id); this._options.onProgress(id, name, size, size); if (xhr.status == 200){ this.log("xhr - server response received"); this.log("responseText = " + xhr.responseText); var response; try { response = eval("(" + xhr.responseText + ")"); } catch(err){ response = {}; } this._options.onComplete(id, name, response); } else { this._options.onComplete(id, name, {}); } this._files[id] = null; this._xhrs[id] = null; this._dequeue(id); }, _cancel: function(id){ this._options.onCancel(id, this.getName(id)); this._files[id] = null; if (this._xhrs[id]){ this._xhrs[id].abort(); this._xhrs[id] = null; } } });
var uploader = new qq.FileUploader({ element: document.getElementById('file-uploader'), action: '/upload', params: { expires: $('#expires').val(), randomize: true, },
messages: { typeError: "{file} has invalid extension. Only {extensions} are allowed.", sizeError: "{file} is too large, maximum file size is {sizeLimit}.", minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.", emptyError: "{file} is empty, please select files again without it.", onLeave: "The files are being uploaded, if you leave now the upload will be cancelled." }, showMessage: function(message){ alert(message); }, });
|