One Hat Cyber Team
Your IP :
172.16.0.254
Server IP :
58.26.163.33
Server :
Windows NT DGPENSV2LPKMN 10.0 build 14393 (Windows Server 2016) AMD64
Server Software :
Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/7.3.25
PHP Version :
7.3.25
Buat File
|
Buat Folder
Eksekusi
Dir :
C:
/
laragon
/
etc
/
apps
/
laragon
/
extras
/
View File Name :
SimpleAjaxUploader.js
/** * Simple Ajax Uploader * Version 2.0 * https://github.com/LPology/Simple-Ajax-Uploader * * Copyright 2012-2015 LPology, LLC * Released under the MIT license */ ;(function( window, document, undefined ) { var ss = window.ss || {}, // ss.trim() rLWhitespace = /^\s+/, rTWhitespace = /\s+$/, // ss.getUID uidReplace = /[xy]/g, // ss.getFilename() rPath = /.*(\/|\\)/, // ss.getExt() rExt = /.*[.]/, // ss.hasClass() rHasClass = /[\t\r\n]/g, // Check for Safari -- it doesn't like multi file uploading. At all. // http://stackoverflow.com/a/9851769/1091949 isSafari = Object.prototype.toString.call( window.HTMLElement ).indexOf( 'Constructor' ) > 0, isIE7 = ( navigator.userAgent.indexOf('MSIE 7') !== -1 ), // Check whether XHR uploads are supported input = document.createElement( 'input' ), XhrOk; input.type = 'file'; XhrOk = ( 'multiple' in input && typeof File !== 'undefined' && typeof ( new XMLHttpRequest() ).upload !== 'undefined' ); /** * Converts object to query string */ ss.obj2string = function( obj, prefix ) { "use strict"; var str = []; for ( var prop in obj ) { if ( obj.hasOwnProperty( prop ) ) { var k = prefix ? prefix + '[' + prop + ']' : prop, v = obj[prop]; str.push( typeof v === 'object' ? ss.obj2string( v, k ) : encodeURIComponent( k ) + '=' + encodeURIComponent( v ) ); } } return str.join( '&' ); }; /** * Copies all missing properties from second object to first object */ ss.extendObj = function( first, second ) { "use strict"; for ( var prop in second ) { if ( second.hasOwnProperty( prop ) ) { first[prop] = second[prop]; } } }; ss.addEvent = function( elem, type, fn ) { "use strict"; if ( elem.addEventListener ) { elem.addEventListener( type, fn, false ); } else { elem.attachEvent( 'on' + type, fn ); } return function() { ss.removeEvent( elem, type, fn ); }; }; ss.removeEvent = function( elem, type, fn ) { "use strict"; if ( elem.removeEventListener ) { elem.removeEventListener( type, fn, false ); } else { elem.detachEvent( 'on' + type, fn ); } }; ss.newXHR = function() { "use strict"; if ( typeof XMLHttpRequest !== 'undefined' ) { return new window.XMLHttpRequest(); } else if ( window.ActiveXObject ) { try { return new window.ActiveXObject( 'Microsoft.XMLHTTP' ); } catch ( err ) { return false; } } }; ss.getIFrame = function() { "use strict"; var id = ss.getUID(), iframe; // IE7 can only create an iframe this way, all others are the other way if ( isIE7 ) { iframe = document.createElement('<iframe src="javascript:false;" name="' + id + '">'); } else { iframe = document.createElement('iframe'); /*jshint scripturl:true*/ iframe.src = 'javascript:false;'; iframe.name = id; } iframe.style.display = 'none'; iframe.id = id; return iframe; }; ss.getForm = function( opts ) { "use strict"; var form = document.createElement('form'); form.encoding = 'multipart/form-data'; // IE form.enctype = 'multipart/form-data'; form.style.display = 'none'; for ( var prop in opts ) { if ( opts.hasOwnProperty( prop ) ) { form[prop] = opts[prop]; } } return form; }; ss.getHidden = function( name, value ) { "use strict"; var input = document.createElement( 'input' ); input.type = 'hidden'; input.name = name; input.value = value; return input; }; /** * Parses a JSON string and returns a Javascript object * Borrowed from www.jquery.com */ ss.parseJSON = function( data ) { "use strict"; if ( !data ) { return false; } data = ss.trim( data + '' ); if ( window.JSON && window.JSON.parse ) { try { // Support: Android 2.3 // Workaround failure to string-cast null input return window.JSON.parse( data + '' ); } catch ( err ) { return false; } } var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g, depth = null, requireNonComma; // Guard against invalid (and possibly dangerous) input by ensuring that nothing remains // after removing valid tokens return data && !ss.trim( data.replace( rvalidtokens, function( token, comma, open, close ) { // Force termination if we see a misplaced comma if ( requireNonComma && comma ) { depth = 0; } // Perform no more replacements after returning to outermost depth if ( depth === 0 ) { return token; } // Commas must not follow "[", "{", or "," requireNonComma = open || comma; // Determine new depth // array/object open ("[" or "{"): depth += true - false (increment) // array/object close ("]" or "}"): depth += false - true (decrement) // other cases ("," or primitive): depth += true - true (numeric cast) depth += !close - !open; // Remove this token return ''; }) ) ? ( Function( 'return ' + data ) )() : false; }; ss.getBox = function( elem ) { "use strict"; var box, docElem, top = 0, left = 0; if ( elem.getBoundingClientRect ) { box = elem.getBoundingClientRect(); docElem = document.documentElement; top = box.top + ( window.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ); left = box.left + ( window.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 ); } else { do { left += elem.offsetLeft; top += elem.offsetTop; } while ( ( elem = elem.offsetParent ) ); } return { top: Math.round( top ), left: Math.round( left ) }; }; /** * Helper that takes object literal * and add all properties to element.style * @param {Element} el * @param {Object} styles */ ss.addStyles = function( elem, styles ) { "use strict"; for ( var name in styles ) { if ( styles.hasOwnProperty( name ) ) { elem.style[name] = styles[name]; } } }; /** * Function places an absolutely positioned * element on top of the specified element * copying position and dimensions. */ ss.copyLayout = function( from, to ) { "use strict"; var box = ss.getBox( from ); ss.addStyles( to, { position: 'absolute', left : box.left + 'px', top : box.top + 'px', width : from.offsetWidth + 'px', height : from.offsetHeight + 'px' }); }; /** * Generates unique ID * Complies with RFC 4122 version 4 * http://stackoverflow.com/a/2117523/1091949 * ID begins with letter "a" to be safe for HTML elem ID/name attr (can't start w/ number) */ ss.getUID = function() { "use strict"; /*jslint bitwise: true*/ return 'axxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(uidReplace, function(c) { var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); return v.toString(16); }); }; /** * Removes white space from left and right of string */ ss.trim = function( text ) { "use strict"; return text.toString().replace( rLWhitespace, '' ).replace( rTWhitespace, '' ); }; /** * Extract file name from path */ ss.getFilename = function( path ) { "use strict"; return path.replace( rPath, '' ); }; /** * Get file extension */ ss.getExt = function( file ) { "use strict"; return ( -1 !== file.indexOf( '.' ) ) ? file.replace( rExt, '' ) : ''; }; /** * Check whether element has a particular CSS class * Parts borrowed from www.jquery.com */ ss.hasClass = function( elem, name ) { "use strict"; return ( ' ' + elem.className + ' ' ).replace( rHasClass, ' ' ).indexOf( ' ' + name + ' ' ) >= 0; }; /** * Adds CSS class to an element */ ss.addClass = function( elem, name ) { "use strict"; if ( !name || name === '' ) { return false; } if ( !ss.hasClass( elem, name ) ) { elem.className += ' ' + name; } }; /** * Removes CSS class from an element */ ss.removeClass = (function() { "use strict"; var c = {}; //cache regexps for performance return function( e, name ) { if ( !c[name] ) { c[name] = new RegExp('(?:^|\\s)' + name + '(?!\\S)'); } e.className = e.className.replace( c[name], '' ); }; })(); /** * Nulls out event handlers to prevent memory leaks in IE6/IE7 * http://javascript.crockford.com/memory/leak.html * @param {Element} d * @return void */ ss.purge = function( d ) { "use strict"; var a = d.attributes, i, l, n; if ( a ) { for ( i = a.length - 1; i >= 0; i -= 1 ) { n = a[i].name; if ( typeof d[n] === 'function' ) { d[n] = null; } } } a = d.childNodes; if ( a ) { l = a.length; for ( i = 0; i < l; i += 1 ) { ss.purge( d.childNodes[i] ); } } }; /** * Removes element from the DOM */ ss.remove = function( elem ) { "use strict"; if ( elem && elem.parentNode ) { // null out event handlers for IE ss.purge( elem ); elem.parentNode.removeChild( elem ); } elem = null; }; /** * Accepts either a jQuery object, a string containing an element ID, or an element, * verifies that it exists, and returns the element. * @param {Mixed} elem * @return {Element} */ ss.verifyElem = function( elem ) { "use strict"; if ( typeof jQuery !== 'undefined' && elem instanceof jQuery ) { elem = elem[0]; } else if ( typeof elem === 'string' ) { if ( elem.charAt( 0 ) == '#' ) { elem = elem.substr( 1 ); } elem = document.getElementById( elem ); } if ( !elem || elem.nodeType !== 1 ) { return false; } if ( elem.nodeName.toUpperCase() == 'A' ) { elem.style.cursor = 'pointer'; ss.addEvent( elem, 'click', function( e ) { if ( e && e.preventDefault ) { e.preventDefault(); } else if ( window.event ) { window.event.returnValue = false; } }); } return elem; }; ss._options = {}; ss.uploadSetup = function( options ) { "use strict"; ss.extendObj( ss._options, options ); }; ss.SimpleUpload = function( options ) { "use strict"; var i, len, btn; this._opts = { button: '', url: '', dropzone: '', dragClass: '', cors: false, progressUrl: false, sessionProgressUrl: false, nginxProgressUrl: false, multiple: false, maxUploads: 3, queue: true, checkProgressInterval: 500, keyParamName: 'APC_UPLOAD_PROGRESS', sessionProgressName: 'PHP_SESSION_UPLOAD_PROGRESS', nginxProgressHeader: 'X-Progress-ID', corsInputName: 'XHR_CORS_TARGETORIGIN', allowedExtensions: [], accept: '', maxSize: false, name: '', data: {}, noParams: false, autoSubmit: true, multipart: false, method: 'POST', responseType: '', debug: false, hoverClass: '', focusClass: '', disabledClass: '', customHeaders: {}, onAbort: function( filename, uploadBtn ) {}, onChange: function( filename, extension, uploadBtn, size ) {}, onSubmit: function( filename, extension, uploadBtn, size ) {}, onProgress: function( pct ) {}, onUpdateFileSize: function( filesize ) {}, onComplete: function( filename, response, uploadBtn ) {}, onExtError: function( filename, extension ) {}, onSizeError: function( filename, fileSize ) {}, onError: function( filename, type, status, statusText, response, uploadBtn ) {}, startXHR: function( filename, fileSize, uploadBtn ) {}, endXHR: function( filename, fileSize, uploadBtn ) {}, startNonXHR: function( filename, uploadBtn ) {}, endNonXHR: function( filename, uploadBtn ) {} }; // Include any setup options ss.extendObj( this._opts, ss._options ); // Then add options for this instance ss.extendObj( this._opts, options ); options = null; this._btns = []; // An array of buttons was passed if ( this._opts.button instanceof Array ) { len = this._opts.button.length; for ( i = 0; i < len; i++ ) { btn = ss.verifyElem( this._opts.button[i] ); if ( btn !== false ) { this._btns.push( this.rerouteClicks( btn ) ); } else { this.log( 'Button with array index ' + i + ' is invalid' ); } } // A single button was passed } else { btn = ss.verifyElem( this._opts.button ); if ( btn !== false ) { this._btns.push( this.rerouteClicks( btn ) ); } } delete this._opts.button; // No valid elements were passed to button option if ( this._opts.dropzone === '' && ( this._btns.length < 1 || this._btns[0] === false ) ) { throw new Error( "Invalid button. Make sure the element you're passing exists." ); } if ( this._opts.multiple === false ) { this._opts.maxUploads = 1; } // An array of objects, each containing two items, a file and a reference // to the button which triggered the upload: { file: uploadFile, btn: button } this._queue = []; this._active = 0; this._disabled = false; // if disabled, clicking on button won't do anything this._maxFails = 10; // max allowed failed progress updates requests in iframe mode this._progKeys = {}; // contains the currently active upload ID progress keys if ( !XhrOk ) { // Cache progress keys after we set sizeBox for fewer trips to the DOM this._sizeFlags = {}; } if ( XhrOk && this._opts.dropzone !== '' ) { this.addDropZone( this._opts.dropzone ); } this._createInput(); this._manDisabled = false; this.enable( true ); }; ss.SimpleUpload.prototype = { destroy: function() { "use strict"; // # of upload buttons var i = this._btns.length; // Put upload buttons back to the way we found them while ( i-- ) { // Remove event listener if ( this._btns[i].off ) { this._btns[i].off(); } // Remove any lingering classes ss.removeClass( this._btns[i], this._opts.hoverClass ); ss.removeClass( this._btns[i], this._opts.focusClass ); ss.removeClass( this._btns[i], this._opts.disabledClass ); // In case we disabled it this._btns[i].disabled = false; } // Remove div/file input combos from the DOM ss.remove( this._input.parentNode ); // Now burn it all down for ( var prop in this ) { if ( this.hasOwnProperty( prop ) ) { delete this.prop; } } }, /** * Send data to browser console if debug is set to true */ log: function( str ) { "use strict"; if ( this._opts.debug && window.console && window.console.log ) { window.console.log( '[Uploader] ' + str ); } }, /** * Replaces user data * Note that all previously set data is entirely removed and replaced */ setData: function( data ) { "use strict"; this._opts.data = data; }, /** * Set or change uploader options * @param {Object} options */ setOptions: function( options ) { "use strict"; ss.extendObj( this._opts, options ); }, /** * Designate an element as a progress bar * The CSS width % of the element will be updated as the upload progresses */ setProgressBar: function( elem ) { "use strict"; this._progBar = ss.verifyElem( elem ); }, /** * Designate an element to receive a string containing progress % during upload * Note: Uses innerHTML, so any existing child elements will be wiped out */ setPctBox: function( elem ) { "use strict"; this._pctBox = ss.verifyElem( elem ); }, /** * Designate an element to receive a string containing file size at start of upload * Note: Uses innerHTML, so any existing child elements will be wiped out */ setFileSizeBox: function( elem ) { "use strict"; this._sizeBox = ss.verifyElem( elem ); }, /** * Designate an element to be removed from DOM when upload is completed * Useful for removing progress bar, file size, etc. after upload */ setProgressContainer: function( elem ) { "use strict"; this._progBox = ss.verifyElem( elem ); }, /** * Designate an element to serve as the upload abort button */ setAbortBtn: function( elem, remove ) { "use strict"; this._abortBtn = ss.verifyElem( elem ); this._removeAbort = false; if ( remove ) { this._removeAbort = true; } }, /** * Returns number of files currently in queue */ getQueueSize: function() { "use strict"; return this._queue.length; }, /** * Enables uploader and submits next file for upload */ _cycleQueue: function() { "use strict"; if ( this._queue.length > 0 && this._opts.autoSubmit ) { this.submit(); } }, /** * Remove current file from upload queue, reset props, cycle to next upload */ removeCurrent: function( id ) { "use strict"; if ( id ) { var i = this._queue.length; while ( i-- ) { if ( this._queue[i].id === id ) { this._queue.splice( i, 1 ); break; } } } else { this._queue.splice( 0, 1 ); } this._cycleQueue(); }, /** * Clears Queue so only most recent select file is uploaded */ clearQueue: function() { "use strict"; this._queue.length = 0; }, /** * Disables upload functionality */ disable: function( _self ) { "use strict"; var i = this._btns.length, nodeName; // _self is always true when disable() is called internally this._manDisabled = !_self || this._manDisabled === true ? true : false; this._disabled = true; while ( i-- ) { nodeName = this._btns[i].nodeName.toUpperCase(); if ( nodeName == 'INPUT' || nodeName == 'BUTTON' ) { this._btns[i].disabled = true; } if ( this._opts.disabledClass !== '' ) { ss.addClass( this._btns[i], this._opts.disabledClass ); } } // Hide file input if ( this._input && this._input.parentNode ) { this._input.parentNode.style.visibility = 'hidden'; } }, /** * Enables upload functionality */ enable: function( _self ) { "use strict"; // _self will always be true when enable() is called internally if ( !_self ) { this._manDisabled = false; } // Don't enable uploader if it was manually disabled if ( this._manDisabled === true ) { return; } var i = this._btns.length; this._disabled = false; while ( i-- ) { ss.removeClass( this._btns[i], this._opts.disabledClass ); this._btns[i].disabled = false; } } }; ss.IframeUpload = { /** * Accepts a URI string and returns the hostname */ _getHost: function( uri ) { var a = document.createElement( 'a' ); a.href = uri; if ( a.hostname ) { return a.hostname.toLowerCase(); } return uri; }, _addFiles: function( file ) { var filename = ss.getFilename( file.value ), ext = ss.getExt( filename ); if ( false === this._opts.onChange.call( this, filename, ext, this._overBtn ) ) { return false; } this._queue.push({ id: ss.getUID(), file: file, name: filename, ext: ext, btn: this._overBtn, size: null }); return true; }, /** * Handles uploading with iFrame */ _uploadIframe: function( fileObj, progBox, sizeBox, progBar, pctBox, abortBtn, removeAbort ) { "use strict"; var self = this, opts = this._opts, key = ss.getUID(), iframe = ss.getIFrame(), form, url, msgLoaded = false, iframeLoaded = false, removeMessageListener, removeLoadListener, cancel; if ( opts.noParams === true ) { url = opts.url; } else { // If we're using Nginx Upload Progress Module, append upload key to the URL // Also, preserve query string if there is one url = !opts.nginxProgressUrl ? opts.url : url + ( ( url.indexOf( '?' ) > -1 ) ? '&' : '?' ) + encodeURIComponent( opts.nginxProgressHeader ) + '=' + encodeURIComponent( key ); } form = ss.getForm({ action: url, target: iframe.name, method: opts.method }); // Begin progress bars at 0% opts.onProgress.call( this, 0 ); if ( pctBox ) { pctBox.innerHTML = '0%'; } if ( progBar ) { progBar.style.width = '0%'; } // For CORS, add a listener for the "message" event, which will be // triggered by the Javascript snippet in the server response if ( opts.cors ) { removeMessageListener = ss.addEvent( window, 'message', function( event ) { // Make sure event.origin matches the upload URL if ( self._getHost( event.origin ) != self._getHost( opts.url ) ) { self.log('Non-matching origin: ' + event.origin); return; } // Set message event success flag to true msgLoaded = true; // Remove listener for message event removeMessageListener(); opts.endNonXHR.call( self, fileObj.name, fileObj.btn ); self._finish( fileObj, '', '', event.data, sizeBox, progBox, pctBox, abortBtn, removeAbort ); }); } var remove = ss.addEvent( iframe, 'load', function() { remove(); if ( opts.sessionProgressUrl ) { form.appendChild( ss.getHidden( opts.sessionProgressName, key ) ); } // PHP APC upload progress key field must come before the file field else if ( opts.progressUrl ) { form.appendChild( ss.getHidden( opts.keyParamName, key ) ); } // We get any additional data here after startNonXHR() // in case the data was changed with setData() prior to submitting for ( var prop in opts.data ) { if ( opts.data.hasOwnProperty( prop ) ) { form.appendChild( ss.getHidden( prop, opts.data[prop] ) ); } } // Add a field (default name: "XHR_CORS_TRARGETORIGIN") to tell server this is a CORS request // Value of the field is targetOrigin parameter of postMessage(message, targetOrigin) if ( opts.cors ) { form.appendChild( ss.getHidden( opts.corsInputName, window.location.href ) ); } form.appendChild( fileObj.file ); removeLoadListener = ss.addEvent( iframe, 'load', function() { if ( !iframe.parentNode || iframeLoaded ) { return; } iframeLoaded = true; delete self._progKeys[key]; delete self._sizeFlags[key]; // Remove listener for iframe load event removeLoadListener(); if ( abortBtn ) { ss.removeEvent( abortBtn, 'click', cancel ); } // After a CORS response, we wait briefly for the "message" event to finish, // during which time the msgLoaded var will be set to true, signalling success. // If iframe loads without "message" event, we assume there was an error if ( opts.cors ) { window.setTimeout(function() { ss.remove( form ); ss.remove( iframe ); // If msgLoaded has not been set to true after "message" event fires, we // infer that an error must have occurred and respond accordingly if ( !msgLoaded ) { self._errorFinish( fileObj, '', '', false, 'error', progBox, sizeBox, pctBox, abortBtn, removeAbort ); } opts = key = form = iframe = sizeBox = progBox = pctBox = abortBtn = removeAbort = null; }, 600); } // Ordinary, non-CORS upload else { try { var doc = iframe.contentDocument ? iframe.contentDocument : iframe.contentWindow.document, response = doc.body.innerHTML; opts.endNonXHR.call( self, fileObj.name, fileObj.btn ); // No way to get status and statusText for an iframe so return empty strings self._finish( fileObj, '', '', response, sizeBox, progBox, pctBox, abortBtn, removeAbort ); } catch ( e ) { self._errorFinish( fileObj, '', e.message, false, 'error', progBox, sizeBox, pctBox, abortBtn, removeAbort ); } window.setTimeout(function() { ss.remove( form ); ss.remove( iframe ); form = iframe = null; }, 0); fileObj = opts = key = sizeBox = progBox = pctBox = null; } });// end load if ( abortBtn ) { cancel = function() { ss.removeEvent( abortBtn, 'click', cancel ); delete self._progKeys[key]; delete self._sizeFlags[key]; if ( iframe ) { iframeLoaded = true; removeLoadListener(); try { if ( iframe.contentWindow.document.execCommand ) { iframe.contentWindow.document.execCommand('Stop'); } iframe.src = 'javascript'.concat(':false;'); } catch( err ) {} window.setTimeout(function() { ss.remove( form ); ss.remove( iframe ); form = iframe = null; }, 0); } self.log('Upload aborted'); opts.onAbort.call( self, fileObj.name, fileObj.btn ); self._last( sizeBox, progBox, pctBox, abortBtn, removeAbort ); }; ss.addEvent( abortBtn, 'click', cancel ); } self.log( 'Commencing upload using iframe' ); form.submit(); if ( self._hasProgUrl ) { // Add progress key to active key array self._progKeys[key] = 1; // Start timer for first progress update window.setTimeout( function() { self._getProg( key, progBar, sizeBox, pctBox, 1 ); progBar = sizeBox = pctBox = null; }, opts.checkProgressInterval ); } // Remove this file from the queue and begin next upload window.setTimeout(function() { self.removeCurrent( fileObj.id ); }, 0); });// end load document.body.appendChild( form ); document.body.appendChild( iframe ); }, /** * Retrieves upload progress updates from the server * (For fallback upload progress support) */ _getProg: function( key, progBar, sizeBox, pctBox, counter ) { "use strict"; var self = this, opts = this._opts, time = new Date().getTime(), xhr, url, callback; if ( !key ) { return; } // Nginx Upload Progress Module if ( opts.nginxProgressUrl ) { url = opts.nginxProgressUrl + '?' + encodeURIComponent( opts.nginxProgressHeader ) + '=' + encodeURIComponent( key ) + '&_=' + time; } else if ( opts.sessionProgressUrl ) { url = opts.sessionProgressUrl; } // PHP APC upload progress else if ( opts.progressUrl ) { url = opts.progressUrl + '?progresskey=' + encodeURIComponent( key ) + '&_=' + time; } callback = function() { var response, size, pct, status, statusText; try { // XDomainRequest doesn't have readyState so we // just assume that it finished correctly if ( callback && ( opts.cors || xhr.readyState === 4 ) ) { callback = undefined; xhr.onreadystatechange = function() {}; try { statusText = xhr.statusText; status = xhr.status; } catch( e ) { // We normalize with Webkit giving an empty statusText statusText = ''; status = ''; } // XDomainRequest also doesn't have status, so we // again just assume that everything is fine if ( opts.cors || ( status >= 200 && status < 300 ) ) { response = ss.parseJSON( xhr.responseText ); if ( response === false ) { self.log( 'Error parsing progress response (expecting JSON)' ); return; } // Handle response if using Nginx Upload Progress Module if ( opts.nginxProgressUrl ) { if ( response.state == 'uploading' ) { size = parseInt( response.size, 10 ); if ( size > 0 ) { pct = Math.round( ( parseInt( response.received, 10 ) / size ) * 100 ); size = Math.round( size / 1024 ); // convert to kilobytes } } else if ( response.state == 'done' ) { pct = 100; } else if ( response.state == 'error' ) { self.log( 'Error requesting upload progress: ' + response.status ); return; } } // Handle response if using PHP APC else if ( opts.sessionProgressUrl || opts.progressUrl ) { if ( response.success === true ) { size = parseInt( response.size, 10 ); pct = parseInt( response.pct, 10 ); } } // Update progress bar width if ( pct ) { if ( pctBox ) { pctBox.innerHTML = pct + '%'; } if ( progBar ) { progBar.style.width = pct + '%'; } opts.onProgress.call( self, pct ); } if ( size && !self._sizeFlags[key] ) { if ( sizeBox ) { sizeBox.innerHTML = size + 'K'; } self._sizeFlags[key] = 1; opts.onUpdateFileSize.call( self, size ); } // Stop attempting progress checks if we keep failing if ( !pct && !size && counter >= self._maxFails ) { counter++; self.log( 'Failed progress request limit reached. Count: ' + counter ); return; } // Begin countdown until next progress update check if ( pct < 100 && self._progKeys[key] ) { window.setTimeout( function() { self._getProg( key, progBar, sizeBox, pctBox, counter ); key = progBar = sizeBox = pctBox = counter = null; }, opts.checkProgressInterval ); } // We didn't get a 2xx status so don't continue sending requests } else { delete self._progKeys[key]; self.log( 'Error requesting upload progress: ' + status + ' ' + statusText ); } xhr = size = pct = status = statusText = response = null; } } catch( e ) { self.log( 'Error requesting upload progress: ' + e.message ); } }; // CORS requests in IE8 and IE9 must use XDomainRequest if ( opts.cors && !opts.sessionProgressUrl ) { if ( window.XDomainRequest ) { xhr = new window.XDomainRequest(); xhr.open( 'GET', url, true ); xhr.onprogress = xhr.ontimeout = function() {}; xhr.onload = callback; xhr.onerror = function() { delete self._progKeys[key]; key = null; self.log('Error requesting upload progress'); }; // IE7 or some other dinosaur -- just give up } else { return; } } else { var method = !opts.sessionProgressUrl ? 'GET' : 'POST', params; xhr = ss.newXHR(); xhr.onreadystatechange = callback; xhr.open( method, url, true ); // PHP session progress updates must be a POST request if ( opts.sessionProgressUrl ) { params = encodeURIComponent( opts.sessionProgressName ) + '=' + encodeURIComponent( key ); xhr.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' ); } // Set the upload progress header for Nginx if ( opts.nginxProgressUrl ) { xhr.setRequestHeader( opts.nginxProgressHeader, key ); } xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); xhr.setRequestHeader( 'Accept', 'application/json, text/javascript, */*; q=0.01' ); xhr.send( ( opts.sessionProgressUrl && params ) || null ); } }, _initUpload: function( fileObj ) { if ( false === this._opts.startNonXHR.call( this, fileObj.name, fileObj.btn ) ) { if ( this._disabled ) { this.enable( true ); } this._active--; return; } this._hasProgUrl = ( this._opts.progressUrl || this._opts.sessionProgressUrl || this._opts.nginxProgressUrl ) ? true : false; this._uploadIframe( fileObj, this._progBox, this._sizeBox, this._progBar, this._pctBox, this._abortBtn, this._removeAbort ); this._progBox = this._sizeBox = this._progBar = this._pctBox = this._abortBtn = this._removeAbort = null; } }; ss.XhrUpload = { _addFiles: function( files ) { var total = files.length, filename, ext, size, i; if ( !this._opts.multiple ) { total = 1; } for ( i = 0; i < total; i++ ) { filename = ss.getFilename( files[i].name ); ext = ss.getExt( filename ); size = Math.round( files[i].size / 1024 ); if ( false === this._opts.onChange.call( this, filename, ext, size ) ) { return false; } this._queue.push({ id: ss.getUID(), file: files[i], name: filename, ext: ext, btn: this._overBtn, size: size }); } return true; }, /** * Handles uploading with XHR */ _uploadXhr: function( fileObj, url, params, headers, sizeBox, progBar, progBox, pctBox, abortBtn, removeAbort ) { "use strict"; var self = this, opts = this._opts, xhr = ss.newXHR(), callback, cancel; // Inject file size into size box if ( sizeBox ) { sizeBox.innerHTML = fileObj.size + 'K'; } // Begin progress bars at 0% if ( pctBox ) { pctBox.innerHTML = '0%'; } if ( progBar ) { progBar.style.width = '0%'; } opts.onProgress.call( this, 0 ); // Borrows heavily from jQuery ajax transport callback = function( _, isAbort ) { var status, statusText; // Firefox throws exceptions when accessing properties // of an xhr when a network error occurred try { // Was never called and is aborted or complete if ( callback && ( isAbort || xhr.readyState === 4 ) ) { callback = undefined; xhr.onreadystatechange = function() {}; // If it's an abort if ( isAbort ) { // Abort it manually if needed if ( xhr.readyState !== 4 ) { xhr.abort(); } opts.onAbort.call( self, fileObj.name, fileObj.btn ); self._last( sizeBox, progBox, pctBox, abortBtn, removeAbort ); } else { if ( abortBtn ) { ss.removeEvent( abortBtn, 'click', cancel ); } status = xhr.status; // Firefox throws an exception when accessing // statusText for faulty cross-domain requests try { statusText = xhr.statusText; } catch( e ) { // We normalize with Webkit giving an empty statusText statusText = ''; } if ( status >= 200 && status < 300 ) { opts.endXHR.call( self, fileObj.name, fileObj.size, fileObj.btn ); self._finish( fileObj, status, statusText, xhr.responseText, sizeBox, progBox, pctBox, abortBtn, removeAbort ); // We didn't get a 2xx status so throw an error } else { self._errorFinish( fileObj, status, statusText, xhr.responseText, 'error', progBox, sizeBox, pctBox, abortBtn, removeAbort ); } } } } catch ( e ) { if ( !isAbort ) { self._errorFinish( fileObj, -1, e.message, false, 'error', progBox, sizeBox, pctBox, abortBtn, removeAbort ); } } }; if ( abortBtn ) { cancel = function() { ss.removeEvent( abortBtn, 'click', cancel ); if ( callback ) { callback( undefined, true ); } }; ss.addEvent( abortBtn, 'click', cancel ); } xhr.onreadystatechange = callback; xhr.open( opts.method.toUpperCase(), url, true ); ss.extendObj( headers, opts.customHeaders ); for ( var i in headers ) { if ( headers.hasOwnProperty( i ) ) { xhr.setRequestHeader( i, headers[ i ] + '' ); } } ss.addEvent( xhr.upload, 'progress', function( event ) { if ( event.lengthComputable ) { var pct = Math.round( ( event.loaded / event.total ) * 100 ); opts.onProgress.call( self, pct ); if ( pctBox ) { pctBox.innerHTML = pct + '%'; } if ( progBar ) { progBar.style.width = pct + '%'; } } }); if ( opts.multipart === true ) { var formData = new FormData(); for ( var prop in params ) { if ( params.hasOwnProperty( prop ) ) { formData.append( prop, params[prop] ); } } formData.append( opts.name, fileObj.file ); this.log( 'Commencing upload using multipart form' ); xhr.send( formData ); } else { this.log( 'Commencing upload using binary stream' ); xhr.send( fileObj.file ); } // Remove file from upload queue and begin next upload this.removeCurrent( fileObj.id ); }, _initUpload: function( fileObj ) { "use strict"; var params = {}, headers = {}, url; // Call the startXHR() callback and stop upload if it returns false // We call it before _uploadXhr() in case setProgressBar, setPctBox, etc., is called if ( false === this._opts.startXHR.call( this, fileObj.name, fileObj.size, fileObj.btn ) ) { if ( this._disabled ) { this.enable( true ); } this._active--; return; } params[this._opts.name] = fileObj.name; headers['X-Requested-With'] = 'XMLHttpRequest'; headers['X-File-Name'] = fileObj.name; if ( this._opts.responseType.toLowerCase() == 'json' ) { headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'; } if ( !this._opts.multipart ) { headers['Content-Type'] = 'application/octet-stream'; } // We get the any additional data here after startXHR() ss.extendObj( params, this._opts.data ); // Build query string while preserving any existing parameters url = this._opts.noParams === true ? this._opts.url : this._opts.url + ( ( this._opts.url.indexOf( '?' ) > -1 ) ? '&' : '?' ) +ss.obj2string( params ); this._uploadXhr( fileObj, url, params, headers, this._sizeBox, this._progBar, this._progBox, this._pctBox, this._abortBtn, this._removeAbort ); this._sizeBox = this._progBar = this._progBox = this._pctBox = this._abortBtn = this._removeAbort = null; } }; (function(){ ss.extendObj( ss.SimpleUpload.prototype, { _createInput: function() { "use strict"; var self = this, div = document.createElement( 'div' ); this._input = document.createElement( 'input' ); this._input.type = 'file'; this._input.name = this._opts.name; // Don't allow multiple file selection in Safari -- it has a nasty bug // http://stackoverflow.com/q/7231054/1091949 if ( XhrOk && !isSafari && this._opts.multiple ) { this._input.multiple = true; } // Check support for file input accept attribute if ( 'accept' in this._input && this._opts.accept !== '' ) { this._input.accept = this._opts.accept; } ss.addStyles( div, { 'display' : 'block', 'position' : 'absolute', 'overflow' : 'hidden', 'margin' : 0, 'padding' : 0, 'opacity' : 0, 'direction' : 'ltr', 'zIndex': 2147483583 }); ss.addStyles( this._input, { 'position' : 'absolute', 'right' : 0, 'margin' : 0, 'padding' : 0, 'fontSize' : '480px', 'fontFamily' : 'sans-serif', 'cursor' : 'pointer' }); if ( div.style.opacity !== '0' ) { div.style.filter = 'alpha(opacity=0)'; } ss.addEvent( this._input, 'change', function() { if ( !self._input || self._input.value === '' ) { return; } if ( false === self._addFiles( XhrOk ? self._input.files : self._input ) ) { return; } ss.removeClass( self._overBtn, self._opts.hoverClass ); ss.removeClass( self._overBtn, self._opts.focusClass ); // Now that file is in upload queue, remove the file input ss.remove( self._input.parentNode ); delete self._input; // Then create a new file input self._createInput(); // Submit if autoSubmit option is true if ( self._opts.autoSubmit ) { self.submit(); } }); if ( self._opts.hoverClass !== '' ) { ss.addEvent( this._input, 'mouseover', function() { ss.addClass( self._overBtn, self._opts.hoverClass ); }); ss.addEvent( this._input, 'mouseout', function() { ss.removeClass( self._overBtn, self._opts.hoverClass ); ss.removeClass( self._overBtn, self._opts.focusClass ); self._input.parentNode.style.visibility = 'hidden'; }); } if ( self._opts.focusClass !== '' ) { ss.addEvent( this._input, 'focus', function() { ss.addClass( self._overBtn, self._opts.focusClass ); }); ss.addEvent( this._input, 'blur', function() { ss.removeClass( self._overBtn, self._opts.focusClass ); }); } div.appendChild( this._input ); document.body.appendChild( div ); }, rerouteClicks: function( elem ) { "use strict"; var self = this; // ss.addEvent() returns a function to detach, which // allows us to call elem.off() to remove mouseover listener elem.off = ss.addEvent( elem, 'mouseover', function() { if ( self._disabled ) { return; } if ( !self._input ) { self._createInput(); } self._overBtn = elem; ss.copyLayout( elem, self._input.parentNode ); self._input.parentNode.style.visibility = 'visible'; }); return elem; }, /** * Final cleanup function after upload ends */ _last: function( sizeBox, progBox, pctBox, abortBtn, removeAbort ) { "use strict"; if ( sizeBox ) { sizeBox.innerHTML = ''; } if ( pctBox ) { pctBox.innerHTML = ''; } if ( abortBtn && removeAbort ) { ss.remove( abortBtn ); } if ( progBox ) { ss.remove( progBox ); } // Decrement the active upload counter this._active--; sizeBox = progBox = pctBox = abortBtn = removeAbort = null; if ( this._disabled ) { this.enable( true ); } this._cycleQueue(); }, /** * Completes upload request if an error is detected */ _errorFinish: function( fileObj, status, statusText, response, errorType, progBox, sizeBox, pctBox, abortBtn, removeAbort ) { "use strict"; this.log( 'Upload failed: ' + status + ' ' + statusText ); this._opts.onError.call( this, fileObj.name, errorType, status, statusText, response, fileObj.btn ); this._last( sizeBox, progBox, pctBox, abortBtn, removeAbort ); fileObj = status = statusText = response = errorType = sizeBox = progBox = pctBox = abortBtn = removeAbort = null; }, /** * Completes upload request if the transfer was successful */ _finish: function( fileObj, status, statusText, response, sizeBox, progBox, pctBox, abortBtn, removeAbort ) { "use strict"; this.log( 'Server response: ' + response ); if ( this._opts.responseType.toLowerCase() == 'json' ) { response = ss.parseJSON( response ); if ( response === false ) { this._errorFinish( fileObj, status, statusText, false, 'parseerror', progBox, sizeBox, abortBtn, removeAbort ); return; } } this._opts.onComplete.call( this, fileObj.name, response, fileObj.btn ); this._last( sizeBox, progBox, pctBox, abortBtn, removeAbort ); fileObj = status = statusText = response = sizeBox = progBox = pctBox = abortBtn = removeAbort = null; }, /** * Verifies that file is allowed * Checks file extension and file size if limits are set */ _checkFile: function( fileObj ) { "use strict"; var extOk = false, i = this._opts.allowedExtensions.length; // Only file extension if allowedExtensions is set if ( i > 0 ) { while ( i-- ) { if ( this._opts.allowedExtensions[i].toLowerCase() == fileObj.ext.toLowerCase() ) { extOk = true; break; } } if ( !extOk ) { this.removeCurrent( fileObj.id ); this.log( 'File extension not permitted' ); this._opts.onExtError.call( this, fileObj.name, fileObj.ext ); return false; } } if ( fileObj.size && this._opts.maxSize !== false && fileObj.size > this._opts.maxSize ) { this.removeCurrent( fileObj.id ); this.log( fileObj.name + ' exceeds ' + this._opts.maxSize + 'K limit' ); this._opts.onSizeError.call( this, fileObj.name, fileObj.size ); return false; } fileObj = null; return true; }, /** * Validates input and directs to either XHR method or iFrame method */ submit: function() { "use strict"; if ( this._disabled || this._active >= this._opts.maxUploads || this._queue.length < 1 ) { return; } if ( !this._checkFile( this._queue[0] ) ) { return; } // User returned false to cancel upload if ( false === this._opts.onSubmit.call( this, this._queue[0].name, this._queue[0].ext, this._queue[0].btn, this._queue[0].size ) ) { return; } // Increment the active upload counter this._active++; // Disable uploading if multiple file uploads are not enabled // or if queue is disabled and we've reached max uploads if ( this._opts.multiple === false || this._opts.queue === false && this._active >= this._opts.maxUploads ) { this.disable( true ); } this._initUpload( this._queue[0] ); } }); if ( XhrOk ) { ss.extendObj( ss.SimpleUpload.prototype, ss.XhrUpload ); } else { ss.extendObj( ss.SimpleUpload.prototype, ss.IframeUpload ); } }()); ss.extendObj(ss.SimpleUpload.prototype, { _dragFileCheck: function( e ) { if ( e.dataTransfer.types ) { for ( var i = 0; i < e.dataTransfer.types.length; i++ ) { if ( e.dataTransfer.types[i] == 'Files' ) { return true; } } } return false; }, addDropZone: function( elem ) { var self = this; elem = ss.verifyElem( elem ); if ( !elem ) { self.log( 'Invalid or nonexistent element passed for drop zone' ); return false; } elem.ondragenter = function( e ) { if ( !self._dragFileCheck( e ) ) { return false; } ss.addClass( this, self._opts.dragClass ); return false; }; elem.ondragover = function() { return false; }; elem.ondragend = function() { ss.removeClass( this, self._opts.dragClass ); return false; }; elem.ondragleave = function() { ss.removeClass( this, self._opts.dragClass ); return false; }; elem.ondrop = function( e ) { e.preventDefault(); if ( !self._dragFileCheck( e ) ) { return false; } self._addFiles( e.dataTransfer.files ); self._cycleQueue(); ss.removeClass( this, self._opts.dragClass ); }; } }); // Expose to the global window object window.ss = ss; })( window, document );