1{"version":3,"sources":["?","../client/js/jquery-plugin.js","../client/js/jquery-dnd.js","../client/js/s3/jquery-plugin.js","../client/js/util.js","../client/js/export.js","../client/js/error/error.js","../client/js/version.js","../client/js/features.js","../client/js/promise.js","../client/js/blob-proxy.js","../client/js/button.js","../client/js/upload-data.js","../client/js/uploader.basic.api.js","../client/js/uploader.basic.js","../client/js/ajax.requester.js","../client/js/upload-handler/upload.handler.js","../client/js/upload-handler/upload.handler.controller.js","../client/js/window.receive.message.js","../client/js/upload-handler/form.upload.handler.js","../client/js/upload-handler/xhr.upload.handler.js","../client/js/deletefile.ajax.requester.js","../client/js/image-support/megapix-image.js","../client/js/image-support/image.js","../client/js/image-support/exif.js","../client/js/identify.js","../client/js/image-support/validation.image.js","../client/js/session.js","../client/js/session.ajax.requester.js","../client/js/image-support/scaler.js","../client/js/third-party/ExifRestorer.js","../client/js/total-progress.js","../client/js/paste.js","../client/js/form-support.js","../client/js/third-party/crypto-js/core.js","../client/js/third-party/crypto-js/enc-base64.js","../client/js/third-party/crypto-js/hmac.js","../client/js/third-party/crypto-js/sha1.js","../client/js/third-party/crypto-js/sha256.js","../client/js/third-party/crypto-js/lib-typedarrays.js","../client/js/s3/util.js","../client/js/non-traditional-common/uploader.basic.api.js","../client/js/s3/uploader.basic.js","../client/js/s3/request-signer.js","../client/js/uploadsuccess.ajax.requester.js","../client/js/s3/multipart.initiate.ajax.requester.js","../client/js/s3/multipart.complete.ajax.requester.js","../client/js/s3/multipart.abort.ajax.requester.js","../client/js/s3/s3.xhr.upload.handler.js","../client/js/s3/s3.form.upload.handler.js","../client/js/dnd.js","../client/js/uploader.api.js","../client/js/uploader.js","../client/js/templating.js","../client/js/ui.handler.events.js","../client/js/ui.handler.click.filebuttons.js","../client/js/ui.handler.click.filename.js","../client/js/ui.handler.focusin.filenameinput.js","../client/js/ui.handler.focus.filenameinput.js","../client/js/ui.handler.edit.filename.js","../client/js/s3/uploader.js"],"names":["global","$","init","options","xformedOpts","transformVariables","newUploaderInstance","getNewUploaderInstance","uploader","addCallbacks","$el","params","uploaderType","pluginOption","namespace","charAt","toUpperCase","slice","toLowerCase","qq","FineUploader","dataStore","key","val","data","undefined","instanceToStore","option","optionVal","transformedOpts","callbacks","each","_options","prop","nonJqueryCallback","name","callbackEventTarget","exec","substring","nonJqueryCallbackRetVal","jqueryEventCallbackRetVal","originalArgs","Array","prototype","call","arguments","transformedArgs","idx","arg","push","maybeWrapInJquery","apply","this","triggerHandler","error","log","message","source","dest","xformed","arrayVals","element","inArray","pluginOptions","isPlainObject","isArray","arrayVal","arrayObjDest","merge","isValidCommand","command","type","match","delegateCommand","retVal","xformedArgs","origArgs","transformedVal","nodeType","cloneNode","fn","fineUploader","optionsOrCommand","self","selfArgs","retVals","index","el","length","jQuery","dropZoneElements","dnd","DragAndDrop","rootDataKey","func","$callbackEl","args","jqueryHandlerResult","fineUploaderDnd","fineUploaderS3","endpointType","hide","style","display","attach","addEventListener","attachEvent","detach","removeEventListener","detachEvent","contains","descendant","compareDocumentPosition","insertBefore","elementB","parentNode","remove","removeChild","css","styles","Error","opacity","filter","Math","round","extend","hasClass","considerParent","re","RegExp","test","className","addClass","removeClass","replace","getByClass","first","candidates","result","querySelector","querySelectorAll","getElementsByTagName","getFirstByClass","children","child","firstChild","nextSibling","setText","text","innerText","textContent","clearText","hasAttribute","attrName","attrVal","getAttribute","canvasToBlob","canvas","mime","quality","dataUriToBlob","toDataURL","dataUri","arrayBuffer","byteString","intArray","mimeString","createBlob","BlobBuilder","window","WebKitBlobBuilder","MozBlobBuilder","MSBlobBuilder","blobBuilder","append","getBlob","Blob","split","indexOf","atob","decodeURI","ArrayBuffer","Uint8Array","character","charCodeAt","level","console","isObject","variable","Object","toString","isFunction","value","buffer","constructor","isItemList","maybeItemList","isNodeList","maybeNodeList","item","namedItem","isString","maybeString","trimStr","string","String","trim","format","str","newStr","nextIdxToReplace","strBefore","strAfter","isFile","maybeFile","File","isFileList","maybeFileList","FileList","isFileOrInput","maybeFileOrInput","isInput","maybeInput","notFile","evaluateType","normalizedType","HTMLInputElement","tagName","isBlob","maybeBlob","isXhrUploadSupported","input","document","createElement","multiple","FormData","createXhrInstance","upload","XMLHttpRequest","ActiveXObject","isFolderDropSupported","dataTransfer","items","webkitGetAsEntry","isFileChunkingSupported","androidStock","webkitSlice","mozSlice","sliceBlob","fileOrBlob","start","end","slicer","arrayBufferToHex","bytesAsHex","bytes","byt","byteAsHexStr","readBlobToHex","blob","startOffset","initialBlob","fileReader","FileReader","promise","Promise","onload","success","onerror","failure","readAsArrayBuffer","second","extendNested","override","target","sourceFn","super_","srcPropName","srcPropVal","arr","elt","from","len","hasOwnProperty","getUniqueId","c","r","random","v","ie","navigator","userAgent","ie7","ie8","ie10","ie11","edge","safari","vendor","chrome","opera","firefox","windows","platform","android","ios6","ios","ios7","ios8","ios800","iosChrome","iosSafari","iosSafariWebView","preventDefault","e","returnValue","toElement","div","html","innerHTML","iterableItem","callback","keyOrIndex","Storage","getItem","bind","oldFunc","context","newArgs","concat","obj2url","obj","temp","prefixDone","uristrings","prefix","add","nextObj","i","nextTemp","encodeURIComponent","join","obj2FormData","formData","arrayKeyName","obj2Inputs","form","setAttribute","appendChild","parseJson","json","JSON","parse","eval","getExtension","filename","extIdx","lastIndexOf","substr","getFilename","blobOrFileInput","fileName","DisposeSupport","disposers","dispose","disposer","shift","addDisposer","disposeFunction","define","amd","module","exports","version","supportedFeatures","testSupportsFileInputElement","tempInput","supported","disabled","ex","isChrome21OrHigher","isChrome14OrHigher","isCrossOriginXhrSupported","xhr","withCredentials","isXdrSupported","XDomainRequest","isCrossOriginAjaxSupported","isFolderSelectionSupported","webkitdirectory","isLocalStorageSupported","localStorage","setItem","isDragAndDropSupported","span","supportsUploading","supportsUploadingBlobs","supportsFileDrop","supportsAjaxFileUploading","supportsFolderDrop","supportsChunking","supportsResume","supportsUploadViaPaste","supportsUploadCors","supportsDeleteFileXdr","supportsDeleteFileCorsXhr","supportsDeleteFileCors","supportsFolderSelection","supportsImagePreviews","supportsUploadProgress","postMessage","ajaxUploading","blobUploading","canDetermineSize","chunking","deleteFileCors","deleteFileCorsXdr","deleteFileCorsXhr","dialogElement","HTMLDialogElement","fileDrop","folderDrop","folderSelection","imagePreviews","imageValidation","itemSizeValidation","pause","progressBar","resume","scaling","tiffPreviews","unlimitedScaledImageSize","uploading","uploadCors","uploadCustomHeaders","uploadNonMultipart","uploadViaPaste","isGenericPromise","maybePromise","then","successArgs","failureArgs","successCallbacks","failureCallbacks","doneCallbacks","state","onSuccess","onFailure","done","BlobProxy","referenceBlob","onCreate","create","UploadButton","o","createInput","BUTTON_ID_ATTR_NAME","buttonId","title","setMultiple","folders","acceptFiles","position","right","top","fontFamily","fontSize","margin","padding","cursor","height","disposeSupport","onChange","hoverClass","focusClass","ios8BrowserCrashWorkaround","overflow","direction","getInput","getButtonId","isMultiple","optInput","removeAttribute","setAcceptFiles","reset","UploadData","uploaderProxy","getDataByIds","idOrIds","entries","id","getDataByUuids","uuids","uuid","byUuid","getDataByStatus","status","statusResults","statuses","statusEnum","statusResultIndexes","byStatus","dataIndex","byProxyGroupId","byBatchId","addFile","spec","SUBMITTING","originalName","size","batchId","proxyGroupId","onStatusChange","retrieve","optionalFilter","setStatus","newStatus","oldStatus","byStatusOldStatusIndex","splice","uuidChanged","newUuid","oldUuid","updateName","newName","updateSize","newSize","setParentId","targetId","parentId","getIdsInProxyGroup","getIdsInBatch","SUBMITTED","REJECTED","QUEUED","CANCELED","PAUSED","UPLOADING","UPLOAD_RETRYING","UPLOAD_SUCCESSFUL","UPLOAD_FAILED","DELETE_FAILED","DELETING","DELETED","basePublicApi","addBlobs","blobDataOrArray","endpoint","addFiles","addInitialFiles","cannedFileList","cannedFile","_addCannedFile","_maybeHandleIos8SafariWorkaround","_storedIds","_currentBatchId","processBlob","_handleNewFile","blobs","defaultName","verifiedFiles","processBlobData","blobData","processCanvas","processCanvasData","canvasData","normalizedQuality","processFileOrInput","fileOrInput","files","file","normalizeData","fileContainer","_prepareItemsForUpload","cancel","_handler","cancelAll","storedIdsCopy","storedFileId","clearStoredFiles","continueUpload","uploadData","_uploadData","enabled","getName","_uploadFile","deleteFile","_onSubmitDelete","doesExist","fileOrBlobId","isValid","drawThumbnail","fileId","imgOrCanvas","maxSize","fromServer","customResizeFunction","fileOrUrl","promiseToReturn","_imageGenerator","_thumbnailUrls","scale","getFile","container","generate","modifiedContainer","reason","getButton","_getButton","_buttonIdsForFileIds","getEndpoint","_endpointStore","get","getInProgress","getParentId","uploadDataEntry","getUploads","getResumableFilesData","getSize","getNetUploads","_netUploaded","getRemainingAllowedItems","allowedItems","_currentItemLimit","_netUploadedOrQueued","getUuid","debug","pauseUpload","_autoRetries","_retryTimeouts","_preventRetries","_buttons","button","_paramsStore","_pasteHandler","session","refreshOnReset","_refreshSessionData","_succeededSinceLastAllComplete","_failedSinceLastAllComplete","_totalProgress","retry","_manualRetry","scaleImage","specs","Scaler","setCustomHeaders","headers","_customHeadersStore","set","setDeleteFileCustomHeaders","_deleteFileCustomHeadersStore","setDeleteFileEndpoint","_deleteFileEndpointStore","setDeleteFileParams","_deleteFileParamsStore","setEndpoint","setForm","elementOrId","_updateFormSupportAndParams","setItemLimit","newItemLimit","setName","setParams","setUuid","uploadStoredFiles","_itemError","_uploadStoredFiles","basePrivateApi","sessionData","deleteFileEndpoint","deleteFileParams","thumbnailUrl","_annotateWithButtonId","associatedInput","qqButtonId","_getButtonId","_batchError","onError","_createDeleteHandler","DeleteFileAjaxRequester","method","maxConnections","uuidParamName","request","uuidName","customHeaders","paramsStore","endpointStore","cors","onDelete","_onDelete","onDeleteComplete","xhrOrXdr","isError","_onDeleteComplete","_createPasteHandler","PasteSupport","targetElement","paste","pasteReceived","_handleCheckedCallback","onPasteReceived","_handlePasteSuccess","identifier","_createStore","initialValue","_readOnlyValues_","store","catchall","perIdReadOnlyValues","readOnlyValues","copy","orig","getReadOnlyValues","includeReadOnlyValues","existing","values","addReadOnly","_createUploadDataTracker","_onUploadStatusChange","_maybeAllComplete","setTimeout","_createUploadButton","allowMultiple","workarounds","iosEmptyVideos","_isAllowedExtension","allowedExtensions","accept","validation","classes","buttonFocus","buttonHover","ios8BrowserCrash","inputName","_onInputChange","fileInputTitle","_disposeSupport","_createUploadHandler","additionalOptions","lastOnProgress","preventRetryParam","preventRetryResponseProperty","onProgress","loaded","total","_onProgress","onComplete","_onComplete","onCancel","cancelFinalizationEffort","_onCancel","onUploadPrep","_onUploadPrep","onUpload","_onUpload","onUploadChunk","chunkData","_onUploadChunk","onUploadChunkSuccess","onResume","onAutoRetry","responseJSON","_onAutoRetry","onUuidChanged","setSize","_setSize","getDataByUuid","isQueued","UploadHandlerController","_fileOrBlobRejected","_formatSize","sizeSymbols","max","toFixed","_generateExtraButtonSpecs","_extraButtonSpecs","extraButtons","extraButtonOptionEntry","extraButtonSpec","_initExtraButton","extraButtonsSpec","_defaultButtonId","buttonOrFileInputOrFile","inputs","fileInput","fileBlobOrInput","_getNotFinished","_getValidationBase","_getValidationDescriptor","fileWrapper","_getValidationDescriptors","fileWrappers","fileDescriptors","_handleCameraAccess","camera","acceptIosCamera","optionRoot","details","callbackRetVal","successParam","newFileWrapperList","actualFile","handler","_customNewFileHandler","_handleNewFileGeneric","addFileToHandler","_trackButton","fileList","extSuppliedName","extension","_initFormSupportAndParams","_formSupport","FormSupport","attachedToForm","getFormInputsAsObject","autoUpload","newAutoUpload","newEndpoint","_isDeletePossible","expected","allowXdr","allowed","valid","allowedExt","extRegex","code","maybeNameOrNames","replacement","extensionsForMessage","placeholderMatch","messages","names","validationBase","allowedExtension","formatFileName","sizeLimit","minSizeLimit","placeholder","_onBeforeManualRetry","notFinished","_onAllComplete","ios8SafariUploads","alert","unsupportedBrowserIos8Safari","_maybeParseAndSendUploadError","response","errorReason","defaultResponseError","_maybeProcessNextItemAfterOnValidateCallback","validItem","stopOnFirstInvalidFile","validationDescriptor","onValidate","_onValidateCallbackSuccess","_onValidateCallbackFailure","successful","failed","onAllComplete","_shouldAutoRetry","retryWaitPeriod","autoAttemptDelay","_onBeforeAutoRetry","itemLimit","onManualRetry","clearTimeout","storedItemIndex","expunge","fileIndex","onIndividualProgress","_onSubmit","_onSubmitCallbackSuccess","_onSubmitted","onSubmitted","_storeForLater","onSuccessCallback","additionalMandatedParams","adjustedOnSuccessCallback","onSubmitDelete","_deleteHandler","sendDelete","_onTotalProgress","onTotalProgress","_onValidateBatchCallbackFailure","_onValidateBatchCallbackSuccess","validationDescriptors","errorMessage","proposedNetFilesUploadedOrQueued","tooManyItemsError","nextIndex","_validateFileOrBlobData","_upload","onValidateBatch","_preventLeaveInProgress","event","onLeave","Session","_session","addFileRecord","refresh","_sessionRequestComplete","onSessionRequestComplete","onNewSize","enableAuto","maxAutoAttempts","formElementOrId","onSubmit","idToUpload","stillSubmitting","validityChecker","allowEmpty","ImageValidation","validate","image","errorCode","_wrapCallbacks","safeCallback","errorMsg","exception","callbackName","callbackFunc","FineUploaderBasic","disableCancelForFormUploads","filenameParam","forceMultipart","paramsInBody","totalFileSizeName","maxHeight","maxWidth","minHeight","minWidth","maybeXhr","maybeXhrOrXdr","attemptNumber","fileOrBlobData","typeError","sizeError","minSizeError","emptyError","noFilesError","maxHeightImageError","maxWidthImageError","minHeightImageError","minWidthImageError","retryFailTooManyItems","concurrent","mandatory","paramNames","partIndex","partByteOffset","chunkSize","totalFileSize","totalParts","partSize","recordsExpireIn","resuming","fileOrBlobName","sendCredentials","interceptSubmit","customResizer","sendOriginal","orient","defaultType","defaultQuality","failureText","includeExif","sizes","ImageGenerator","_scaler","handleNewFile","TotalProgress","entry","AjaxRequester","isSimpleMethod","containsNonSimpleHeaders","containsNonSimple","header","isXdr","getCorsAjaxTransport","ontimeout","onprogress","getXhrOrXdr","suppliedXhr","requestData","dequeue","nextId","queue","sendRequest","xdrError","isResponseSuccessful","getParams","onDemandParams","additionalParams","mandatedParams","optXhr","url","payload","onSend","createUrl","additionalQueryParams","getXdrLoadHandler","getXdrErrorHandler","onreadystatechange","getXhrReadyStateChangeHandler","registerForUploadProgress","open","setHeaders","send","shouldParamsBeInQueryString","contentType","stringify","addToPath","readyState","lengthComputable","onDemandHeaders","additionalHeaders","allHeaders","acceptHeader","setRequestHeader","allowXRequestedWithAndCacheControl","responseCode","successfulResponseCodes","prepareToSend","validMethods","DELETE","PATCH","POST","PUT","GET","initTransport","path","cacheBuster","withPath","appendToPath","withParams","withQueryParams","_additionalQueryParams_","withHeaders","withPayload","thePayload","withCacheBuster","qqtimestamp","Date","getTime","canceled","UploadHandler","proxy","fileState","fileItem","onCancelRetVal","getThirdPartyFileId","_getFileState","_setThirdPartyFileId","thirdPartyFileId","_wasCanceled","preventRetryResponse","controller","chunkingPossible","concurrentChunkingPossible","chunked","chunkIdx","_getChunkData","attemptingResume","chunkProgress","_getChunkDataForCallback","finalize","finalizeChunks","normaizedResponse","normalizeResponse","_maybeDeletePersistedChunkData","cleanup","handleFailure","clearCachedChunk","inProgressIdx","responseToReport","inProgress","remaining","unshift","ignoreFailure","keys","_getXhrs","ckid","ckXhr","abort","_cancelled","moveInProgressToRemaining","connectionManager","free","hasMoreParts","nextPart","nextIdx","_getTotalChunks","reevaluateChunking","sendNext","inProgressChunks","available","uploadChunk","inProgressChunkIdx","_maybePersistChunkedState","clearXhr","_open","_openChunks","_waiting","openChunkEntriesCount","openChunksCount","openChunkIndexes","dontAllowNext","allowNext","waitingIndex","connectionsIndex","getProxyOrBlob","getWaitingOrConnected","waitingOrConnected","chunks","parseInt","isUsingConnection","pop","openChunksEntry","simple","uploadFile","maybeNewUuid","_clearXhrs","getProxy","initHandler","handlerType","traditional","handlerModuleSubtype","_removeExpiredChunkingRecords","isDeferredEligibleForUpload","maybeDefer","generatedBlob","updateBlob","maybeSendDeferredFiles","errorResponse","idsInGroup","uploadedThisId","idInGroup","now","originalResponse","_shouldChunkThisFile","blobToUpload","cancelRetVal","isProxied","isResumable","WindowReceiveMessage","callbackWrapperDetachers","receiveMessage","onMessageCallbackWrapper","stopReceivingMessages","detacher","FormUploadHandler","expungeFile","detachLoadEvents","isCors","postMessageCallbackTimers","corsMessageReceiver","iframe","getElementById","_getIframeName","getFileIdForIframeName","iframeName","initIframeForUpload","body","registerPostMessageCallback","onloadCallbacks","onloadCallback","_parseJsonResponse","_detachLoadEvent","formHandlerInstanceId","_attachLoadEvent","responseDescriptor","contentDocument","_createIframe","_initFormForUpload","targetName","innerHtmlOrMessage","XhrUploadHandler","xhrId","ajaxRequester","_getAjaxRequester","chunkFiles","resumeEnabled","blobOrProxy","_initTempState","_maybePrepareForResume","cachedChunks","tempState","xhrs","ajaxRequesters","responseParser","lastChunkIdx","_getXhr","resumableFilesData","_iterateResumeRecords","notResumable","optInProgress","optRemaining","reverse","paused","totalChunks","parts","newBlob","chunkId","_createXhr","optChunkIdx","_registerXhr","chunkIndex","fileSize","startBytes","endBytes","part","count","startByte","endByte","_getLocalStorageId","formatVersion","_getMimeType","_getPersistableData","ceil","_markNotResumable","localStorageId","removeItem","persistedData","lastUpdated","_registerProgressHandler","progressCalculator","totalSuccessfullyLoadedForFile","loadedForRequest","totalForRequest","estActualChunkLoaded","totalLoadedForFile","chunkLoaded","optAjaxRequester","xhrsId","expirationDays","expirationDate","setDate","getDate","getMandatedParams","_method","requester","detectSubsampling","img","ctx","iw","naturalWidth","ih","naturalHeight","width","getContext","drawImage","getImageData","detectVerticalSquash","alpha","ratio","sy","ey","py","renderImageToDataURL","doSquash","renderImageToCanvas","maybeCalculateDownsampledDimensions","maxPixels","origHeight","origWidth","newHeight","sqrt","newWidth","modifiedDimensions","save","resize","renderImageToCanvasWithCustomResizer","imageHeight","imageWidth","orientation","targetHeight","targetWidth","transformCoordinate","tmpCtx","sx","dx","d","tmpCanvas","vertSquashRatio","dw","dh","dy","clearRect","restore","qqImageRendered","resizeInfo","sourceCanvas","sourceCanvasContext","targetCanvas","translate","rotate","PI","MegaPixImage","srcImage","errorCallback","Image","URL","createObjectURL","webkitURL","src","listeners","imageLoadListeners","render","opt","imgWidth","imgHeight","optionsKey","optionsValue","oldTargetSrc","onrender","isImg","isCanvas","isImgCorsSupported","crossOrigin","isCanvasSupported","determineMimeOfFileName","nameWithPath","pathSegments","isCrossOrigin","targetProtocol","targetHostname","targetPort","targetAnchor","href","protocol","port","hostname","location","registerImgLoadListeners","registerCanvasDrawImageListener","registerThumbnailRenderedListener","registered","draw","drawPreview","Identify","megapixErrorHandler","isPreviewable","dummyExif","exif","Exif","mpImg","Orientation","failureMsg","drawOnCanvasOrImgFromUrl","canvasOrImg","tempImg","tempImgRender","drawOnImgFromUrlWithCssScaling","drawFromUrl","fileBlobOrUrl","_testing","parseLittleEndian","hex","pow","seekToApp1","offset","theOffset","thePromise","segmentLength","getApp1Offset","isLittleEndian","app1Start","getDirEntryCount","littleEndian","getIfd","dirEntries","getDirEntries","ifdHex","getTagValues","TAG_VAL_OFFSET","tagsToFind","TAG_IDS","vals","tagValHex","tagValLength","idHex","tagsToFindIdx","TAG_INFO","274","parser","onParseFailure","app1Offset","dirEntryCount","tagValues","isIdentifiable","magicBytes","questionableBytes","identifiable","magicBytesEntries","magicBytesArrayEntry","previewable","isPreviewableSync","PREVIEWABLE_MIME_TYPES","fileMime","isRecognizedImage","image/jpeg","image/gif","image/png","image/bmp","image/tiff","hasNonZeroLimits","limits","atLeastOne","limit","getWidthHeight","sizeDetermination","getFailingLimit","dimensions","failingLimit","limitName","limitValue","limitMatcher","dimensionPropName","actualValue","validationEffort","isJsonResponseValid","handleFileItems","fileItems","someItemsIgnored","err","refreshEffort","refreshCompleteCallback","requesterOptions","SessionAjaxRequester","queryServer","responseText","includeOriginal","failedToScaleText","_getSortedSizes","getFileRecords","originalFileUuid","originalFileName","originalBlobOrBlobData","records","originalBlob","sizeRecord","outputType","_determineOutputType","requestedType","refType","_getName","_generateScaledImage","failedText","api","scaledIds","originalId","record","blobSize","scaledId","qqparentuuid","qqparentsize","param","scalingEffort","scalingOptions","scaler","referenceType","scaledVersionProperties","startOfExt","versionType","scaledName","scaledExt","nameAppendage","sort","a","b","sourceFile","imageGenerator","scaledImageDataUri","signalSuccess","_insertExifHeader","scaledImageDataUriWithExif","originalImage","reader","insertionEffort","originalImageDataUri","ExifRestorer","readAsDataURL","_dataUriToBlob","_createBlob","KEY_STR","encode64","chr1","chr2","enc1","enc2","enc3","output","chr3","enc4","isNaN","origFileBase64","resizedFileBase64","expectedBase64Header","rawImage","decode64","segments","slice2Segments","exifManipulation","exifArray","getExifArray","newImageArray","insertExif","aBuffer","seg","x","imageData","buf","separatePoint","mae","ato","array","rawImageArray","head","endPoint","base64test","perFileProgress","totalLoaded","totalSize","lastLoadedSent","lastTotalSent","callbackProxy","noRetryableFiles","retryable","none","failedId","updateTotalProgress","onNew","newLoaded","newTotal","oldLoaded","oldTotal","isImage","registerPasteHandler","detachPasteHandler","clipboardData","getAsFile","unregisterPasteHandler","startUpload","determineNewEndpoint","formEl","validateForm","nativeSubmit","checkValidity","maybeUploadOnSubmit","submit","determineFormEl","_form2Obj","notIrrelevantType","irrelevantTypes","radioOrCheckbox","ignoreValue","checked","selectValue","select","selected","elements","CryptoJS","C","C_lib","lib","Base","F","overrides","subtype","mixIn","$super","instance","properties","propertyName","clone","WordArray","words","sigBytes","encoder","Hex","wordArray","thisWords","thatWords","thisSigBytes","thatSigBytes","clamp","thatByte","nBytes","C_enc","enc","hexChars","bite","hexStr","hexStrLength","Latin1","latin1Chars","fromCharCode","latin1Str","latin1StrLength","Utf8","decodeURIComponent","escape","utf8Str","unescape","BufferedBlockAlgorithm","_data","_nDataBytes","_append","_process","doFlush","dataWords","dataSigBytes","blockSize","blockSizeBytes","nBlocksReady","_minBufferSize","nWordsReady","nBytesReady","min","_doProcessBlock","processedWords","C_algo","Hasher","cfg","_doReset","update","messageUpdate","hash","_doFinalize","_createHelper","hasher","_createHmacHelper","HMAC","algo","Base64","map","_map","base64Chars","byte1","byte2","byte3","triplet","j","paddingChar","base64Str","base64StrLength","paddingIndex","bits1","bits2","_hasher","hasherBlockSize","hasherBlockSizeBytes","oKey","_oKey","iKey","_iKey","oKeyWords","iKeyWords","innerHash","hmac","W","SHA1","_hash","M","H","n","t","nBitsTotal","nBitsLeft","floor","HmacSHA1","K","isPrime","sqrtN","factor","getFractionalBits","nPrime","SHA256","f","g","h","gamma0x","gamma0","gamma1x","gamma1","ch","maj","sigma0","sigma1","t1","t2","HmacSHA256","superInit","subInit","typedArray","Int8Array","Uint8ClampedArray","Int16Array","Uint16Array","Int32Array","Uint32Array","Float32Array","Float64Array","byteOffset","byteLength","typedArrayByteLength","s3","util","ALGORITHM_PARAM_NAME","AWS_PARAM_PREFIX","CREDENTIAL_PARAM_NAME","DATE_PARAM_NAME","REDUCED_REDUNDANCY_PARAM_NAME","REDUCED_REDUNDANCY_PARAM_VALUE","SERVER_SIDE_ENCRYPTION_PARAM_NAME","SERVER_SIDE_ENCRYPTION_PARAM_VALUE","SESSION_TOKEN_PARAM_NAME","V4_ALGORITHM_PARAM_VALUE","V4_SIGNATURE_PARAM_NAME","CASE_SENSITIVE_PARAM_NAMES","UNSIGNABLE_REST_HEADER_NAMES","UNPREFIXED_PARAM_NAMES","getBucket","bucket","patterns","pattern","_getPrefixedParamName","getPolicy","policy","conditions","date","drift","clockDrift","accessKey","acl","expectedStatus","sessionToken","successRedirectUrl","getSuccessRedirectAbsoluteUrl","minFileSize","maxFileSize","reducedRedundancy","region","serverSideEncryption","signatureVersion","expiration","getPolicyExpirationDate","Content-Type","success_action_status","success_action_redirect","getV4CredentialsString","getV4PolicyDate","awsParamName","enforceSizeLimits","refreshPolicyCredentials","newSessionToken","sessionTokenFound","oldCondIdx","oldCondObj","oldCondName","oldCondVal","generateAwsParams","signPolicyCallback","policyJson","awsParams","customParams","AWSAccessKeyId","policyAndSignature","updatedAccessKey","updatedSessionToken","signature","minSize","adjustedMinSize","adjustedMaxSize","adjustedDate","getPolicyDate","getCredentialsDate","getUTCFullYear","getUTCMonth","getUTCDate","_minutesToAdd_","pad","minutesToAdd","setMinutes","getMinutes","toISOString","number","getUTCHours","getUTCMinutes","getUTCSeconds","getUTCMilliseconds","parseIframeResponse","doc","contentWindow","queryString","search","etag","targetAnchorContainer","encodeQueryStringParam","percentEncoded","uriEscape","uriEscapePath","nonTraditionalBasePublicApi","setUploadSuccessParams","_uploadSuccessParamsStore","setUploadSuccessEndpoint","_uploadSuccessEndpointStore","nonTraditionalBasePrivateApi","submitSuccessRequest","successAjaxRequester","onCompleteArgs","successEndpoint","successCustomHeaders","uploadSuccess","successMethod","uploadSuccessParams","fileParams","onSuccessFromServer","successRequestResult","_failedSuccessRequestCallbacks","onFailureFromServer","UploadSuccessAjaxRequester","_getEndpointSpecificParams","sendSuccessRequest","successRequestCallback","objectProperties","host","credentials","secretKey","iframeSupport","localBlankPagePath","onCredentialsExpired","setCredentials","_currentCredentials","_aclStore","_cannedKeys","_cannedBuckets","_buckets","_hosts","getKey","ignoreEmpty","setAcl","aclStore","_determineBucket","getHost","_determineHost","getKeyName","_determineKeyName","maxSizeLimit","oldParams","modifiedParams","paramName","credentialsProvider","onExpired","updateCredentials","_determineObjectPropertyValue","property","maybe","keynameLogic","onGetKeynameFailure","onGetKeynameSuccess","keyname","keynameToUse","_handleKeynameFunction","keynameFunc","successCallback","failureCallback","getResponseHeader","s3Key","s3Bucket","RequestSigner","handleSignatureReceived","responseJson","pendingSignatureData","pendingSignatures","signatureConstructor","invalid","expectingPolicy","generateHeaders","getStringToSignArtifacts","requestInfo","endOfUrl","signatureSpec","toSign","headerNames","headersStr","generateStringToSign","contentMd5","headerIndexesToRemove","headerName","headerIdx","hashedContent","v2","getStringToSign","v4","signedHeaders","getSignedHeaders","hashed","toSignRaw","raw","thisSignatureRequester","REQUEST_TYPE","MULTIPART_ABORT","uploadId","MULTIPART_INITIATE","MULTIPART_COMPLETE","MULTIPART_UPLOAD","partNum","getEncodedHashedPayload","content","Host","determineSignatureClientSide","toBeSigned","signatureEffort","updatedHeaders","getHeaders","getToSign","signatureArtifacts","signApiRequest","stringToSign","signPolicy","Authorization","getRequestDate","getEndOfUrl","headersWordArray","headersHmacSha1","headersHmacSha1Base64","policyStr","policyWordArray","base64Policy","policyHmacSha1","policyHmacSha1Base64","getCanonicalQueryString","endOfUri","encodedQueryParams","encodedQueryParamNames","queryStrings","queryParamIdx","canonicalQueryString","nameAndVal","paramVal","forEach","encodedQueryParamName","getCanonicalRequest","getCanonicalUri","onloadend","DONE","getScope","canonicalRequest","hashedRequest","scope","stringToSignTemplate","dateKey","dateRegionKey","dateRegionServiceKey","signingKey","headersPattern","matches","credentialPattern","credentialCondition","credential","condition","getSignature","queryParams","stringToSignRaw","constructStringToSign","artifacts","theHeaders","withUploadId","theUploadId","withContent","theContent","withContentType","theContentType","withPartNum","thePartNum","toUTCString","_artifacts_","handleSuccessResponse","parsedResponse","pendingRequests","successIndicator","failureIndicator","InitiateMultipartAjaxRequester","getSignatureAjaxRequester","getContentType","handleInitiateRequestComplete","uploadIdElements","messageElements","pendingInitiateRequests","domParser","DOMParser","responseDoc","parseFromString","CompleteMultipartAjaxRequester","handleCompleteRequestComplete","pendingCompleteRequests","bucketEls","keyEls","getCompleteRequestBody","etagEntries","implementation","createDocument","etagEntry","partEl","partNumEl","partNumTextEl","createTextNode","etagTextEl","etagEl","XMLSerializer","serializeToString","AbortMultipartAjaxRequester","handleAbortRequestComplete","awsErrorMsg","errorEls","onGetBucket","onGetHost","onGetKeyName","combine","etagMap","etags","requesters","completeMultipart","initHeaders","urlSafe","restSignature","put","domain","track","setup","uploadIdPromise","initiateMultipart","abortMultipart","policySignature","initParams","toSend","cachedBucket","cachedHost","shouldReset","keyName","encodedKey","parsedErrorProps","parseError","awsResponseXml","codeEls","messageEls","parsedDoc","errorDetails","existedInLocalStorage","baseStorageId","bucketName","isValidResponse","innerHtml","responseData","createForm","handleFinishedUpload","handleUpload","uploadDroppedFiles","uploadDropZone","filesAsArray","dropLog","dropDisabled","processingDroppedFilesComplete","getElement","traverseFileTree","parseEntryPromise","fullPath","indexOfNameInFullPath","qqPath","droppedFiles","fileError","isDirectory","getFilesInDirectory","entriesLeft","accumEntries","existingPromise","dirReader","createReader","readEntries","newEntries","handleDataTransfer","pendingFolderPromises","handleDataTransferPromise","processingDroppedFiles","allowMultipleItems","dropError","setupDropzone","dropArea","dropZone","UploadDropZone","HIDE_ZONES_EVENT_NAME","onEnter","dropActive","stopPropagation","onLeaveNotDescendants","onDrop","HIDE_BEFORE_ENTER_ATTR","uploadDropZones","isFileDrag","dragEvent","fileDrag","types","leavingDocumentOut","relatedTarget","y","setupDragDrop","dropZones","maybeHideDropZones","HTMLElement","setupExtraDropzone","removeDropzone","dzs","targetEl","errorSpecifics","dragoverShouldBeCanceled","disableDropOutside","dropOutsideDisabled","dropEffect","isValidFileDrag","effectTest","dt","isSafari","effectAllowed","isOrSetDropDisabled","isDisabled","preventDrop","triggerHidezonesEvent","triggerUsingOldApi","hideZonesEvent","createEvent","initEvent","CustomEvent","dispatchEvent","attachEvents","effect","elementFromPoint","clientX","clientY","uiPublicApi","_parent","_templating","addCacheToDom","clearFiles","addExtraDropzone","_dnd","removeExtraDropzone","getItemByFileId","isHiddenForever","getFileContainer","_setupDragAndDrop","_totalFilesInBatch","_filesInBatchAddedToUi","_setupClickAndEditEventHandlers","formattedFilename","updateFilename","uploadPaused","continued","uploadContinued","getId","fileContainerOrChildEl","getFileId","getDropTarget","qqDropTarget","uiPrivateApi","_removeFileItem","removeFile","_fileButtonsClickHandler","FileButtonsClickHandler","_bindFileButtonsClickEvent","_focusinEventSupported","_isEditFilenameEnabled","_filenameClickHandler","_bindFilenameClickEvent","_filenameInputFocusInHandler","_bindFilenameInputFocusInEvent","_filenameInputFocusHandler","_bindFilenameInputFocusEvent","dragAndDrop","extraDropzones","templating","defaultDropZone","getDropZone","showDropProcessing","hideDropProcessing","errorData","lvl","onDeleteFile","onRetry","onPause","onContinue","onGetName","isEditFilenamePossible","FilenameClickHandler","FilenameInputFocusHandler","_filenameEditHandler","onGetUploadStatus","onSetName","onEditingStatusChange","isEditing","qqInput","getEditInput","qqFileContainer","hideFilename","hideEditIcon","showFilename","showEditIcon","markFilenameEditable","hideRetry","setStatusText","_classes","retrying","hidePause","FilenameInputFocusInHandler","hideSpinner","resetTotalProgress","reportDirectoryPaths","qqpath","_addToList","addHandler","updateProgress","hideCancel","hideProgress","waitingForResponse","_displayFileSize","completeUpload","_markFileAsSuccessful","fail","showCancel","isRetryPossible","showRetry","_controlFailureTextDisplay","parentRetVal","newResult","showDeleteButton","_maybeUpdateThumbnail","showSpinner","allowPause","retryNumForDisplay","maxAuto","retryNote","_showCancelLink","showAutoRetryNote","autoRetryNote","resetProgress","_onSubmitDeleteSuccess","forceConfirm","_showDeleteConfirm","_sendDeleteRequest","deletingFailedText","hideDeleteButton","deletingStatusText","confirmMessage","deleteRequestArgs","showConfirm","canned","prependData","prependIndex","dontDisplay","hideScaled","prependFiles","disableCancel","_handledProxyGroup","_clearList","addFileToCache","updateThumbnail","thumbnails","generatePreview","fileSizeOnSubmit","loadedSize","sizeForDisplay","_formatProgress","uploadedSize","formatProgress","mode","responseProperty","failureReason","failedUploadTextDisplay","failUpload","enableTooltip","_showTooltip","showMessage","_setupPastePrompt","namePromptMessage","defaultVal","showPrompt","fileStatus","placeholders","waitUntilResponse","listElement","template","editable","tooManyFilesError","unsupportedBrowser","promptForName","maxCount","notAvailablePath","waitingPath","timeBetweenThumbs","hasDialog","showDialog","confirm","defaultValue","prompt","Templating","templateIdOrEl","containerEl","fileContainerEl","maxThumbs","waitUntilUpdate","thumbnailNotAvailable","waitingForThumbnail","renderFailure","isEditElementsExist","isRetryElementExist","templateHtml","showThumbnails","serverScale","FILE_ID_ATTR","FILE_CLASS_PREFIX","THUMBNAIL_MAX_SIZE_ATTR","THUMBNAIL_SERVER_SCALE_ATTR","HIDE_DROPZONE_ATTR","DROPZPONE_TEXT_ATTR","IN_PROGRESS_CLASS","HIDDEN_FOREVER_CLASS","fileBatch","createDocumentFragment","isCancelDisabled","generatedThumbnails","thumbnailQueueMonitorRunning","thumbGenerationQueue","thumbnailMaxSize","selectorClasses","alertDialog","dialogCancelButton","confirmDialog","dialogMessage","dialogOkButton","promptDialog","drop","list","progressBarContainer","totalProgressBarContainer","totalProgressBar","spinner","continueButton","deleteButton","statusText","editFilenameInput","editNameIcon","dropText","dropProcessing","dropProcessingSpinner","thumbnail","previewGeneration","cachedThumbnailNotAvailableImg","cachedWaitingForThumbnailImg","cacheThumbnailPlaceholders","notAvailableUrl","waitingUrl","updatedImg","displayWaitingImg","waitingImgPlacement","maybeScalePlaceholderViaCss","show","generateNewPreview","getThumbnail","qqThumbnailId","maybeSetDisplayNotAvailableImg","generateNextQueuedPreview","queuedThumbRequest","processUpdateQueuedPreviewRequest","processNewQueuedPreviewRequest","getCancel","getTemplateEl","getContinue","getDialog","getDelete","getDropProcessing","getEditIcon","getPause","getProgress","getRetry","getSpinner","cssClass","previewing","notAvailableImgPlacement","parseAndGetTemplate","scriptEl","scriptHtml","fileListNode","tempTemplateEl","fileListHtml","defaultButton","dropTextEl","uploaderEl","fileTemplate","prependFile","parentEl","beforeEl","optFileOrBlob","relatedThumbnailId","useCachedPreview","showWaitingImg","setProgressBarWidth","percent","bar","progressBarSelector","targetThumbnailId","cachedThumbnailId","targetThumbnail","cachedThumbnail","hideTotalProgress","cantRenderEl","prependInfo","hideForever","batch","thumb","fileEl","fileNameEl","waitingImg","currentNode","getFileList","filenameEl","isFileName","icon","isEditIcon","isEditInput","isCancel","allowContinueButton","isPause","isContinueButton","isDeleteButton","isRetry","textEl","dialog","messageEl","inputEl","cancelBtn","okBtn","closeHandler","cancelClickHandler","okClickHandler","close","showModal","UiEventHandler","s","protectedApi","eventType","srcElement","onHandled","attachTo","getFileIdFromItem","qqFileId","getDisposeSupport","examineEvent","buttonHandlers","buttonType","firstLetterCapButtonType","inheritedInternalApi","handleFilenameEdit","FilenameEditHandler","handleInputFocus","getFilenameSansExtension","filenameSansExt","getOriginalExtension","origName","handleNameUpdate","newFilenameInputEl","origExtension","registerInputBlurHandler","registerInputEnterKeyHandler","keyCode","which","focusInput","focus"],"mappings":"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","file":"s3.jquery.fine-uploader.min.js","sourcesContent":[null,"/*globals jQuery, qq*/\n(function($) {\n    \"use strict\";\n    var $el,\n        pluginOptions = [\"uploaderType\", \"endpointType\"];\n\n    function init(options) {\n        var xformedOpts = transformVariables(options || {}),\n            newUploaderInstance = getNewUploaderInstance(xformedOpts);\n\n        uploader(newUploaderInstance);\n        addCallbacks(xformedOpts, newUploaderInstance);\n\n        return $el;\n    }\n\n    function getNewUploaderInstance(params) {\n        var uploaderType = pluginOption(\"uploaderType\"),\n            namespace = pluginOption(\"endpointType\");\n\n        // If the integrator has defined a specific type of uploader to load, use that, otherwise assume `qq.FineUploader`\n        if (uploaderType) {\n            // We can determine the correct constructor function to invoke by combining \"FineUploader\"\n            // with the upper camel cased `uploaderType` value.\n            uploaderType = uploaderType.charAt(0).toUpperCase() + uploaderType.slice(1).toLowerCase();\n\n            if (namespace) {\n                return new qq[namespace][\"FineUploader\" + uploaderType](params);\n            }\n\n            return new qq[\"FineUploader\" + uploaderType](params);\n        }\n        else {\n            if (namespace) {\n                return new qq[namespace].FineUploader(params);\n            }\n\n            return new qq.FineUploader(params);\n        }\n    }\n\n    function dataStore(key, val) {\n        var data = $el.data(\"fineuploader\");\n\n        if (val) {\n            if (data === undefined) {\n                data = {};\n            }\n            data[key] = val;\n            $el.data(\"fineuploader\", data);\n        }\n        else {\n            if (data === undefined) {\n                return null;\n            }\n            return data[key];\n        }\n    }\n\n    //the underlying Fine Uploader instance is stored in jQuery's data stored, associated with the element\n    // tied to this instance of the plug-in\n    function uploader(instanceToStore) {\n        return dataStore(\"uploader\", instanceToStore);\n    }\n\n    function pluginOption(option, optionVal) {\n        return dataStore(option, optionVal);\n    }\n\n    // Implement all callbacks defined in Fine Uploader as functions that trigger appropriately names events and\n    // return the result of executing the bound handler back to Fine Uploader\n    function addCallbacks(transformedOpts, newUploaderInstance) {\n        var callbacks = transformedOpts.callbacks = {};\n\n        $.each(newUploaderInstance._options.callbacks, function(prop, nonJqueryCallback) {\n            var name, callbackEventTarget;\n\n            name = /^on(\\w+)/.exec(prop)[1];\n            name = name.substring(0, 1).toLowerCase() + name.substring(1);\n            callbackEventTarget = $el;\n\n            callbacks[prop] = function() {\n                var originalArgs = Array.prototype.slice.call(arguments),\n                    transformedArgs = [],\n                    nonJqueryCallbackRetVal, jqueryEventCallbackRetVal;\n\n                $.each(originalArgs, function(idx, arg) {\n                    transformedArgs.push(maybeWrapInJquery(arg));\n                });\n\n                nonJqueryCallbackRetVal = nonJqueryCallback.apply(this, originalArgs);\n\n                try {\n                    jqueryEventCallbackRetVal = callbackEventTarget.triggerHandler(name, transformedArgs);\n                }\n                catch (error) {\n                    qq.log(\"Caught error in Fine Uploader jQuery event handler: \" + error.message, \"error\");\n                }\n\n                /*jshint -W116*/\n                if (nonJqueryCallbackRetVal != null) {\n                    return nonJqueryCallbackRetVal;\n                }\n                return jqueryEventCallbackRetVal;\n            };\n        });\n\n        newUploaderInstance._options.callbacks = callbacks;\n    }\n\n    //transform jQuery objects into HTMLElements, and pass along all other option properties\n    function transformVariables(source, dest) {\n        var xformed, arrayVals;\n\n        if (dest === undefined) {\n            if (source.uploaderType !== \"basic\") {\n                xformed = { element: $el[0] };\n            }\n            else {\n                xformed = {};\n            }\n        }\n        else {\n            xformed = dest;\n        }\n\n        $.each(source, function(prop, val) {\n            if ($.inArray(prop, pluginOptions) >= 0) {\n                pluginOption(prop, val);\n            }\n            else if (val instanceof $) {\n                xformed[prop] = val[0];\n            }\n            else if ($.isPlainObject(val)) {\n                xformed[prop] = {};\n                transformVariables(val, xformed[prop]);\n            }\n            else if ($.isArray(val)) {\n                arrayVals = [];\n                $.each(val, function(idx, arrayVal) {\n                    var arrayObjDest = {};\n\n                    if (arrayVal instanceof $) {\n                        $.merge(arrayVals, arrayVal);\n                    }\n                    else if ($.isPlainObject(arrayVal)) {\n                        transformVariables(arrayVal, arrayObjDest);\n                        arrayVals.push(arrayObjDest);\n                    }\n                    else {\n                        arrayVals.push(arrayVal);\n                    }\n                });\n                xformed[prop] = arrayVals;\n            }\n            else {\n                xformed[prop] = val;\n            }\n        });\n\n        if (dest === undefined) {\n            return xformed;\n        }\n    }\n\n    function isValidCommand(command) {\n        return $.type(command) === \"string\" &&\n            !command.match(/^_/) && //enforce private methods convention\n            uploader()[command] !== undefined;\n    }\n\n    // Assuming we have already verified that this is a valid command, call the associated function in the underlying\n    // Fine Uploader instance (passing along the arguments from the caller) and return the result of the call back to the caller\n    function delegateCommand(command) {\n        var xformedArgs = [],\n            origArgs = Array.prototype.slice.call(arguments, 1),\n            retVal;\n\n        transformVariables(origArgs, xformedArgs);\n\n        retVal = uploader()[command].apply(uploader(), xformedArgs);\n\n        return maybeWrapInJquery(retVal);\n    }\n\n    // If the value is an `HTMLElement` or `HTMLDocument`, wrap it in a `jQuery` object\n    function maybeWrapInJquery(val) {\n        var transformedVal = val;\n\n        // If the command is returning an `HTMLElement` or `HTMLDocument`, wrap it in a `jQuery` object\n        /*jshint -W116*/\n        if (val != null && typeof val === \"object\" &&\n           (val.nodeType === 1 || val.nodeType === 9) && val.cloneNode) {\n\n            transformedVal = $(val);\n        }\n\n        return transformedVal;\n    }\n\n    $.fn.fineUploader = function(optionsOrCommand) {\n        var self = this, selfArgs = arguments, retVals = [];\n\n        this.each(function(index, el) {\n            $el = $(el);\n\n            if (uploader() && isValidCommand(optionsOrCommand)) {\n                retVals.push(delegateCommand.apply(self, selfArgs));\n\n                if (self.length === 1) {\n                    return false;\n                }\n            }\n            else if (typeof optionsOrCommand === \"object\" || !optionsOrCommand) {\n                init.apply(self, selfArgs);\n            }\n            else {\n                $.error(\"Method \" +  optionsOrCommand + \" does not exist on jQuery.fineUploader\");\n            }\n        });\n\n        if (retVals.length === 1) {\n            return retVals[0];\n        }\n        else if (retVals.length > 1) {\n            return retVals;\n        }\n\n        return this;\n    };\n\n}(jQuery));\n","/*globals jQuery, qq*/\n(function($) {\n    \"use strict\";\n    var rootDataKey = \"fineUploaderDnd\",\n        $el;\n\n    function init(options) {\n        if (!options) {\n            options = {};\n        }\n\n        options.dropZoneElements = [$el];\n        var xformedOpts = transformVariables(options);\n        addCallbacks(xformedOpts);\n        dnd(new qq.DragAndDrop(xformedOpts));\n\n        return $el;\n    }\n\n    function dataStore(key, val) {\n        var data = $el.data(rootDataKey);\n\n        if (val) {\n            if (data === undefined) {\n                data = {};\n            }\n            data[key] = val;\n            $el.data(rootDataKey, data);\n        }\n        else {\n            if (data === undefined) {\n                return null;\n            }\n            return data[key];\n        }\n    }\n\n    function dnd(instanceToStore) {\n        return dataStore(\"dndInstance\", instanceToStore);\n    }\n\n    function addCallbacks(transformedOpts) {\n        var callbacks = transformedOpts.callbacks = {};\n\n        $.each(new qq.DragAndDrop.callbacks(), function(prop, func) {\n            var name = prop,\n                $callbackEl;\n\n            $callbackEl = $el;\n\n            callbacks[prop] = function() {\n                var args = Array.prototype.slice.call(arguments),\n                    jqueryHandlerResult = $callbackEl.triggerHandler(name, args);\n\n                return jqueryHandlerResult;\n            };\n        });\n    }\n\n    //transform jQuery objects into HTMLElements, and pass along all other option properties\n    function transformVariables(source, dest) {\n        var xformed, arrayVals;\n\n        if (dest === undefined) {\n            xformed = {};\n        }\n        else {\n            xformed = dest;\n        }\n\n        $.each(source, function(prop, val) {\n            if (val instanceof $) {\n                xformed[prop] = val[0];\n            }\n            else if ($.isPlainObject(val)) {\n                xformed[prop] = {};\n                transformVariables(val, xformed[prop]);\n            }\n            else if ($.isArray(val)) {\n                arrayVals = [];\n                $.each(val, function(idx, arrayVal) {\n                    if (arrayVal instanceof $) {\n                        $.merge(arrayVals, arrayVal);\n                    }\n                    else {\n                        arrayVals.push(arrayVal);\n                    }\n                });\n                xformed[prop] = arrayVals;\n            }\n            else {\n                xformed[prop] = val;\n            }\n        });\n\n        if (dest === undefined) {\n            return xformed;\n        }\n    }\n\n    function isValidCommand(command) {\n        return $.type(command) === \"string\" &&\n            command === \"dispose\" &&\n            dnd()[command] !== undefined;\n    }\n\n    function delegateCommand(command) {\n        var xformedArgs = [], origArgs = Array.prototype.slice.call(arguments, 1);\n        transformVariables(origArgs, xformedArgs);\n        return dnd()[command].apply(dnd(), xformedArgs);\n    }\n\n    $.fn.fineUploaderDnd = function(optionsOrCommand) {\n        var self = this, selfArgs = arguments, retVals = [];\n\n        this.each(function(index, el) {\n            $el = $(el);\n\n            if (dnd() && isValidCommand(optionsOrCommand)) {\n                retVals.push(delegateCommand.apply(self, selfArgs));\n\n                if (self.length === 1) {\n                    return false;\n                }\n            }\n            else if (typeof optionsOrCommand === \"object\" || !optionsOrCommand) {\n                init.apply(self, selfArgs);\n            }\n            else {\n                $.error(\"Method \" +  optionsOrCommand + \" does not exist in Fine Uploader's DnD module.\");\n            }\n        });\n\n        if (retVals.length === 1) {\n            return retVals[0];\n        }\n        else if (retVals.length > 1) {\n            return retVals;\n        }\n\n        return this;\n    };\n\n}(jQuery));\n","/*globals jQuery*/\n/**\n * Simply an alias for the `fineUploader` plug-in wrapper, but hides the required `endpointType` option from the\n * integrator.  I thought it may be confusing to convey to the integrator that, when using Fine Uploader in S3 mode,\n * you need to specify an `endpointType` with a value of S3, and perhaps an `uploaderType` with a value of \"basic\" if\n * you want to use basic mode when uploading directly to S3 as well.  So, you can use this plug-in alias and not worry\n * about the `endpointType` option at all.\n */\n(function($) {\n    \"use strict\";\n\n    $.fn.fineUploaderS3 = function(optionsOrCommand) {\n        if (typeof optionsOrCommand === \"object\") {\n\n            // This option is used to tell the plug-in wrapper to instantiate the appropriate S3-namespace modules.\n            optionsOrCommand.endpointType = \"s3\";\n        }\n\n        return $.fn.fineUploader.apply(this, arguments);\n    };\n\n}(jQuery));\n","/*globals window, navigator, document, FormData, File, HTMLInputElement, XMLHttpRequest, Blob, Storage, ActiveXObject */\n/* jshint -W079 */\nvar qq = function(element) {\n    \"use strict\";\n\n    return {\n        hide: function() {\n            element.style.display = \"none\";\n            return this;\n        },\n\n        /** Returns the function which detaches attached event */\n        attach: function(type, fn) {\n            if (element.addEventListener) {\n                element.addEventListener(type, fn, false);\n            } else if (element.attachEvent) {\n                element.attachEvent(\"on\" + type, fn);\n            }\n            return function() {\n                qq(element).detach(type, fn);\n            };\n        },\n\n        detach: function(type, fn) {\n            if (element.removeEventListener) {\n                element.removeEventListener(type, fn, false);\n            } else if (element.attachEvent) {\n                element.detachEvent(\"on\" + type, fn);\n            }\n            return this;\n        },\n\n        contains: function(descendant) {\n            // The [W3C spec](http://www.w3.org/TR/domcore/#dom-node-contains)\n            // says a `null` (or ostensibly `undefined`) parameter\n            // passed into `Node.contains` should result in a false return value.\n            // IE7 throws an exception if the parameter is `undefined` though.\n            if (!descendant) {\n                return false;\n            }\n\n            // compareposition returns false in this case\n            if (element === descendant) {\n                return true;\n            }\n\n            if (element.contains) {\n                return element.contains(descendant);\n            } else {\n                /*jslint bitwise: true*/\n                return !!(descendant.compareDocumentPosition(element) & 8);\n            }\n        },\n\n        /**\n         * Insert this element before elementB.\n         */\n        insertBefore: function(elementB) {\n            elementB.parentNode.insertBefore(element, elementB);\n            return this;\n        },\n\n        remove: function() {\n            element.parentNode.removeChild(element);\n            return this;\n        },\n\n        /**\n         * Sets styles for an element.\n         * Fixes opacity in IE6-8.\n         */\n        css: function(styles) {\n            /*jshint eqnull: true*/\n            if (element.style == null) {\n                throw new qq.Error(\"Can't apply style to node as it is not on the HTMLElement prototype chain!\");\n            }\n\n            /*jshint -W116*/\n            if (styles.opacity != null) {\n                if (typeof element.style.opacity !== \"string\" && typeof (element.filters) !== \"undefined\") {\n                    styles.filter = \"alpha(opacity=\" + Math.round(100 * styles.opacity) + \")\";\n                }\n            }\n            qq.extend(element.style, styles);\n\n            return this;\n        },\n\n        hasClass: function(name, considerParent) {\n            var re = new RegExp(\"(^| )\" + name + \"( |$)\");\n            return re.test(element.className) || !!(considerParent && re.test(element.parentNode.className));\n        },\n\n        addClass: function(name) {\n            if (!qq(element).hasClass(name)) {\n                element.className += \" \" + name;\n            }\n            return this;\n        },\n\n        removeClass: function(name) {\n            var re = new RegExp(\"(^| )\" + name + \"( |$)\");\n            element.className = element.className.replace(re, \" \").replace(/^\\s+|\\s+$/g, \"\");\n            return this;\n        },\n\n        getByClass: function(className, first) {\n            var candidates,\n                result = [];\n\n            if (first && element.querySelector) {\n                return element.querySelector(\".\" + className);\n            }\n            else if (element.querySelectorAll) {\n                return element.querySelectorAll(\".\" + className);\n            }\n\n            candidates = element.getElementsByTagName(\"*\");\n\n            qq.each(candidates, function(idx, val) {\n                if (qq(val).hasClass(className)) {\n                    result.push(val);\n                }\n            });\n            return first ? result[0] : result;\n        },\n\n        getFirstByClass: function(className) {\n            return qq(element).getByClass(className, true);\n        },\n\n        children: function() {\n            var children = [],\n                child = element.firstChild;\n\n            while (child) {\n                if (child.nodeType === 1) {\n                    children.push(child);\n                }\n                child = child.nextSibling;\n            }\n\n            return children;\n        },\n\n        setText: function(text) {\n            element.innerText = text;\n            element.textContent = text;\n            return this;\n        },\n\n        clearText: function() {\n            return qq(element).setText(\"\");\n        },\n\n        // Returns true if the attribute exists on the element\n        // AND the value of the attribute is NOT \"false\" (case-insensitive)\n        hasAttribute: function(attrName) {\n            var attrVal;\n\n            if (element.hasAttribute) {\n\n                if (!element.hasAttribute(attrName)) {\n                    return false;\n                }\n\n                /*jshint -W116*/\n                return (/^false$/i).exec(element.getAttribute(attrName)) == null;\n            }\n            else {\n                attrVal = element[attrName];\n\n                if (attrVal === undefined) {\n                    return false;\n                }\n\n                /*jshint -W116*/\n                return (/^false$/i).exec(attrVal) == null;\n            }\n        }\n    };\n};\n\n(function() {\n    \"use strict\";\n\n    qq.canvasToBlob = function(canvas, mime, quality) {\n        return qq.dataUriToBlob(canvas.toDataURL(mime, quality));\n    };\n\n    qq.dataUriToBlob = function(dataUri) {\n        var arrayBuffer, byteString,\n            createBlob = function(data, mime) {\n                var BlobBuilder = window.BlobBuilder ||\n                        window.WebKitBlobBuilder ||\n                        window.MozBlobBuilder ||\n                        window.MSBlobBuilder,\n                    blobBuilder = BlobBuilder && new BlobBuilder();\n\n                if (blobBuilder) {\n                    blobBuilder.append(data);\n                    return blobBuilder.getBlob(mime);\n                }\n                else {\n                    return new Blob([data], {type: mime});\n                }\n            },\n            intArray, mimeString;\n\n        // convert base64 to raw binary data held in a string\n        if (dataUri.split(\",\")[0].indexOf(\"base64\") >= 0) {\n            byteString = atob(dataUri.split(\",\")[1]);\n        }\n        else {\n            byteString = decodeURI(dataUri.split(\",\")[1]);\n        }\n\n        // extract the MIME\n        mimeString = dataUri.split(\",\")[0]\n            .split(\":\")[1]\n            .split(\";\")[0];\n\n        // write the bytes of the binary string to an ArrayBuffer\n        arrayBuffer = new ArrayBuffer(byteString.length);\n        intArray = new Uint8Array(arrayBuffer);\n        qq.each(byteString, function(idx, character) {\n            intArray[idx] = character.charCodeAt(0);\n        });\n\n        return createBlob(arrayBuffer, mimeString);\n    };\n\n    qq.log = function(message, level) {\n        if (window.console) {\n            if (!level || level === \"info\") {\n                window.console.log(message);\n            }\n            else\n            {\n                if (window.console[level]) {\n                    window.console[level](message);\n                }\n                else {\n                    window.console.log(\"<\" + level + \"> \" + message);\n                }\n            }\n        }\n    };\n\n    qq.isObject = function(variable) {\n        return variable && !variable.nodeType && Object.prototype.toString.call(variable) === \"[object Object]\";\n    };\n\n    qq.isFunction = function(variable) {\n        return typeof (variable) === \"function\";\n    };\n\n    /**\n     * Check the type of a value.  Is it an \"array\"?\n     *\n     * @param value value to test.\n     * @returns true if the value is an array or associated with an `ArrayBuffer`\n     */\n    qq.isArray = function(value) {\n        return Object.prototype.toString.call(value) === \"[object Array]\" ||\n            (value && window.ArrayBuffer && value.buffer && value.buffer.constructor === ArrayBuffer);\n    };\n\n    // Looks for an object on a `DataTransfer` object that is associated with drop events when utilizing the Filesystem API.\n    qq.isItemList = function(maybeItemList) {\n        return Object.prototype.toString.call(maybeItemList) === \"[object DataTransferItemList]\";\n    };\n\n    // Looks for an object on a `NodeList` or an `HTMLCollection`|`HTMLFormElement`|`HTMLSelectElement`\n    // object that is associated with collections of Nodes.\n    qq.isNodeList = function(maybeNodeList) {\n        return Object.prototype.toString.call(maybeNodeList) === \"[object NodeList]\" ||\n            // If `HTMLCollection` is the actual type of the object, we must determine this\n            // by checking for expected properties/methods on the object\n            (maybeNodeList.item && maybeNodeList.namedItem);\n    };\n\n    qq.isString = function(maybeString) {\n        return Object.prototype.toString.call(maybeString) === \"[object String]\";\n    };\n\n    qq.trimStr = function(string) {\n        if (String.prototype.trim) {\n            return string.trim();\n        }\n\n        return string.replace(/^\\s+|\\s+$/g, \"\");\n    };\n\n    /**\n     * @param str String to format.\n     * @returns {string} A string, swapping argument values with the associated occurrence of {} in the passed string.\n     */\n    qq.format = function(str) {\n\n        var args =  Array.prototype.slice.call(arguments, 1),\n            newStr = str,\n            nextIdxToReplace = newStr.indexOf(\"{}\");\n\n        qq.each(args, function(idx, val) {\n            var strBefore = newStr.substring(0, nextIdxToReplace),\n                strAfter = newStr.substring(nextIdxToReplace + 2);\n\n            newStr = strBefore + val + strAfter;\n            nextIdxToReplace = newStr.indexOf(\"{}\", nextIdxToReplace + val.length);\n\n            // End the loop if we have run out of tokens (when the arguments exceed the # of tokens)\n            if (nextIdxToReplace < 0) {\n                return false;\n            }\n        });\n\n        return newStr;\n    };\n\n    qq.isFile = function(maybeFile) {\n        return window.File && Object.prototype.toString.call(maybeFile) === \"[object File]\";\n    };\n\n    qq.isFileList = function(maybeFileList) {\n        return window.FileList && Object.prototype.toString.call(maybeFileList) === \"[object FileList]\";\n    };\n\n    qq.isFileOrInput = function(maybeFileOrInput) {\n        return qq.isFile(maybeFileOrInput) || qq.isInput(maybeFileOrInput);\n    };\n\n    qq.isInput = function(maybeInput, notFile) {\n        var evaluateType = function(type) {\n            var normalizedType = type.toLowerCase();\n\n            if (notFile) {\n                return normalizedType !== \"file\";\n            }\n\n            return normalizedType === \"file\";\n        };\n\n        if (window.HTMLInputElement) {\n            if (Object.prototype.toString.call(maybeInput) === \"[object HTMLInputElement]\") {\n                if (maybeInput.type && evaluateType(maybeInput.type)) {\n                    return true;\n                }\n            }\n        }\n        if (maybeInput.tagName) {\n            if (maybeInput.tagName.toLowerCase() === \"input\") {\n                if (maybeInput.type && evaluateType(maybeInput.type)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    };\n\n    qq.isBlob = function(maybeBlob) {\n        if (window.Blob && Object.prototype.toString.call(maybeBlob) === \"[object Blob]\") {\n            return true;\n        }\n    };\n\n    qq.isXhrUploadSupported = function() {\n        var input = document.createElement(\"input\");\n        input.type = \"file\";\n\n        return (\n            input.multiple !== undefined &&\n                typeof File !== \"undefined\" &&\n                typeof FormData !== \"undefined\" &&\n                typeof (qq.createXhrInstance()).upload !== \"undefined\");\n    };\n\n    // Fall back to ActiveX is native XHR is disabled (possible in any version of IE).\n    qq.createXhrInstance = function() {\n        if (window.XMLHttpRequest) {\n            return new XMLHttpRequest();\n        }\n\n        try {\n            return new ActiveXObject(\"MSXML2.XMLHTTP.3.0\");\n        }\n        catch (error) {\n            qq.log(\"Neither XHR or ActiveX are supported!\", \"error\");\n            return null;\n        }\n    };\n\n    qq.isFolderDropSupported = function(dataTransfer) {\n        return dataTransfer.items &&\n            dataTransfer.items.length > 0 &&\n            dataTransfer.items[0].webkitGetAsEntry;\n    };\n\n    qq.isFileChunkingSupported = function() {\n        return !qq.androidStock() && //Android's stock browser cannot upload Blobs correctly\n            qq.isXhrUploadSupported() &&\n            (File.prototype.slice !== undefined || File.prototype.webkitSlice !== undefined || File.prototype.mozSlice !== undefined);\n    };\n\n    qq.sliceBlob = function(fileOrBlob, start, end) {\n        var slicer = fileOrBlob.slice || fileOrBlob.mozSlice || fileOrBlob.webkitSlice;\n\n        return slicer.call(fileOrBlob, start, end);\n    };\n\n    qq.arrayBufferToHex = function(buffer) {\n        var bytesAsHex = \"\",\n            bytes = new Uint8Array(buffer);\n\n        qq.each(bytes, function(idx, byt) {\n            var byteAsHexStr = byt.toString(16);\n\n            if (byteAsHexStr.length < 2) {\n                byteAsHexStr = \"0\" + byteAsHexStr;\n            }\n\n            bytesAsHex += byteAsHexStr;\n        });\n\n        return bytesAsHex;\n    };\n\n    qq.readBlobToHex = function(blob, startOffset, length) {\n        var initialBlob = qq.sliceBlob(blob, startOffset, startOffset + length),\n            fileReader = new FileReader(),\n            promise = new qq.Promise();\n\n        fileReader.onload = function() {\n            promise.success(qq.arrayBufferToHex(fileReader.result));\n        };\n\n        fileReader.onerror = promise.failure;\n\n        fileReader.readAsArrayBuffer(initialBlob);\n\n        return promise;\n    };\n\n    qq.extend = function(first, second, extendNested) {\n        qq.each(second, function(prop, val) {\n            if (extendNested && qq.isObject(val)) {\n                if (first[prop] === undefined) {\n                    first[prop] = {};\n                }\n                qq.extend(first[prop], val, true);\n            }\n            else {\n                first[prop] = val;\n            }\n        });\n\n        return first;\n    };\n\n    /**\n     * Allow properties in one object to override properties in another,\n     * keeping track of the original values from the target object.\n     *\n     * Note that the pre-overriden properties to be overriden by the source will be passed into the `sourceFn` when it is invoked.\n     *\n     * @param target Update properties in this object from some source\n     * @param sourceFn A function that, when invoked, will return properties that will replace properties with the same name in the target.\n     * @returns {object} The target object\n     */\n    qq.override = function(target, sourceFn) {\n        var super_ = {},\n            source = sourceFn(super_);\n\n        qq.each(source, function(srcPropName, srcPropVal) {\n            if (target[srcPropName] !== undefined) {\n                super_[srcPropName] = target[srcPropName];\n            }\n\n            target[srcPropName] = srcPropVal;\n        });\n\n        return target;\n    };\n\n    /**\n     * Searches for a given element (elt) in the array, returns -1 if it is not present.\n     */\n    qq.indexOf = function(arr, elt, from) {\n        if (arr.indexOf) {\n            return arr.indexOf(elt, from);\n        }\n\n        from = from || 0;\n        var len = arr.length;\n\n        if (from < 0) {\n            from += len;\n        }\n\n        for (; from < len; from += 1) {\n            if (arr.hasOwnProperty(from) && arr[from] === elt) {\n                return from;\n            }\n        }\n        return -1;\n    };\n\n    //this is a version 4 UUID\n    qq.getUniqueId = function() {\n        return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function(c) {\n            /*jslint eqeq: true, bitwise: true*/\n            var r = Math.random() * 16 | 0, v = c == \"x\" ? r : (r & 0x3 | 0x8);\n            return v.toString(16);\n        });\n    };\n\n    //\n    // Browsers and platforms detection\n    qq.ie = function() {\n        return navigator.userAgent.indexOf(\"MSIE\") !== -1 ||\n            navigator.userAgent.indexOf(\"Trident\") !== -1;\n    };\n\n    qq.ie7 = function() {\n        return navigator.userAgent.indexOf(\"MSIE 7\") !== -1;\n    };\n\n    qq.ie8 = function() {\n        return navigator.userAgent.indexOf(\"MSIE 8\") !== -1;\n    };\n\n    qq.ie10 = function() {\n        return navigator.userAgent.indexOf(\"MSIE 10\") !== -1;\n    };\n\n    qq.ie11 = function() {\n        return qq.ie() && navigator.userAgent.indexOf(\"rv:11\") !== -1;\n    };\n\n    qq.edge = function() {\n        return navigator.userAgent.indexOf(\"Edge\") >= 0;\n    };\n\n    qq.safari = function() {\n        return navigator.vendor !== undefined && navigator.vendor.indexOf(\"Apple\") !== -1;\n    };\n\n    qq.chrome = function() {\n        return navigator.vendor !== undefined && navigator.vendor.indexOf(\"Google\") !== -1;\n    };\n\n    qq.opera = function() {\n        return navigator.vendor !== undefined && navigator.vendor.indexOf(\"Opera\") !== -1;\n    };\n\n    qq.firefox = function() {\n        return (!qq.edge() && !qq.ie11() && navigator.userAgent.indexOf(\"Mozilla\") !== -1 && navigator.vendor !== undefined && navigator.vendor === \"\");\n    };\n\n    qq.windows = function() {\n        return navigator.platform === \"Win32\";\n    };\n\n    qq.android = function() {\n        return navigator.userAgent.toLowerCase().indexOf(\"android\") !== -1;\n    };\n\n    // We need to identify the Android stock browser via the UA string to work around various bugs in this browser,\n    // such as the one that prevents a `Blob` from being uploaded.\n    qq.androidStock = function() {\n        return qq.android() && navigator.userAgent.toLowerCase().indexOf(\"chrome\") < 0;\n    };\n\n    qq.ios6 = function() {\n        return qq.ios() && navigator.userAgent.indexOf(\" OS 6_\") !== -1;\n    };\n\n    qq.ios7 = function() {\n        return qq.ios() && navigator.userAgent.indexOf(\" OS 7_\") !== -1;\n    };\n\n    qq.ios8 = function() {\n        return qq.ios() && navigator.userAgent.indexOf(\" OS 8_\") !== -1;\n    };\n\n    // iOS 8.0.0\n    qq.ios800 = function() {\n        return qq.ios() && navigator.userAgent.indexOf(\" OS 8_0 \") !== -1;\n    };\n\n    qq.ios = function() {\n        /*jshint -W014 */\n        return navigator.userAgent.indexOf(\"iPad\") !== -1\n            || navigator.userAgent.indexOf(\"iPod\") !== -1\n            || navigator.userAgent.indexOf(\"iPhone\") !== -1;\n    };\n\n    qq.iosChrome = function() {\n        return qq.ios() && navigator.userAgent.indexOf(\"CriOS\") !== -1;\n    };\n\n    qq.iosSafari = function() {\n        return qq.ios() && !qq.iosChrome() && navigator.userAgent.indexOf(\"Safari\") !== -1;\n    };\n\n    qq.iosSafariWebView = function() {\n        return qq.ios() && !qq.iosChrome() && !qq.iosSafari();\n    };\n\n    //\n    // Events\n\n    qq.preventDefault = function(e) {\n        if (e.preventDefault) {\n            e.preventDefault();\n        } else {\n            e.returnValue = false;\n        }\n    };\n\n    /**\n     * Creates and returns element from html string\n     * Uses innerHTML to create an element\n     */\n    qq.toElement = (function() {\n        var div = document.createElement(\"div\");\n        return function(html) {\n            div.innerHTML = html;\n            var element = div.firstChild;\n            div.removeChild(element);\n            return element;\n        };\n    }());\n\n    //key and value are passed to callback for each entry in the iterable item\n    qq.each = function(iterableItem, callback) {\n        var keyOrIndex, retVal;\n\n        if (iterableItem) {\n            // Iterate through [`Storage`](http://www.w3.org/TR/webstorage/#the-storage-interface) items\n            if (window.Storage && iterableItem.constructor === window.Storage) {\n                for (keyOrIndex = 0; keyOrIndex < iterableItem.length; keyOrIndex++) {\n                    retVal = callback(iterableItem.key(keyOrIndex), iterableItem.getItem(iterableItem.key(keyOrIndex)));\n                    if (retVal === false) {\n                        break;\n                    }\n                }\n            }\n            // `DataTransferItemList` & `NodeList` objects are array-like and should be treated as arrays\n            // when iterating over items inside the object.\n            else if (qq.isArray(iterableItem) || qq.isItemList(iterableItem) || qq.isNodeList(iterableItem)) {\n                for (keyOrIndex = 0; keyOrIndex < iterableItem.length; keyOrIndex++) {\n                    retVal = callback(keyOrIndex, iterableItem[keyOrIndex]);\n                    if (retVal === false) {\n                        break;\n                    }\n                }\n            }\n            else if (qq.isString(iterableItem)) {\n                for (keyOrIndex = 0; keyOrIndex < iterableItem.length; keyOrIndex++) {\n                    retVal = callback(keyOrIndex, iterableItem.charAt(keyOrIndex));\n                    if (retVal === false) {\n                        break;\n                    }\n                }\n            }\n            else {\n                for (keyOrIndex in iterableItem) {\n                    if (Object.prototype.hasOwnProperty.call(iterableItem, keyOrIndex)) {\n                        retVal = callback(keyOrIndex, iterableItem[keyOrIndex]);\n                        if (retVal === false) {\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n    };\n\n    //include any args that should be passed to the new function after the context arg\n    qq.bind = function(oldFunc, context) {\n        if (qq.isFunction(oldFunc)) {\n            var args =  Array.prototype.slice.call(arguments, 2);\n\n            return function() {\n                var newArgs = qq.extend([], args);\n                if (arguments.length) {\n                    newArgs = newArgs.concat(Array.prototype.slice.call(arguments));\n                }\n                return oldFunc.apply(context, newArgs);\n            };\n        }\n\n        throw new Error(\"first parameter must be a function!\");\n    };\n\n    /**\n     * obj2url() takes a json-object as argument and generates\n     * a querystring. pretty much like jQuery.param()\n     *\n     * how to use:\n     *\n     *    `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');`\n     *\n     * will result in:\n     *\n     *    `http://any.url/upload?otherParam=value&a=b&c=d`\n     *\n     * @param  Object JSON-Object\n     * @param  String current querystring-part\n     * @return String encoded querystring\n     */\n    qq.obj2url = function(obj, temp, prefixDone) {\n        /*jshint laxbreak: true*/\n        var uristrings = [],\n            prefix = \"&\",\n            add = function(nextObj, i) {\n                var nextTemp = temp\n                    ? (/\\[\\]$/.test(temp)) // prevent double-encoding\n                    ? temp\n                    : temp + \"[\" + i + \"]\"\n                    : i;\n                if ((nextTemp !== \"undefined\") && (i !== \"undefined\")) {\n                    uristrings.push(\n                        (typeof nextObj === \"object\")\n                            ? qq.obj2url(nextObj, nextTemp, true)\n                            : (Object.prototype.toString.call(nextObj) === \"[object Function]\")\n                            ? encodeURIComponent(nextTemp) + \"=\" + encodeURIComponent(nextObj())\n                            : encodeURIComponent(nextTemp) + \"=\" + encodeURIComponent(nextObj)\n                    );\n                }\n            };\n\n        if (!prefixDone && temp) {\n            prefix = (/\\?/.test(temp)) ? (/\\?$/.test(temp)) ? \"\" : \"&\" : \"?\";\n            uristrings.push(temp);\n            uristrings.push(qq.obj2url(obj));\n        } else if ((Object.prototype.toString.call(obj) === \"[object Array]\") && (typeof obj !== \"undefined\")) {\n            qq.each(obj, function(idx, val) {\n                add(val, idx);\n            });\n        } else if ((typeof obj !== \"undefined\") && (obj !== null) && (typeof obj === \"object\")) {\n            qq.each(obj, function(prop, val) {\n                add(val, prop);\n            });\n        } else {\n            uristrings.push(encodeURIComponent(temp) + \"=\" + encodeURIComponent(obj));\n        }\n\n        if (temp) {\n            return uristrings.join(prefix);\n        } else {\n            return uristrings.join(prefix)\n                .replace(/^&/, \"\")\n                .replace(/%20/g, \"+\");\n        }\n    };\n\n    qq.obj2FormData = function(obj, formData, arrayKeyName) {\n        if (!formData) {\n            formData = new FormData();\n        }\n\n        qq.each(obj, function(key, val) {\n            key = arrayKeyName ? arrayKeyName + \"[\" + key + \"]\" : key;\n\n            if (qq.isObject(val)) {\n                qq.obj2FormData(val, formData, key);\n            }\n            else if (qq.isFunction(val)) {\n                formData.append(key, val());\n            }\n            else {\n                formData.append(key, val);\n            }\n        });\n\n        return formData;\n    };\n\n    qq.obj2Inputs = function(obj, form) {\n        var input;\n\n        if (!form) {\n            form = document.createElement(\"form\");\n        }\n\n        qq.obj2FormData(obj, {\n            append: function(key, val) {\n                input = document.createElement(\"input\");\n                input.setAttribute(\"name\", key);\n                input.setAttribute(\"value\", val);\n                form.appendChild(input);\n            }\n        });\n\n        return form;\n    };\n\n    /**\n     * Not recommended for use outside of Fine Uploader since this falls back to an unchecked eval if JSON.parse is not\n     * implemented.  For a more secure JSON.parse polyfill, use Douglas Crockford's json2.js.\n     */\n    qq.parseJson = function(json) {\n        /*jshint evil: true*/\n        if (window.JSON && qq.isFunction(JSON.parse)) {\n            return JSON.parse(json);\n        } else {\n            return eval(\"(\" + json + \")\");\n        }\n    };\n\n    /**\n     * Retrieve the extension of a file, if it exists.\n     *\n     * @param filename\n     * @returns {string || undefined}\n     */\n    qq.getExtension = function(filename) {\n        var extIdx = filename.lastIndexOf(\".\") + 1;\n\n        if (extIdx > 0) {\n            return filename.substr(extIdx, filename.length - extIdx);\n        }\n    };\n\n    qq.getFilename = function(blobOrFileInput) {\n        /*jslint regexp: true*/\n\n        if (qq.isInput(blobOrFileInput)) {\n            // get input value and remove path to normalize\n            return blobOrFileInput.value.replace(/.*(\\/|\\\\)/, \"\");\n        }\n        else if (qq.isFile(blobOrFileInput)) {\n            if (blobOrFileInput.fileName !== null && blobOrFileInput.fileName !== undefined) {\n                return blobOrFileInput.fileName;\n            }\n        }\n\n        return blobOrFileInput.name;\n    };\n\n    /**\n     * A generic module which supports object disposing in dispose() method.\n     * */\n    qq.DisposeSupport = function() {\n        var disposers = [];\n\n        return {\n            /** Run all registered disposers */\n            dispose: function() {\n                var disposer;\n                do {\n                    disposer = disposers.shift();\n                    if (disposer) {\n                        disposer();\n                    }\n                }\n                while (disposer);\n            },\n\n            /** Attach event handler and register de-attacher as a disposer */\n            attach: function() {\n                var args = arguments;\n                /*jslint undef:true*/\n                this.addDisposer(qq(args[0]).attach.apply(this, Array.prototype.slice.call(arguments, 1)));\n            },\n\n            /** Add disposer to the collection */\n            addDisposer: function(disposeFunction) {\n                disposers.push(disposeFunction);\n            }\n        };\n    };\n}());\n","/* globals define, module, global, qq */\n(function() {\n    \"use strict\";\n    if (typeof define === \"function\" && define.amd) {\n        define(function() {\n            return qq;\n        });\n    }\n    else if (typeof module !== \"undefined\" && module.exports) {\n        module.exports = qq;\n    }\n    else {\n        global.qq = qq;\n    }\n}());\n","/* globals qq */\n/**\n * Fine Uploader top-level Error container.  Inherits from `Error`.\n */\n(function() {\n    \"use strict\";\n\n    qq.Error = function(message) {\n        this.message = \"[Fine Uploader \" + qq.version + \"] \" + message;\n    };\n\n    qq.Error.prototype = new Error();\n}());\n","/*global qq */\nqq.version = \"5.13.0\";\n","/* globals qq */\nqq.supportedFeatures = (function() {\n    \"use strict\";\n\n    var supportsUploading,\n        supportsUploadingBlobs,\n        supportsFileDrop,\n        supportsAjaxFileUploading,\n        supportsFolderDrop,\n        supportsChunking,\n        supportsResume,\n        supportsUploadViaPaste,\n        supportsUploadCors,\n        supportsDeleteFileXdr,\n        supportsDeleteFileCorsXhr,\n        supportsDeleteFileCors,\n        supportsFolderSelection,\n        supportsImagePreviews,\n        supportsUploadProgress;\n\n    function testSupportsFileInputElement() {\n        var supported = true,\n            tempInput;\n\n        try {\n            tempInput = document.createElement(\"input\");\n            tempInput.type = \"file\";\n            qq(tempInput).hide();\n\n            if (tempInput.disabled) {\n                supported = false;\n            }\n        }\n        catch (ex) {\n            supported = false;\n        }\n\n        return supported;\n    }\n\n    //only way to test for Filesystem API support since webkit does not expose the DataTransfer interface\n    function isChrome21OrHigher() {\n        return (qq.chrome() || qq.opera()) &&\n            navigator.userAgent.match(/Chrome\\/[2][1-9]|Chrome\\/[3-9][0-9]/) !== undefined;\n    }\n\n    //only way to test for complete Clipboard API support at this time\n    function isChrome14OrHigher() {\n        return (qq.chrome() || qq.opera()) &&\n            navigator.userAgent.match(/Chrome\\/[1][4-9]|Chrome\\/[2-9][0-9]/) !== undefined;\n    }\n\n    //Ensure we can send cross-origin `XMLHttpRequest`s\n    function isCrossOriginXhrSupported() {\n        if (window.XMLHttpRequest) {\n            var xhr = qq.createXhrInstance();\n\n            //Commonly accepted test for XHR CORS support.\n            return xhr.withCredentials !== undefined;\n        }\n\n        return false;\n    }\n\n    //Test for (terrible) cross-origin ajax transport fallback for IE9 and IE8\n    function isXdrSupported() {\n        return window.XDomainRequest !== undefined;\n    }\n\n    // CORS Ajax requests are supported if it is either possible to send credentialed `XMLHttpRequest`s,\n    // or if `XDomainRequest` is an available alternative.\n    function isCrossOriginAjaxSupported() {\n        if (isCrossOriginXhrSupported()) {\n            return true;\n        }\n\n        return isXdrSupported();\n    }\n\n    function isFolderSelectionSupported() {\n        // We know that folder selection is only supported in Chrome via this proprietary attribute for now\n        return document.createElement(\"input\").webkitdirectory !== undefined;\n    }\n\n    function isLocalStorageSupported() {\n        try {\n            return !!window.localStorage &&\n                // unpatched versions of IE10/11 have buggy impls of localStorage where setItem is a string\n                qq.isFunction(window.localStorage.setItem);\n        }\n        catch (error) {\n            // probably caught a security exception, so no localStorage for you\n            return false;\n        }\n    }\n\n    function isDragAndDropSupported() {\n        var span = document.createElement(\"span\");\n\n        return (\"draggable\" in span || (\"ondragstart\" in span && \"ondrop\" in span)) &&\n            !qq.android() && !qq.ios();\n    }\n\n    supportsUploading = testSupportsFileInputElement();\n\n    supportsAjaxFileUploading = supportsUploading && qq.isXhrUploadSupported();\n\n    supportsUploadingBlobs = supportsAjaxFileUploading && !qq.androidStock();\n\n    supportsFileDrop = supportsAjaxFileUploading && isDragAndDropSupported();\n\n    supportsFolderDrop = supportsFileDrop && isChrome21OrHigher();\n\n    supportsChunking = supportsAjaxFileUploading && qq.isFileChunkingSupported();\n\n    supportsResume = supportsAjaxFileUploading && supportsChunking && isLocalStorageSupported();\n\n    supportsUploadViaPaste = supportsAjaxFileUploading && isChrome14OrHigher();\n\n    supportsUploadCors = supportsUploading && (window.postMessage !== undefined || supportsAjaxFileUploading);\n\n    supportsDeleteFileCorsXhr = isCrossOriginXhrSupported();\n\n    supportsDeleteFileXdr = isXdrSupported();\n\n    supportsDeleteFileCors = isCrossOriginAjaxSupported();\n\n    supportsFolderSelection = isFolderSelectionSupported();\n\n    supportsImagePreviews = supportsAjaxFileUploading && window.FileReader !== undefined;\n\n    supportsUploadProgress = (function() {\n        if (supportsAjaxFileUploading) {\n            return !qq.androidStock() && !qq.iosChrome();\n        }\n        return false;\n    }());\n\n    return {\n        ajaxUploading: supportsAjaxFileUploading,\n        blobUploading: supportsUploadingBlobs,\n        canDetermineSize: supportsAjaxFileUploading,\n        chunking: supportsChunking,\n        deleteFileCors: supportsDeleteFileCors,\n        deleteFileCorsXdr: supportsDeleteFileXdr, //NOTE: will also return true in IE10, where XDR is also supported\n        deleteFileCorsXhr: supportsDeleteFileCorsXhr,\n        dialogElement: !!window.HTMLDialogElement,\n        fileDrop: supportsFileDrop,\n        folderDrop: supportsFolderDrop,\n        folderSelection: supportsFolderSelection,\n        imagePreviews: supportsImagePreviews,\n        imageValidation: supportsImagePreviews,\n        itemSizeValidation: supportsAjaxFileUploading,\n        pause: supportsChunking,\n        progressBar: supportsUploadProgress,\n        resume: supportsResume,\n        scaling: supportsImagePreviews && supportsUploadingBlobs,\n        tiffPreviews: qq.safari(), // Not the best solution, but simple and probably accurate enough (for now)\n        unlimitedScaledImageSize: !qq.ios(), // false simply indicates that there is some known limit\n        uploading: supportsUploading,\n        uploadCors: supportsUploadCors,\n        uploadCustomHeaders: supportsAjaxFileUploading,\n        uploadNonMultipart: supportsAjaxFileUploading,\n        uploadViaPaste: supportsUploadViaPaste\n    };\n\n}());\n","/*globals qq*/\n\n// Is the passed object a promise instance?\nqq.isGenericPromise = function(maybePromise) {\n    \"use strict\";\n    return !!(maybePromise && maybePromise.then && qq.isFunction(maybePromise.then));\n};\n\nqq.Promise = function() {\n    \"use strict\";\n\n    var successArgs, failureArgs,\n        successCallbacks = [],\n        failureCallbacks = [],\n        doneCallbacks = [],\n        state = 0;\n\n    qq.extend(this, {\n        then: function(onSuccess, onFailure) {\n            if (state === 0) {\n                if (onSuccess) {\n                    successCallbacks.push(onSuccess);\n                }\n                if (onFailure) {\n                    failureCallbacks.push(onFailure);\n                }\n            }\n            else if (state === -1) {\n                onFailure && onFailure.apply(null, failureArgs);\n            }\n            else if (onSuccess) {\n                onSuccess.apply(null, successArgs);\n            }\n\n            return this;\n        },\n\n        done: function(callback) {\n            if (state === 0) {\n                doneCallbacks.push(callback);\n            }\n            else {\n                callback.apply(null, failureArgs === undefined ? successArgs : failureArgs);\n            }\n\n            return this;\n        },\n\n        success: function() {\n            state = 1;\n            successArgs = arguments;\n\n            if (successCallbacks.length) {\n                qq.each(successCallbacks, function(idx, callback) {\n                    callback.apply(null, successArgs);\n                });\n            }\n\n            if (doneCallbacks.length) {\n                qq.each(doneCallbacks, function(idx, callback) {\n                    callback.apply(null, successArgs);\n                });\n            }\n\n            return this;\n        },\n\n        failure: function() {\n            state = -1;\n            failureArgs = arguments;\n\n            if (failureCallbacks.length) {\n                qq.each(failureCallbacks, function(idx, callback) {\n                    callback.apply(null, failureArgs);\n                });\n            }\n\n            if (doneCallbacks.length) {\n                qq.each(doneCallbacks, function(idx, callback) {\n                    callback.apply(null, failureArgs);\n                });\n            }\n\n            return this;\n        }\n    });\n};\n","/* globals qq */\n/**\n * Placeholder for a Blob that will be generated on-demand.\n *\n * @param referenceBlob Parent of the generated blob\n * @param onCreate Function to invoke when the blob must be created.  Must be promissory.\n * @constructor\n */\nqq.BlobProxy = function(referenceBlob, onCreate) {\n    \"use strict\";\n\n    qq.extend(this, {\n        referenceBlob: referenceBlob,\n\n        create: function() {\n            return onCreate(referenceBlob);\n        }\n    });\n};\n","/*globals qq*/\n\n/**\n * This module represents an upload or \"Select File(s)\" button.  It's job is to embed an opaque `<input type=\"file\">`\n * element as a child of a provided \"container\" element.  This \"container\" element (`options.element`) is used to provide\n * a custom style for the `<input type=\"file\">` element.  The ability to change the style of the container element is also\n * provided here by adding CSS classes to the container on hover/focus.\n *\n * TODO Eliminate the mouseover and mouseout event handlers since the :hover CSS pseudo-class should now be\n * available on all supported browsers.\n *\n * @param o Options to override the default values\n */\nqq.UploadButton = function(o) {\n    \"use strict\";\n\n    var self = this,\n\n        disposeSupport = new qq.DisposeSupport(),\n\n        options = {\n            // Corresponds to the `accept` attribute on the associated `<input type=\"file\">`\n            acceptFiles: null,\n\n            // \"Container\" element\n            element: null,\n\n            focusClass: \"qq-upload-button-focus\",\n\n            // A true value allows folders to be selected, if supported by the UA\n            folders: false,\n\n            // **This option will be removed** in the future as the :hover CSS pseudo-class is available on all supported browsers\n            hoverClass: \"qq-upload-button-hover\",\n\n            ios8BrowserCrashWorkaround: false,\n\n            // If true adds `multiple` attribute to `<input type=\"file\">`\n            multiple: false,\n\n            // `name` attribute of `<input type=\"file\">`\n            name: \"qqfile\",\n\n            // Called when the browser invokes the onchange handler on the `<input type=\"file\">`\n            onChange: function(input) {},\n\n            title: null\n        },\n        input, buttonId;\n\n    // Overrides any of the default option values with any option values passed in during construction.\n    qq.extend(options, o);\n\n    buttonId = qq.getUniqueId();\n\n    // Embed an opaque `<input type=\"file\">` element as a child of `options.element`.\n    function createInput() {\n        var input = document.createElement(\"input\");\n\n        input.setAttribute(qq.UploadButton.BUTTON_ID_ATTR_NAME, buttonId);\n        input.setAttribute(\"title\", options.title);\n\n        self.setMultiple(options.multiple, input);\n\n        if (options.folders && qq.supportedFeatures.folderSelection) {\n            // selecting directories is only possible in Chrome now, via a vendor-specific prefixed attribute\n            input.setAttribute(\"webkitdirectory\", \"\");\n        }\n\n        if (options.acceptFiles) {\n            input.setAttribute(\"accept\", options.acceptFiles);\n        }\n\n        input.setAttribute(\"type\", \"file\");\n        input.setAttribute(\"name\", options.name);\n\n        qq(input).css({\n            position: \"absolute\",\n            // in Opera only 'browse' button\n            // is clickable and it is located at\n            // the right side of the input\n            right: 0,\n            top: 0,\n            fontFamily: \"Arial\",\n            // It's especially important to make this an arbitrarily large value\n            // to ensure the rendered input button in IE takes up the entire\n            // space of the container element.  Otherwise, the left side of the\n            // button will require a double-click to invoke the file chooser.\n            // In other browsers, this might cause other issues, so a large font-size\n            // is only used in IE.  There is a bug in IE8 where the opacity style is  ignored\n            // in some cases when the font-size is large.  So, this workaround is not applied\n            // to IE8.\n            fontSize: qq.ie() && !qq.ie8() ? \"3500px\" : \"118px\",\n            margin: 0,\n            padding: 0,\n            cursor: \"pointer\",\n            opacity: 0\n        });\n\n        // Setting the file input's height to 100% in IE7 causes\n        // most of the visible button to be unclickable.\n        !qq.ie7() && qq(input).css({height: \"100%\"});\n\n        options.element.appendChild(input);\n\n        disposeSupport.attach(input, \"change\", function() {\n            options.onChange(input);\n        });\n\n        // **These event handlers will be removed** in the future as the :hover CSS pseudo-class is available on all supported browsers\n        disposeSupport.attach(input, \"mouseover\", function() {\n            qq(options.element).addClass(options.hoverClass);\n        });\n        disposeSupport.attach(input, \"mouseout\", function() {\n            qq(options.element).removeClass(options.hoverClass);\n        });\n\n        disposeSupport.attach(input, \"focus\", function() {\n            qq(options.element).addClass(options.focusClass);\n        });\n        disposeSupport.attach(input, \"blur\", function() {\n            qq(options.element).removeClass(options.focusClass);\n        });\n\n        return input;\n    }\n\n    // Make button suitable container for input\n    qq(options.element).css({\n        position: \"relative\",\n        overflow: \"hidden\",\n        // Make sure browse button is in the right side in Internet Explorer\n        direction: \"ltr\"\n    });\n\n    // Exposed API\n    qq.extend(this, {\n        getInput: function() {\n            return input;\n        },\n\n        getButtonId: function() {\n            return buttonId;\n        },\n\n        setMultiple: function(isMultiple, optInput) {\n            var input = optInput || this.getInput();\n\n            // Temporary workaround for bug in in iOS8 UIWebView that causes the browser to crash\n            // before the file chooser appears if the file input doesn't contain a multiple attribute.\n            // See #1283.\n            if (options.ios8BrowserCrashWorkaround && qq.ios8() && (qq.iosChrome() || qq.iosSafariWebView())) {\n                input.setAttribute(\"multiple\", \"\");\n            }\n\n            else {\n                if (isMultiple) {\n                    input.setAttribute(\"multiple\", \"\");\n                }\n                else {\n                    input.removeAttribute(\"multiple\");\n                }\n            }\n        },\n\n        setAcceptFiles: function(acceptFiles) {\n            if (acceptFiles !== options.acceptFiles) {\n                input.setAttribute(\"accept\", acceptFiles);\n            }\n        },\n\n        reset: function() {\n            if (input.parentNode) {\n                qq(input).remove();\n            }\n\n            qq(options.element).removeClass(options.focusClass);\n            input = null;\n            input = createInput();\n        }\n    });\n\n    input = createInput();\n};\n\nqq.UploadButton.BUTTON_ID_ATTR_NAME = \"qq-button-id\";\n","/*globals qq */\nqq.UploadData = function(uploaderProxy) {\n    \"use strict\";\n\n    var data = [],\n        byUuid = {},\n        byStatus = {},\n        byProxyGroupId = {},\n        byBatchId = {};\n\n    function getDataByIds(idOrIds) {\n        if (qq.isArray(idOrIds)) {\n            var entries = [];\n\n            qq.each(idOrIds, function(idx, id) {\n                entries.push(data[id]);\n            });\n\n            return entries;\n        }\n\n        return data[idOrIds];\n    }\n\n    function getDataByUuids(uuids) {\n        if (qq.isArray(uuids)) {\n            var entries = [];\n\n            qq.each(uuids, function(idx, uuid) {\n                entries.push(data[byUuid[uuid]]);\n            });\n\n            return entries;\n        }\n\n        return data[byUuid[uuids]];\n    }\n\n    function getDataByStatus(status) {\n        var statusResults = [],\n            statuses = [].concat(status);\n\n        qq.each(statuses, function(index, statusEnum) {\n            var statusResultIndexes = byStatus[statusEnum];\n\n            if (statusResultIndexes !== undefined) {\n                qq.each(statusResultIndexes, function(i, dataIndex) {\n                    statusResults.push(data[dataIndex]);\n                });\n            }\n        });\n\n        return statusResults;\n    }\n\n    qq.extend(this, {\n        /**\n         * Adds a new file to the data cache for tracking purposes.\n         *\n         * @param spec Data that describes this file.  Possible properties are:\n         *\n         * - uuid: Initial UUID for this file.\n         * - name: Initial name of this file.\n         * - size: Size of this file, omit if this cannot be determined\n         * - status: Initial `qq.status` for this file.  Omit for `qq.status.SUBMITTING`.\n         * - batchId: ID of the batch this file belongs to\n         * - proxyGroupId: ID of the proxy group associated with this file\n         *\n         * @returns {number} Internal ID for this file.\n         */\n        addFile: function(spec) {\n            var status = spec.status || qq.status.SUBMITTING,\n                id = data.push({\n                    name: spec.name,\n                    originalName: spec.name,\n                    uuid: spec.uuid,\n                    size: spec.size == null ? -1 : spec.size,\n                    status: status\n                }) - 1;\n\n            if (spec.batchId) {\n                data[id].batchId = spec.batchId;\n\n                if (byBatchId[spec.batchId] === undefined) {\n                    byBatchId[spec.batchId] = [];\n                }\n                byBatchId[spec.batchId].push(id);\n            }\n\n            if (spec.proxyGroupId) {\n                data[id].proxyGroupId = spec.proxyGroupId;\n\n                if (byProxyGroupId[spec.proxyGroupId] === undefined) {\n                    byProxyGroupId[spec.proxyGroupId] = [];\n                }\n                byProxyGroupId[spec.proxyGroupId].push(id);\n            }\n\n            data[id].id = id;\n            byUuid[spec.uuid] = id;\n\n            if (byStatus[status] === undefined) {\n                byStatus[status] = [];\n            }\n            byStatus[status].push(id);\n\n            uploaderProxy.onStatusChange(id, null, status);\n\n            return id;\n        },\n\n        retrieve: function(optionalFilter) {\n            if (qq.isObject(optionalFilter) && data.length)  {\n                if (optionalFilter.id !== undefined) {\n                    return getDataByIds(optionalFilter.id);\n                }\n\n                else if (optionalFilter.uuid !== undefined) {\n                    return getDataByUuids(optionalFilter.uuid);\n                }\n\n                else if (optionalFilter.status) {\n                    return getDataByStatus(optionalFilter.status);\n                }\n            }\n            else {\n                return qq.extend([], data, true);\n            }\n        },\n\n        reset: function() {\n            data = [];\n            byUuid = {};\n            byStatus = {};\n            byBatchId = {};\n        },\n\n        setStatus: function(id, newStatus) {\n            var oldStatus = data[id].status,\n                byStatusOldStatusIndex = qq.indexOf(byStatus[oldStatus], id);\n\n            byStatus[oldStatus].splice(byStatusOldStatusIndex, 1);\n\n            data[id].status = newStatus;\n\n            if (byStatus[newStatus] === undefined) {\n                byStatus[newStatus] = [];\n            }\n            byStatus[newStatus].push(id);\n\n            uploaderProxy.onStatusChange(id, oldStatus, newStatus);\n        },\n\n        uuidChanged: function(id, newUuid) {\n            var oldUuid = data[id].uuid;\n\n            data[id].uuid = newUuid;\n            byUuid[newUuid] = id;\n            delete byUuid[oldUuid];\n        },\n\n        updateName: function(id, newName) {\n            data[id].name = newName;\n        },\n\n        updateSize: function(id, newSize) {\n            data[id].size = newSize;\n        },\n\n        // Only applicable if this file has a parent that we may want to reference later.\n        setParentId: function(targetId, parentId) {\n            data[targetId].parentId = parentId;\n        },\n\n        getIdsInProxyGroup: function(id) {\n            var proxyGroupId = data[id].proxyGroupId;\n\n            if (proxyGroupId) {\n                return byProxyGroupId[proxyGroupId];\n            }\n            return [];\n        },\n\n        getIdsInBatch: function(id) {\n            var batchId = data[id].batchId;\n\n            return byBatchId[batchId];\n        }\n    });\n};\n\nqq.status = {\n    SUBMITTING: \"submitting\",\n    SUBMITTED: \"submitted\",\n    REJECTED: \"rejected\",\n    QUEUED: \"queued\",\n    CANCELED: \"canceled\",\n    PAUSED: \"paused\",\n    UPLOADING: \"uploading\",\n    UPLOAD_RETRYING: \"retrying upload\",\n    UPLOAD_SUCCESSFUL: \"upload successful\",\n    UPLOAD_FAILED: \"upload failed\",\n    DELETE_FAILED: \"delete failed\",\n    DELETING: \"deleting\",\n    DELETED: \"deleted\"\n};\n","/*globals qq*/\n/**\n * Defines the public API for FineUploaderBasic mode.\n */\n(function() {\n    \"use strict\";\n\n    qq.basePublicApi = {\n        // DEPRECATED - TODO REMOVE IN NEXT MAJOR RELEASE (replaced by addFiles)\n        addBlobs: function(blobDataOrArray, params, endpoint) {\n            this.addFiles(blobDataOrArray, params, endpoint);\n        },\n\n        addInitialFiles: function(cannedFileList) {\n            var self = this;\n\n            qq.each(cannedFileList, function(index, cannedFile) {\n                self._addCannedFile(cannedFile);\n            });\n        },\n\n        addFiles: function(data, params, endpoint) {\n            this._maybeHandleIos8SafariWorkaround();\n\n            var batchId = this._storedIds.length === 0 ? qq.getUniqueId() : this._currentBatchId,\n\n                processBlob = qq.bind(function(blob) {\n                    this._handleNewFile({\n                        blob: blob,\n                        name: this._options.blobs.defaultName\n                    }, batchId, verifiedFiles);\n                }, this),\n\n                processBlobData = qq.bind(function(blobData) {\n                    this._handleNewFile(blobData, batchId, verifiedFiles);\n                }, this),\n\n                processCanvas = qq.bind(function(canvas) {\n                    var blob = qq.canvasToBlob(canvas);\n\n                    this._handleNewFile({\n                        blob: blob,\n                        name: this._options.blobs.defaultName + \".png\"\n                    }, batchId, verifiedFiles);\n                }, this),\n\n                processCanvasData = qq.bind(function(canvasData) {\n                    var normalizedQuality = canvasData.quality && canvasData.quality / 100,\n                        blob = qq.canvasToBlob(canvasData.canvas, canvasData.type, normalizedQuality);\n\n                    this._handleNewFile({\n                        blob: blob,\n                        name: canvasData.name\n                    }, batchId, verifiedFiles);\n                }, this),\n\n                processFileOrInput = qq.bind(function(fileOrInput) {\n                    if (qq.isInput(fileOrInput) && qq.supportedFeatures.ajaxUploading) {\n                        var files = Array.prototype.slice.call(fileOrInput.files),\n                            self = this;\n\n                        qq.each(files, function(idx, file) {\n                            self._handleNewFile(file, batchId, verifiedFiles);\n                        });\n                    }\n                    else {\n                        this._handleNewFile(fileOrInput, batchId, verifiedFiles);\n                    }\n                }, this),\n\n                normalizeData = function() {\n                    if (qq.isFileList(data)) {\n                        data = Array.prototype.slice.call(data);\n                    }\n                    data = [].concat(data);\n                },\n\n                self = this,\n                verifiedFiles = [];\n\n            this._currentBatchId = batchId;\n\n            if (data) {\n                normalizeData();\n\n                qq.each(data, function(idx, fileContainer) {\n                    if (qq.isFileOrInput(fileContainer)) {\n                        processFileOrInput(fileContainer);\n                    }\n                    else if (qq.isBlob(fileContainer)) {\n                        processBlob(fileContainer);\n                    }\n                    else if (qq.isObject(fileContainer)) {\n                        if (fileContainer.blob && fileContainer.name) {\n                            processBlobData(fileContainer);\n                        }\n                        else if (fileContainer.canvas && fileContainer.name) {\n                            processCanvasData(fileContainer);\n                        }\n                    }\n                    else if (fileContainer.tagName && fileContainer.tagName.toLowerCase() === \"canvas\") {\n                        processCanvas(fileContainer);\n                    }\n                    else {\n                        self.log(fileContainer + \" is not a valid file container!  Ignoring!\", \"warn\");\n                    }\n                });\n\n                this.log(\"Received \" + verifiedFiles.length + \" files.\");\n                this._prepareItemsForUpload(verifiedFiles, params, endpoint);\n            }\n        },\n\n        cancel: function(id) {\n            this._handler.cancel(id);\n        },\n\n        cancelAll: function() {\n            var storedIdsCopy = [],\n                self = this;\n\n            qq.extend(storedIdsCopy, this._storedIds);\n            qq.each(storedIdsCopy, function(idx, storedFileId) {\n                self.cancel(storedFileId);\n            });\n\n            this._handler.cancelAll();\n        },\n\n        clearStoredFiles: function() {\n            this._storedIds = [];\n        },\n\n        continueUpload: function(id) {\n            var uploadData = this._uploadData.retrieve({id: id});\n\n            if (!qq.supportedFeatures.pause || !this._options.chunking.enabled) {\n                return false;\n            }\n\n            if (uploadData.status === qq.status.PAUSED) {\n                this.log(qq.format(\"Paused file ID {} ({}) will be continued.  Not paused.\", id, this.getName(id)));\n                this._uploadFile(id);\n                return true;\n            }\n            else {\n                this.log(qq.format(\"Ignoring continue for file ID {} ({}).  Not paused.\", id, this.getName(id)), \"error\");\n            }\n\n            return false;\n        },\n\n        deleteFile: function(id) {\n            return this._onSubmitDelete(id);\n        },\n\n        // TODO document?\n        doesExist: function(fileOrBlobId) {\n            return this._handler.isValid(fileOrBlobId);\n        },\n\n        // Generate a variable size thumbnail on an img or canvas,\n        // returning a promise that is fulfilled when the attempt completes.\n        // Thumbnail can either be based off of a URL for an image returned\n        // by the server in the upload response, or the associated `Blob`.\n        drawThumbnail: function(fileId, imgOrCanvas, maxSize, fromServer, customResizeFunction) {\n            var promiseToReturn = new qq.Promise(),\n                fileOrUrl, options;\n\n            if (this._imageGenerator) {\n                fileOrUrl = this._thumbnailUrls[fileId];\n                options = {\n                    customResizeFunction: customResizeFunction,\n                    maxSize: maxSize > 0 ? maxSize : null,\n                    scale: maxSize > 0\n                };\n\n                // If client-side preview generation is possible\n                // and we are not specifically looking for the image URl returned by the server...\n                if (!fromServer && qq.supportedFeatures.imagePreviews) {\n                    fileOrUrl = this.getFile(fileId);\n                }\n\n                /* jshint eqeqeq:false,eqnull:true */\n                if (fileOrUrl == null) {\n                    promiseToReturn.failure({container: imgOrCanvas, error: \"File or URL not found.\"});\n                }\n                else {\n                    this._imageGenerator.generate(fileOrUrl, imgOrCanvas, options).then(\n                        function success(modifiedContainer) {\n                            promiseToReturn.success(modifiedContainer);\n                        },\n\n                        function failure(container, reason) {\n                            promiseToReturn.failure({container: container, error: reason || \"Problem generating thumbnail\"});\n                        }\n                    );\n                }\n            }\n            else {\n                promiseToReturn.failure({container: imgOrCanvas, error: \"Missing image generator module\"});\n            }\n\n            return promiseToReturn;\n        },\n\n        getButton: function(fileId) {\n            return this._getButton(this._buttonIdsForFileIds[fileId]);\n        },\n\n        getEndpoint: function(fileId) {\n            return this._endpointStore.get(fileId);\n        },\n\n        getFile: function(fileOrBlobId) {\n            return this._handler.getFile(fileOrBlobId) || null;\n        },\n\n        getInProgress: function() {\n            return this._uploadData.retrieve({\n                status: [\n                    qq.status.UPLOADING,\n                    qq.status.UPLOAD_RETRYING,\n                    qq.status.QUEUED\n                ]\n            }).length;\n        },\n\n        getName: function(id) {\n            return this._uploadData.retrieve({id: id}).name;\n        },\n\n        // Parent ID for a specific file, or null if this is the parent, or if it has no parent.\n        getParentId: function(id) {\n            var uploadDataEntry = this.getUploads({id: id}),\n                parentId = null;\n\n            if (uploadDataEntry) {\n                if (uploadDataEntry.parentId !== undefined) {\n                    parentId = uploadDataEntry.parentId;\n                }\n            }\n\n            return parentId;\n        },\n\n        getResumableFilesData: function() {\n            return this._handler.getResumableFilesData();\n        },\n\n        getSize: function(id) {\n            return this._uploadData.retrieve({id: id}).size;\n        },\n\n        getNetUploads: function() {\n            return this._netUploaded;\n        },\n\n        getRemainingAllowedItems: function() {\n            var allowedItems = this._currentItemLimit;\n\n            if (allowedItems > 0) {\n                return allowedItems - this._netUploadedOrQueued;\n            }\n\n            return null;\n        },\n\n        getUploads: function(optionalFilter) {\n            return this._uploadData.retrieve(optionalFilter);\n        },\n\n        getUuid: function(id) {\n            return this._uploadData.retrieve({id: id}).uuid;\n        },\n\n        log: function(str, level) {\n            if (this._options.debug && (!level || level === \"info\")) {\n                qq.log(\"[Fine Uploader \" + qq.version + \"] \" + str);\n            }\n            else if (level && level !== \"info\") {\n                qq.log(\"[Fine Uploader \" + qq.version + \"] \" + str, level);\n\n            }\n        },\n\n        pauseUpload: function(id) {\n            var uploadData = this._uploadData.retrieve({id: id});\n\n            if (!qq.supportedFeatures.pause || !this._options.chunking.enabled) {\n                return false;\n            }\n\n            // Pause only really makes sense if the file is uploading or retrying\n            if (qq.indexOf([qq.status.UPLOADING, qq.status.UPLOAD_RETRYING], uploadData.status) >= 0) {\n                if (this._handler.pause(id)) {\n                    this._uploadData.setStatus(id, qq.status.PAUSED);\n                    return true;\n                }\n                else {\n                    this.log(qq.format(\"Unable to pause file ID {} ({}).\", id, this.getName(id)), \"error\");\n                }\n            }\n            else {\n                this.log(qq.format(\"Ignoring pause for file ID {} ({}).  Not in progress.\", id, this.getName(id)), \"error\");\n            }\n\n            return false;\n        },\n\n        reset: function() {\n            this.log(\"Resetting uploader...\");\n\n            this._handler.reset();\n            this._storedIds = [];\n            this._autoRetries = [];\n            this._retryTimeouts = [];\n            this._preventRetries = [];\n            this._thumbnailUrls = [];\n\n            qq.each(this._buttons, function(idx, button) {\n                button.reset();\n            });\n\n            this._paramsStore.reset();\n            this._endpointStore.reset();\n            this._netUploadedOrQueued = 0;\n            this._netUploaded = 0;\n            this._uploadData.reset();\n            this._buttonIdsForFileIds = [];\n\n            this._pasteHandler && this._pasteHandler.reset();\n            this._options.session.refreshOnReset && this._refreshSessionData();\n\n            this._succeededSinceLastAllComplete = [];\n            this._failedSinceLastAllComplete = [];\n\n            this._totalProgress && this._totalProgress.reset();\n        },\n\n        retry: function(id) {\n            return this._manualRetry(id);\n        },\n\n        scaleImage: function(id, specs) {\n            var self = this;\n\n            return qq.Scaler.prototype.scaleImage(id, specs, {\n                log: qq.bind(self.log, self),\n                getFile: qq.bind(self.getFile, self),\n                uploadData: self._uploadData\n            });\n        },\n\n        setCustomHeaders: function(headers, id) {\n            this._customHeadersStore.set(headers, id);\n        },\n\n        setDeleteFileCustomHeaders: function(headers, id) {\n            this._deleteFileCustomHeadersStore.set(headers, id);\n        },\n\n        setDeleteFileEndpoint: function(endpoint, id) {\n            this._deleteFileEndpointStore.set(endpoint, id);\n        },\n\n        setDeleteFileParams: function(params, id) {\n            this._deleteFileParamsStore.set(params, id);\n        },\n\n        // Re-sets the default endpoint, an endpoint for a specific file, or an endpoint for a specific button\n        setEndpoint: function(endpoint, id) {\n            this._endpointStore.set(endpoint, id);\n        },\n\n        setForm: function(elementOrId) {\n            this._updateFormSupportAndParams(elementOrId);\n        },\n\n        setItemLimit: function(newItemLimit) {\n            this._currentItemLimit = newItemLimit;\n        },\n\n        setName: function(id, newName) {\n            this._uploadData.updateName(id, newName);\n        },\n\n        setParams: function(params, id) {\n            this._paramsStore.set(params, id);\n        },\n\n        setUuid: function(id, newUuid) {\n            return this._uploadData.uuidChanged(id, newUuid);\n        },\n\n        uploadStoredFiles: function() {\n            if (this._storedIds.length === 0) {\n                this._itemError(\"noFilesError\");\n            }\n            else {\n                this._uploadStoredFiles();\n            }\n        }\n    };\n\n    /**\n     * Defines the private (internal) API for FineUploaderBasic mode.\n     */\n    qq.basePrivateApi = {\n        // Updates internal state with a file record (not backed by a live file).  Returns the assigned ID.\n        _addCannedFile: function(sessionData) {\n            var id = this._uploadData.addFile({\n                uuid: sessionData.uuid,\n                name: sessionData.name,\n                size: sessionData.size,\n                status: qq.status.UPLOAD_SUCCESSFUL\n            });\n\n            sessionData.deleteFileEndpoint && this.setDeleteFileEndpoint(sessionData.deleteFileEndpoint, id);\n            sessionData.deleteFileParams && this.setDeleteFileParams(sessionData.deleteFileParams, id);\n\n            if (sessionData.thumbnailUrl) {\n                this._thumbnailUrls[id] = sessionData.thumbnailUrl;\n            }\n\n            this._netUploaded++;\n            this._netUploadedOrQueued++;\n\n            return id;\n        },\n\n        _annotateWithButtonId: function(file, associatedInput) {\n            if (qq.isFile(file)) {\n                file.qqButtonId = this._getButtonId(associatedInput);\n            }\n        },\n\n        _batchError: function(message) {\n            this._options.callbacks.onError(null, null, message, undefined);\n        },\n\n        _createDeleteHandler: function() {\n            var self = this;\n\n            return new qq.DeleteFileAjaxRequester({\n                method: this._options.deleteFile.method.toUpperCase(),\n                maxConnections: this._options.maxConnections,\n                uuidParamName: this._options.request.uuidName,\n                customHeaders: this._deleteFileCustomHeadersStore,\n                paramsStore: this._deleteFileParamsStore,\n                endpointStore: this._deleteFileEndpointStore,\n                cors: this._options.cors,\n                log: qq.bind(self.log, self),\n                onDelete: function(id) {\n                    self._onDelete(id);\n                    self._options.callbacks.onDelete(id);\n                },\n                onDeleteComplete: function(id, xhrOrXdr, isError) {\n                    self._onDeleteComplete(id, xhrOrXdr, isError);\n                    self._options.callbacks.onDeleteComplete(id, xhrOrXdr, isError);\n                }\n\n            });\n        },\n\n        _createPasteHandler: function() {\n            var self = this;\n\n            return new qq.PasteSupport({\n                targetElement: this._options.paste.targetElement,\n                callbacks: {\n                    log: qq.bind(self.log, self),\n                    pasteReceived: function(blob) {\n                        self._handleCheckedCallback({\n                            name: \"onPasteReceived\",\n                            callback: qq.bind(self._options.callbacks.onPasteReceived, self, blob),\n                            onSuccess: qq.bind(self._handlePasteSuccess, self, blob),\n                            identifier: \"pasted image\"\n                        });\n                    }\n                }\n            });\n        },\n\n        _createStore: function(initialValue, _readOnlyValues_) {\n            var store = {},\n                catchall = initialValue,\n                perIdReadOnlyValues = {},\n                readOnlyValues = _readOnlyValues_,\n                copy = function(orig) {\n                    if (qq.isObject(orig)) {\n                        return qq.extend({}, orig);\n                    }\n                    return orig;\n                },\n                getReadOnlyValues = function() {\n                    if (qq.isFunction(readOnlyValues)) {\n                        return readOnlyValues();\n                    }\n                    return readOnlyValues;\n                },\n                includeReadOnlyValues = function(id, existing) {\n                    if (readOnlyValues && qq.isObject(existing)) {\n                        qq.extend(existing, getReadOnlyValues());\n                    }\n\n                    if (perIdReadOnlyValues[id]) {\n                        qq.extend(existing, perIdReadOnlyValues[id]);\n                    }\n                };\n\n            return {\n                set: function(val, id) {\n                    /*jshint eqeqeq: true, eqnull: true*/\n                    if (id == null) {\n                        store = {};\n                        catchall = copy(val);\n                    }\n                    else {\n                        store[id] = copy(val);\n                    }\n                },\n\n                get: function(id) {\n                    var values;\n\n                    /*jshint eqeqeq: true, eqnull: true*/\n                    if (id != null && store[id]) {\n                        values = store[id];\n                    }\n                    else {\n                        values = copy(catchall);\n                    }\n\n                    includeReadOnlyValues(id, values);\n\n                    return copy(values);\n                },\n\n                addReadOnly: function(id, values) {\n                    // Only applicable to Object stores\n                    if (qq.isObject(store)) {\n                        // If null ID, apply readonly values to all files\n                        if (id === null) {\n                            if (qq.isFunction(values)) {\n                                readOnlyValues = values;\n                            }\n                            else {\n                                readOnlyValues = readOnlyValues || {};\n                                qq.extend(readOnlyValues, values);\n                            }\n                        }\n                        else {\n                            perIdReadOnlyValues[id] = perIdReadOnlyValues[id] || {};\n                            qq.extend(perIdReadOnlyValues[id], values);\n                        }\n                    }\n                },\n\n                remove: function(fileId) {\n                    return delete store[fileId];\n                },\n\n                reset: function() {\n                    store = {};\n                    perIdReadOnlyValues = {};\n                    catchall = initialValue;\n                }\n            };\n        },\n\n        _createUploadDataTracker: function() {\n            var self = this;\n\n            return new qq.UploadData({\n                getName: function(id) {\n                    return self.getName(id);\n                },\n                getUuid: function(id) {\n                    return self.getUuid(id);\n                },\n                getSize: function(id) {\n                    return self.getSize(id);\n                },\n                onStatusChange: function(id, oldStatus, newStatus) {\n                    self._onUploadStatusChange(id, oldStatus, newStatus);\n                    self._options.callbacks.onStatusChange(id, oldStatus, newStatus);\n                    self._maybeAllComplete(id, newStatus);\n\n                    if (self._totalProgress) {\n                        setTimeout(function() {\n                            self._totalProgress.onStatusChange(id, oldStatus, newStatus);\n                        }, 0);\n                    }\n                }\n            });\n        },\n\n        /**\n         * Generate a tracked upload button.\n         *\n         * @param spec Object containing a required `element` property\n         * along with optional `multiple`, `accept`, and `folders`.\n         * @returns {qq.UploadButton}\n         * @private\n         */\n        _createUploadButton: function(spec) {\n            var self = this,\n                acceptFiles = spec.accept || this._options.validation.acceptFiles,\n                allowedExtensions = spec.allowedExtensions || this._options.validation.allowedExtensions,\n                button;\n\n            function allowMultiple() {\n                if (qq.supportedFeatures.ajaxUploading) {\n                    // Workaround for bug in iOS7+ (see #1039)\n                    if (self._options.workarounds.iosEmptyVideos &&\n                        qq.ios() &&\n                        !qq.ios6() &&\n                        self._isAllowedExtension(allowedExtensions, \".mov\")) {\n\n                        return false;\n                    }\n\n                    if (spec.multiple === undefined) {\n                        return self._options.multiple;\n                    }\n\n                    return spec.multiple;\n                }\n\n                return false;\n            }\n\n            button = new qq.UploadButton({\n                acceptFiles: acceptFiles,\n                element: spec.element,\n                focusClass: this._options.classes.buttonFocus,\n                folders: spec.folders,\n                hoverClass: this._options.classes.buttonHover,\n                ios8BrowserCrashWorkaround: this._options.workarounds.ios8BrowserCrash,\n                multiple: allowMultiple(),\n                name: this._options.request.inputName,\n                onChange: function(input) {\n                    self._onInputChange(input);\n                },\n                title: spec.title == null ? this._options.text.fileInputTitle : spec.title\n            });\n\n            this._disposeSupport.addDisposer(function() {\n                button.dispose();\n            });\n\n            self._buttons.push(button);\n\n            return button;\n        },\n\n        _createUploadHandler: function(additionalOptions, namespace) {\n            var self = this,\n                lastOnProgress = {},\n                options = {\n                    debug: this._options.debug,\n                    maxConnections: this._options.maxConnections,\n                    cors: this._options.cors,\n                    paramsStore: this._paramsStore,\n                    endpointStore: this._endpointStore,\n                    chunking: this._options.chunking,\n                    resume: this._options.resume,\n                    blobs: this._options.blobs,\n                    log: qq.bind(self.log, self),\n                    preventRetryParam: this._options.retry.preventRetryResponseProperty,\n                    onProgress: function(id, name, loaded, total) {\n                        if (loaded < 0 || total < 0) {\n                            return;\n                        }\n\n                        if (lastOnProgress[id]) {\n                            if (lastOnProgress[id].loaded !== loaded || lastOnProgress[id].total !== total) {\n                                self._onProgress(id, name, loaded, total);\n                                self._options.callbacks.onProgress(id, name, loaded, total);\n                            }\n                        }\n                        else {\n                            self._onProgress(id, name, loaded, total);\n                            self._options.callbacks.onProgress(id, name, loaded, total);\n                        }\n\n                        lastOnProgress[id] = {loaded: loaded, total: total};\n\n                    },\n                    onComplete: function(id, name, result, xhr) {\n                        delete lastOnProgress[id];\n\n                        var status = self.getUploads({id: id}).status,\n                            retVal;\n\n                        // This is to deal with some observed cases where the XHR readyStateChange handler is\n                        // invoked by the browser multiple times for the same XHR instance with the same state\n                        // readyState value.  Higher level: don't invoke complete-related code if we've already\n                        // done this.\n                        if (status === qq.status.UPLOAD_SUCCESSFUL || status === qq.status.UPLOAD_FAILED) {\n                            return;\n                        }\n\n                        retVal = self._onComplete(id, name, result, xhr);\n\n                        // If the internal `_onComplete` handler returns a promise, don't invoke the `onComplete` callback\n                        // until the promise has been fulfilled.\n                        if (retVal instanceof  qq.Promise) {\n                            retVal.done(function() {\n                                self._options.callbacks.onComplete(id, name, result, xhr);\n                            });\n                        }\n                        else {\n                            self._options.callbacks.onComplete(id, name, result, xhr);\n                        }\n                    },\n                    onCancel: function(id, name, cancelFinalizationEffort) {\n                        var promise = new qq.Promise();\n\n                        self._handleCheckedCallback({\n                            name: \"onCancel\",\n                            callback: qq.bind(self._options.callbacks.onCancel, self, id, name),\n                            onFailure: promise.failure,\n                            onSuccess: function() {\n                                cancelFinalizationEffort.then(function() {\n                                    self._onCancel(id, name);\n                                });\n\n                                promise.success();\n                            },\n                            identifier: id\n                        });\n\n                        return promise;\n                    },\n                    onUploadPrep: qq.bind(this._onUploadPrep, this),\n                    onUpload: function(id, name) {\n                        self._onUpload(id, name);\n                        self._options.callbacks.onUpload(id, name);\n                    },\n                    onUploadChunk: function(id, name, chunkData) {\n                        self._onUploadChunk(id, chunkData);\n                        self._options.callbacks.onUploadChunk(id, name, chunkData);\n                    },\n                    onUploadChunkSuccess: function(id, chunkData, result, xhr) {\n                        self._options.callbacks.onUploadChunkSuccess.apply(self, arguments);\n                    },\n                    onResume: function(id, name, chunkData) {\n                        return self._options.callbacks.onResume(id, name, chunkData);\n                    },\n                    onAutoRetry: function(id, name, responseJSON, xhr) {\n                        return self._onAutoRetry.apply(self, arguments);\n                    },\n                    onUuidChanged: function(id, newUuid) {\n                        self.log(\"Server requested UUID change from '\" + self.getUuid(id) + \"' to '\" + newUuid + \"'\");\n                        self.setUuid(id, newUuid);\n                    },\n                    getName: qq.bind(self.getName, self),\n                    getUuid: qq.bind(self.getUuid, self),\n                    getSize: qq.bind(self.getSize, self),\n                    setSize: qq.bind(self._setSize, self),\n                    getDataByUuid: function(uuid) {\n                        return self.getUploads({uuid: uuid});\n                    },\n                    isQueued: function(id) {\n                        var status = self.getUploads({id: id}).status;\n                        return status === qq.status.QUEUED ||\n                            status === qq.status.SUBMITTED ||\n                            status === qq.status.UPLOAD_RETRYING ||\n                            status === qq.status.PAUSED;\n                    },\n                    getIdsInProxyGroup: self._uploadData.getIdsInProxyGroup,\n                    getIdsInBatch: self._uploadData.getIdsInBatch\n                };\n\n            qq.each(this._options.request, function(prop, val) {\n                options[prop] = val;\n            });\n\n            options.customHeaders = this._customHeadersStore;\n\n            if (additionalOptions) {\n                qq.each(additionalOptions, function(key, val) {\n                    options[key] = val;\n                });\n            }\n\n            return new qq.UploadHandlerController(options, namespace);\n        },\n\n        _fileOrBlobRejected: function(id) {\n            this._netUploadedOrQueued--;\n            this._uploadData.setStatus(id, qq.status.REJECTED);\n        },\n\n        _formatSize: function(bytes) {\n            if (bytes === 0) {\n                return bytes + this._options.text.sizeSymbols[0];\n            }\n            var i = -1;\n            do {\n                bytes = bytes / 1000;\n                i++;\n            } while (bytes > 999);\n\n            return Math.max(bytes, 0.1).toFixed(1) + this._options.text.sizeSymbols[i];\n        },\n\n        // Creates an internal object that tracks various properties of each extra button,\n        // and then actually creates the extra button.\n        _generateExtraButtonSpecs: function() {\n            var self = this;\n\n            this._extraButtonSpecs = {};\n\n            qq.each(this._options.extraButtons, function(idx, extraButtonOptionEntry) {\n                var multiple = extraButtonOptionEntry.multiple,\n                    validation = qq.extend({}, self._options.validation, true),\n                    extraButtonSpec = qq.extend({}, extraButtonOptionEntry);\n\n                if (multiple === undefined) {\n                    multiple = self._options.multiple;\n                }\n\n                if (extraButtonSpec.validation) {\n                    qq.extend(validation, extraButtonOptionEntry.validation, true);\n                }\n\n                qq.extend(extraButtonSpec, {\n                    multiple: multiple,\n                    validation: validation\n                }, true);\n\n                self._initExtraButton(extraButtonSpec);\n            });\n        },\n\n        _getButton: function(buttonId) {\n            var extraButtonsSpec = this._extraButtonSpecs[buttonId];\n\n            if (extraButtonsSpec) {\n                return extraButtonsSpec.element;\n            }\n            else if (buttonId === this._defaultButtonId) {\n                return this._options.button;\n            }\n        },\n\n        /**\n         * Gets the internally used tracking ID for a button.\n         *\n         * @param buttonOrFileInputOrFile `File`, `<input type=\"file\">`, or a button container element\n         * @returns {*} The button's ID, or undefined if no ID is recoverable\n         * @private\n         */\n        _getButtonId: function(buttonOrFileInputOrFile) {\n            var inputs, fileInput,\n                fileBlobOrInput = buttonOrFileInputOrFile;\n\n            // We want the reference file/blob here if this is a proxy (a file that will be generated on-demand later)\n            if (fileBlobOrInput instanceof qq.BlobProxy) {\n                fileBlobOrInput = fileBlobOrInput.referenceBlob;\n            }\n\n            // If the item is a `Blob` it will never be associated with a button or drop zone.\n            if (fileBlobOrInput && !qq.isBlob(fileBlobOrInput)) {\n                if (qq.isFile(fileBlobOrInput)) {\n                    return fileBlobOrInput.qqButtonId;\n                }\n                else if (fileBlobOrInput.tagName.toLowerCase() === \"input\" &&\n                    fileBlobOrInput.type.toLowerCase() === \"file\") {\n\n                    return fileBlobOrInput.getAttribute(qq.UploadButton.BUTTON_ID_ATTR_NAME);\n                }\n\n                inputs = fileBlobOrInput.getElementsByTagName(\"input\");\n\n                qq.each(inputs, function(idx, input) {\n                    if (input.getAttribute(\"type\") === \"file\") {\n                        fileInput = input;\n                        return false;\n                    }\n                });\n\n                if (fileInput) {\n                    return fileInput.getAttribute(qq.UploadButton.BUTTON_ID_ATTR_NAME);\n                }\n            }\n        },\n\n        _getNotFinished: function() {\n            return this._uploadData.retrieve({\n                status: [\n                    qq.status.UPLOADING,\n                    qq.status.UPLOAD_RETRYING,\n                    qq.status.QUEUED,\n                    qq.status.SUBMITTING,\n                    qq.status.SUBMITTED,\n                    qq.status.PAUSED\n                ]\n            }).length;\n        },\n\n        // Get the validation options for this button.  Could be the default validation option\n        // or a specific one assigned to this particular button.\n        _getValidationBase: function(buttonId) {\n            var extraButtonSpec = this._extraButtonSpecs[buttonId];\n\n            return extraButtonSpec ? extraButtonSpec.validation : this._options.validation;\n        },\n\n        _getValidationDescriptor: function(fileWrapper) {\n            if (fileWrapper.file instanceof qq.BlobProxy) {\n                return {\n                    name: qq.getFilename(fileWrapper.file.referenceBlob),\n                    size: fileWrapper.file.referenceBlob.size\n                };\n            }\n\n            return {\n                name: this.getUploads({id: fileWrapper.id}).name,\n                size: this.getUploads({id: fileWrapper.id}).size\n            };\n        },\n\n        _getValidationDescriptors: function(fileWrappers) {\n            var self = this,\n                fileDescriptors = [];\n\n            qq.each(fileWrappers, function(idx, fileWrapper) {\n                fileDescriptors.push(self._getValidationDescriptor(fileWrapper));\n            });\n\n            return fileDescriptors;\n        },\n\n        // Allows camera access on either the default or an extra button for iOS devices.\n        _handleCameraAccess: function() {\n            if (this._options.camera.ios && qq.ios()) {\n                var acceptIosCamera = \"image/*;capture=camera\",\n                    button = this._options.camera.button,\n                    buttonId = button ? this._getButtonId(button) : this._defaultButtonId,\n                    optionRoot = this._options;\n\n                // If we are not targeting the default button, it is an \"extra\" button\n                if (buttonId && buttonId !== this._defaultButtonId) {\n                    optionRoot = this._extraButtonSpecs[buttonId];\n                }\n\n                // Camera access won't work in iOS if the `multiple` attribute is present on the file input\n                optionRoot.multiple = false;\n\n                // update the options\n                if (optionRoot.validation.acceptFiles === null) {\n                    optionRoot.validation.acceptFiles = acceptIosCamera;\n                }\n                else {\n                    optionRoot.validation.acceptFiles += \",\" + acceptIosCamera;\n                }\n\n                // update the already-created button\n                qq.each(this._buttons, function(idx, button) {\n                    if (button.getButtonId() === buttonId) {\n                        button.setMultiple(optionRoot.multiple);\n                        button.setAcceptFiles(optionRoot.acceptFiles);\n\n                        return false;\n                    }\n                });\n            }\n        },\n\n        _handleCheckedCallback: function(details) {\n            var self = this,\n                callbackRetVal = details.callback();\n\n            if (qq.isGenericPromise(callbackRetVal)) {\n                this.log(details.name + \" - waiting for \" + details.name + \" promise to be fulfilled for \" + details.identifier);\n                return callbackRetVal.then(\n                    function(successParam) {\n                        self.log(details.name + \" promise success for \" + details.identifier);\n                        details.onSuccess(successParam);\n                    },\n                    function() {\n                        if (details.onFailure) {\n                            self.log(details.name + \" promise failure for \" + details.identifier);\n                            details.onFailure();\n                        }\n                        else {\n                            self.log(details.name + \" promise failure for \" + details.identifier);\n                        }\n                    });\n            }\n\n            if (callbackRetVal !== false) {\n                details.onSuccess(callbackRetVal);\n            }\n            else {\n                if (details.onFailure) {\n                    this.log(details.name + \" - return value was 'false' for \" + details.identifier + \".  Invoking failure callback.\");\n                    details.onFailure();\n                }\n                else {\n                    this.log(details.name + \" - return value was 'false' for \" + details.identifier + \".  Will not proceed.\");\n                }\n            }\n\n            return callbackRetVal;\n        },\n\n        // Updates internal state when a new file has been received, and adds it along with its ID to a passed array.\n        _handleNewFile: function(file, batchId, newFileWrapperList) {\n            var self = this,\n                uuid = qq.getUniqueId(),\n                size = -1,\n                name = qq.getFilename(file),\n                actualFile = file.blob || file,\n                handler = this._customNewFileHandler ?\n                    this._customNewFileHandler :\n                    qq.bind(self._handleNewFileGeneric, self);\n\n            if (!qq.isInput(actualFile) && actualFile.size >= 0) {\n                size = actualFile.size;\n            }\n\n            handler(actualFile, name, uuid, size, newFileWrapperList, batchId, this._options.request.uuidName, {\n                uploadData: self._uploadData,\n                paramsStore: self._paramsStore,\n                addFileToHandler: function(id, file) {\n                    self._handler.add(id, file);\n                    self._netUploadedOrQueued++;\n                    self._trackButton(id);\n                }\n            });\n        },\n\n        _handleNewFileGeneric: function(file, name, uuid, size, fileList, batchId) {\n            var id = this._uploadData.addFile({uuid: uuid, name: name, size: size, batchId: batchId});\n\n            this._handler.add(id, file);\n            this._trackButton(id);\n\n            this._netUploadedOrQueued++;\n\n            fileList.push({id: id, file: file});\n        },\n\n        _handlePasteSuccess: function(blob, extSuppliedName) {\n            var extension = blob.type.split(\"/\")[1],\n                name = extSuppliedName;\n\n            /*jshint eqeqeq: true, eqnull: true*/\n            if (name == null) {\n                name = this._options.paste.defaultName;\n            }\n\n            name += \".\" + extension;\n\n            this.addFiles({\n                name: name,\n                blob: blob\n            });\n        },\n\n        // Creates an extra button element\n        _initExtraButton: function(spec) {\n            var button = this._createUploadButton({\n                accept: spec.validation.acceptFiles,\n                allowedExtensions: spec.validation.allowedExtensions,\n                element: spec.element,\n                folders: spec.folders,\n                multiple: spec.multiple,\n                title: spec.fileInputTitle\n            });\n\n            this._extraButtonSpecs[button.getButtonId()] = spec;\n        },\n\n        _initFormSupportAndParams: function() {\n            this._formSupport = qq.FormSupport && new qq.FormSupport(\n                this._options.form, qq.bind(this.uploadStoredFiles, this), qq.bind(this.log, this)\n            );\n\n            if (this._formSupport && this._formSupport.attachedToForm) {\n                this._paramsStore = this._createStore(\n                    this._options.request.params,  this._formSupport.getFormInputsAsObject\n                );\n\n                this._options.autoUpload = this._formSupport.newAutoUpload;\n                if (this._formSupport.newEndpoint) {\n                    this._options.request.endpoint = this._formSupport.newEndpoint;\n                }\n            }\n            else {\n                this._paramsStore = this._createStore(this._options.request.params);\n            }\n        },\n\n        _isDeletePossible: function() {\n            if (!qq.DeleteFileAjaxRequester || !this._options.deleteFile.enabled) {\n                return false;\n            }\n\n            if (this._options.cors.expected) {\n                if (qq.supportedFeatures.deleteFileCorsXhr) {\n                    return true;\n                }\n\n                if (qq.supportedFeatures.deleteFileCorsXdr && this._options.cors.allowXdr) {\n                    return true;\n                }\n\n                return false;\n            }\n\n            return true;\n        },\n\n        _isAllowedExtension: function(allowed, fileName) {\n            var valid = false;\n\n            if (!allowed.length) {\n                return true;\n            }\n\n            qq.each(allowed, function(idx, allowedExt) {\n                /**\n                 * If an argument is not a string, ignore it.  Added when a possible issue with MooTools hijacking the\n                 * `allowedExtensions` array was discovered.  See case #735 in the issue tracker for more details.\n                 */\n                if (qq.isString(allowedExt)) {\n                    /*jshint eqeqeq: true, eqnull: true*/\n                    var extRegex = new RegExp(\"\\\\.\" + allowedExt + \"$\", \"i\");\n\n                    if (fileName.match(extRegex) != null) {\n                        valid = true;\n                        return false;\n                    }\n                }\n            });\n\n            return valid;\n        },\n\n        /**\n         * Constructs and returns a message that describes an item/file error.  Also calls `onError` callback.\n         *\n         * @param code REQUIRED - a code that corresponds to a stock message describing this type of error\n         * @param maybeNameOrNames names of the items that have failed, if applicable\n         * @param item `File`, `Blob`, or `<input type=\"file\">`\n         * @private\n         */\n        _itemError: function(code, maybeNameOrNames, item) {\n            var message = this._options.messages[code],\n                allowedExtensions = [],\n                names = [].concat(maybeNameOrNames),\n                name = names[0],\n                buttonId = this._getButtonId(item),\n                validationBase = this._getValidationBase(buttonId),\n                extensionsForMessage, placeholderMatch;\n\n            function r(name, replacement) { message = message.replace(name, replacement); }\n\n            qq.each(validationBase.allowedExtensions, function(idx, allowedExtension) {\n                /**\n                 * If an argument is not a string, ignore it.  Added when a possible issue with MooTools hijacking the\n                 * `allowedExtensions` array was discovered.  See case #735 in the issue tracker for more details.\n                 */\n                if (qq.isString(allowedExtension)) {\n                    allowedExtensions.push(allowedExtension);\n                }\n            });\n\n            extensionsForMessage = allowedExtensions.join(\", \").toLowerCase();\n\n            r(\"{file}\", this._options.formatFileName(name));\n            r(\"{extensions}\", extensionsForMessage);\n            r(\"{sizeLimit}\", this._formatSize(validationBase.sizeLimit));\n            r(\"{minSizeLimit}\", this._formatSize(validationBase.minSizeLimit));\n\n            placeholderMatch = message.match(/(\\{\\w+\\})/g);\n            if (placeholderMatch !== null) {\n                qq.each(placeholderMatch, function(idx, placeholder) {\n                    r(placeholder, names[idx]);\n                });\n            }\n\n            this._options.callbacks.onError(null, name, message, undefined);\n\n            return message;\n        },\n\n        /**\n         * Conditionally orders a manual retry of a failed upload.\n         *\n         * @param id File ID of the failed upload\n         * @param callback Optional callback to invoke if a retry is prudent.\n         * In lieu of asking the upload handler to retry.\n         * @returns {boolean} true if a manual retry will occur\n         * @private\n         */\n        _manualRetry: function(id, callback) {\n            if (this._onBeforeManualRetry(id)) {\n                this._netUploadedOrQueued++;\n                this._uploadData.setStatus(id, qq.status.UPLOAD_RETRYING);\n\n                if (callback) {\n                    callback(id);\n                }\n                else {\n                    this._handler.retry(id);\n                }\n\n                return true;\n            }\n        },\n\n        _maybeAllComplete: function(id, status) {\n            var self = this,\n                notFinished = this._getNotFinished();\n\n            if (status === qq.status.UPLOAD_SUCCESSFUL) {\n                this._succeededSinceLastAllComplete.push(id);\n            }\n            else if (status === qq.status.UPLOAD_FAILED) {\n                this._failedSinceLastAllComplete.push(id);\n            }\n\n            if (notFinished === 0 &&\n                (this._succeededSinceLastAllComplete.length || this._failedSinceLastAllComplete.length)) {\n                // Attempt to ensure onAllComplete is not invoked before other callbacks, such as onCancel & onComplete\n                setTimeout(function() {\n                    self._onAllComplete(self._succeededSinceLastAllComplete, self._failedSinceLastAllComplete);\n                }, 0);\n            }\n        },\n\n        _maybeHandleIos8SafariWorkaround: function() {\n            var self = this;\n\n            if (this._options.workarounds.ios8SafariUploads && qq.ios800() && qq.iosSafari()) {\n                setTimeout(function() {\n                    window.alert(self._options.messages.unsupportedBrowserIos8Safari);\n                }, 0);\n                throw new qq.Error(this._options.messages.unsupportedBrowserIos8Safari);\n            }\n        },\n\n        _maybeParseAndSendUploadError: function(id, name, response, xhr) {\n            // Assuming no one will actually set the response code to something other than 200\n            // and still set 'success' to true...\n            if (!response.success) {\n                if (xhr && xhr.status !== 200 && !response.error) {\n                    this._options.callbacks.onError(id, name, \"XHR returned response code \" + xhr.status, xhr);\n                }\n                else {\n                    var errorReason = response.error ? response.error : this._options.text.defaultResponseError;\n                    this._options.callbacks.onError(id, name, errorReason, xhr);\n                }\n            }\n        },\n\n        _maybeProcessNextItemAfterOnValidateCallback: function(validItem, items, index, params, endpoint) {\n            var self = this;\n\n            if (items.length > index) {\n                if (validItem || !this._options.validation.stopOnFirstInvalidFile) {\n                    //use setTimeout to prevent a stack overflow with a large number of files in the batch & non-promissory callbacks\n                    setTimeout(function() {\n                        var validationDescriptor = self._getValidationDescriptor(items[index]),\n                            buttonId = self._getButtonId(items[index].file),\n                            button = self._getButton(buttonId);\n\n                        self._handleCheckedCallback({\n                            name: \"onValidate\",\n                            callback: qq.bind(self._options.callbacks.onValidate, self, validationDescriptor, button),\n                            onSuccess: qq.bind(self._onValidateCallbackSuccess, self, items, index, params, endpoint),\n                            onFailure: qq.bind(self._onValidateCallbackFailure, self, items, index, params, endpoint),\n                            identifier: \"Item '\" + validationDescriptor.name + \"', size: \" + validationDescriptor.size\n                        });\n                    }, 0);\n                }\n                else if (!validItem) {\n                    for (; index < items.length; index++) {\n                        self._fileOrBlobRejected(items[index].id);\n                    }\n                }\n            }\n        },\n\n        _onAllComplete: function(successful, failed) {\n            this._totalProgress && this._totalProgress.onAllComplete(successful, failed, this._preventRetries);\n\n            this._options.callbacks.onAllComplete(qq.extend([], successful), qq.extend([], failed));\n\n            this._succeededSinceLastAllComplete = [];\n            this._failedSinceLastAllComplete = [];\n        },\n\n        /**\n         * Attempt to automatically retry a failed upload.\n         *\n         * @param id The file ID of the failed upload\n         * @param name The name of the file associated with the failed upload\n         * @param responseJSON Response from the server, parsed into a javascript object\n         * @param xhr Ajax transport used to send the failed request\n         * @param callback Optional callback to be invoked if a retry is prudent.\n         * Invoked in lieu of asking the upload handler to retry.\n         * @returns {boolean} true if an auto-retry will occur\n         * @private\n         */\n        _onAutoRetry: function(id, name, responseJSON, xhr, callback) {\n            var self = this;\n\n            self._preventRetries[id] = responseJSON[self._options.retry.preventRetryResponseProperty];\n\n            if (self._shouldAutoRetry(id, name, responseJSON)) {\n                var retryWaitPeriod = self._options.retry.autoAttemptDelay * 1000;\n\n                self._maybeParseAndSendUploadError.apply(self, arguments);\n                self._options.callbacks.onAutoRetry(id, name, self._autoRetries[id]);\n                self._onBeforeAutoRetry(id, name);\n\n                self._uploadData.setStatus(id, qq.status.UPLOAD_RETRYING);\n                self._retryTimeouts[id] = setTimeout(function() {\n                    self.log(\"Starting retry for \" + name + \"...\");\n\n                    if (callback) {\n                        callback(id);\n                    }\n                    else {\n                        self._handler.retry(id);\n                    }\n                }, retryWaitPeriod);\n\n                return true;\n            }\n        },\n\n        _onBeforeAutoRetry: function(id, name) {\n            this.log(\"Waiting \" + this._options.retry.autoAttemptDelay + \" seconds before retrying \" + name + \"...\");\n        },\n\n        //return false if we should not attempt the requested retry\n        _onBeforeManualRetry: function(id) {\n            var itemLimit = this._currentItemLimit,\n                fileName;\n\n            if (this._preventRetries[id]) {\n                this.log(\"Retries are forbidden for id \" + id, \"warn\");\n                return false;\n            }\n            else if (this._handler.isValid(id)) {\n                fileName = this.getName(id);\n\n                if (this._options.callbacks.onManualRetry(id, fileName) === false) {\n                    return false;\n                }\n\n                if (itemLimit > 0 && this._netUploadedOrQueued + 1 > itemLimit) {\n                    this._itemError(\"retryFailTooManyItems\");\n                    return false;\n                }\n\n                this.log(\"Retrying upload for '\" + fileName + \"' (id: \" + id + \")...\");\n                return true;\n            }\n            else {\n                this.log(\"'\" + id + \"' is not a valid file ID\", \"error\");\n                return false;\n            }\n        },\n\n        _onCancel: function(id, name) {\n            this._netUploadedOrQueued--;\n\n            clearTimeout(this._retryTimeouts[id]);\n\n            var storedItemIndex = qq.indexOf(this._storedIds, id);\n            if (!this._options.autoUpload && storedItemIndex >= 0) {\n                this._storedIds.splice(storedItemIndex, 1);\n            }\n\n            this._uploadData.setStatus(id, qq.status.CANCELED);\n        },\n\n        _onComplete: function(id, name, result, xhr) {\n            if (!result.success) {\n                this._netUploadedOrQueued--;\n                this._uploadData.setStatus(id, qq.status.UPLOAD_FAILED);\n\n                if (result[this._options.retry.preventRetryResponseProperty] === true) {\n                    this._preventRetries[id] = true;\n                }\n            }\n            else {\n                if (result.thumbnailUrl) {\n                    this._thumbnailUrls[id] = result.thumbnailUrl;\n                }\n\n                this._netUploaded++;\n                this._uploadData.setStatus(id, qq.status.UPLOAD_SUCCESSFUL);\n            }\n\n            this._maybeParseAndSendUploadError(id, name, result, xhr);\n\n            return result.success ? true : false;\n        },\n\n        _onDelete: function(id) {\n            this._uploadData.setStatus(id, qq.status.DELETING);\n        },\n\n        _onDeleteComplete: function(id, xhrOrXdr, isError) {\n            var name = this.getName(id);\n\n            if (isError) {\n                this._uploadData.setStatus(id, qq.status.DELETE_FAILED);\n                this.log(\"Delete request for '\" + name + \"' has failed.\", \"error\");\n\n                // For error reporting, we only have access to the response status if this is not\n                // an `XDomainRequest`.\n                if (xhrOrXdr.withCredentials === undefined) {\n                    this._options.callbacks.onError(id, name, \"Delete request failed\", xhrOrXdr);\n                }\n                else {\n                    this._options.callbacks.onError(id, name, \"Delete request failed with response code \" + xhrOrXdr.status, xhrOrXdr);\n                }\n            }\n            else {\n                this._netUploadedOrQueued--;\n                this._netUploaded--;\n                this._handler.expunge(id);\n                this._uploadData.setStatus(id, qq.status.DELETED);\n                this.log(\"Delete request for '\" + name + \"' has succeeded.\");\n            }\n        },\n\n        _onInputChange: function(input) {\n            var fileIndex;\n\n            if (qq.supportedFeatures.ajaxUploading) {\n                for (fileIndex = 0; fileIndex < input.files.length; fileIndex++) {\n                    this._annotateWithButtonId(input.files[fileIndex], input);\n                }\n\n                this.addFiles(input.files);\n            }\n            // Android 2.3.x will fire `onchange` even if no file has been selected\n            else if (input.value.length > 0) {\n                this.addFiles(input);\n            }\n\n            qq.each(this._buttons, function(idx, button) {\n                button.reset();\n            });\n        },\n\n        _onProgress: function(id, name, loaded, total) {\n            this._totalProgress && this._totalProgress.onIndividualProgress(id, loaded, total);\n        },\n\n        _onSubmit: function(id, name) {\n            //nothing to do yet in core uploader\n        },\n\n        _onSubmitCallbackSuccess: function(id, name) {\n            this._onSubmit.apply(this, arguments);\n            this._uploadData.setStatus(id, qq.status.SUBMITTED);\n            this._onSubmitted.apply(this, arguments);\n\n            if (this._options.autoUpload) {\n                this._options.callbacks.onSubmitted.apply(this, arguments);\n                this._uploadFile(id);\n            }\n            else {\n                this._storeForLater(id);\n                this._options.callbacks.onSubmitted.apply(this, arguments);\n            }\n        },\n\n        _onSubmitDelete: function(id, onSuccessCallback, additionalMandatedParams) {\n            var uuid = this.getUuid(id),\n                adjustedOnSuccessCallback;\n\n            if (onSuccessCallback) {\n                adjustedOnSuccessCallback = qq.bind(onSuccessCallback, this, id, uuid, additionalMandatedParams);\n            }\n\n            if (this._isDeletePossible()) {\n                this._handleCheckedCallback({\n                    name: \"onSubmitDelete\",\n                    callback: qq.bind(this._options.callbacks.onSubmitDelete, this, id),\n                    onSuccess: adjustedOnSuccessCallback ||\n                        qq.bind(this._deleteHandler.sendDelete, this, id, uuid, additionalMandatedParams),\n                    identifier: id\n                });\n                return true;\n            }\n            else {\n                this.log(\"Delete request ignored for ID \" + id + \", delete feature is disabled or request not possible \" +\n                    \"due to CORS on a user agent that does not support pre-flighting.\", \"warn\");\n                return false;\n            }\n        },\n\n        _onSubmitted: function(id) {\n            //nothing to do in the base uploader\n        },\n\n        _onTotalProgress: function(loaded, total) {\n            this._options.callbacks.onTotalProgress(loaded, total);\n        },\n\n        _onUploadPrep: function(id) {\n            // nothing to do in the core uploader for now\n        },\n\n        _onUpload: function(id, name) {\n            this._uploadData.setStatus(id, qq.status.UPLOADING);\n        },\n\n        _onUploadChunk: function(id, chunkData) {\n            //nothing to do in the base uploader\n        },\n\n        _onUploadStatusChange: function(id, oldStatus, newStatus) {\n            // Make sure a \"queued\" retry attempt is canceled if the upload has been paused\n            if (newStatus === qq.status.PAUSED) {\n                clearTimeout(this._retryTimeouts[id]);\n            }\n        },\n\n        _onValidateBatchCallbackFailure: function(fileWrappers) {\n            var self = this;\n\n            qq.each(fileWrappers, function(idx, fileWrapper) {\n                self._fileOrBlobRejected(fileWrapper.id);\n            });\n        },\n\n        _onValidateBatchCallbackSuccess: function(validationDescriptors, items, params, endpoint, button) {\n            var errorMessage,\n                itemLimit = this._currentItemLimit,\n                proposedNetFilesUploadedOrQueued = this._netUploadedOrQueued;\n\n            if (itemLimit === 0 || proposedNetFilesUploadedOrQueued <= itemLimit) {\n                if (items.length > 0) {\n                    this._handleCheckedCallback({\n                        name: \"onValidate\",\n                        callback: qq.bind(this._options.callbacks.onValidate, this, validationDescriptors[0], button),\n                        onSuccess: qq.bind(this._onValidateCallbackSuccess, this, items, 0, params, endpoint),\n                        onFailure: qq.bind(this._onValidateCallbackFailure, this, items, 0, params, endpoint),\n                        identifier: \"Item '\" + items[0].file.name + \"', size: \" + items[0].file.size\n                    });\n                }\n                else {\n                    this._itemError(\"noFilesError\");\n                }\n            }\n            else {\n                this._onValidateBatchCallbackFailure(items);\n                errorMessage = this._options.messages.tooManyItemsError\n                    .replace(/\\{netItems\\}/g, proposedNetFilesUploadedOrQueued)\n                    .replace(/\\{itemLimit\\}/g, itemLimit);\n                this._batchError(errorMessage);\n            }\n        },\n\n        _onValidateCallbackFailure: function(items, index, params, endpoint) {\n            var nextIndex = index + 1;\n\n            this._fileOrBlobRejected(items[index].id, items[index].file.name);\n\n            this._maybeProcessNextItemAfterOnValidateCallback(false, items, nextIndex, params, endpoint);\n        },\n\n        _onValidateCallbackSuccess: function(items, index, params, endpoint) {\n            var self = this,\n                nextIndex = index + 1,\n                validationDescriptor = this._getValidationDescriptor(items[index]);\n\n            this._validateFileOrBlobData(items[index], validationDescriptor)\n                .then(\n                function() {\n                    self._upload(items[index].id, params, endpoint);\n                    self._maybeProcessNextItemAfterOnValidateCallback(true, items, nextIndex, params, endpoint);\n                },\n                function() {\n                    self._maybeProcessNextItemAfterOnValidateCallback(false, items, nextIndex, params, endpoint);\n                }\n            );\n        },\n\n        _prepareItemsForUpload: function(items, params, endpoint) {\n            if (items.length === 0) {\n                this._itemError(\"noFilesError\");\n                return;\n            }\n\n            var validationDescriptors = this._getValidationDescriptors(items),\n                buttonId = this._getButtonId(items[0].file),\n                button = this._getButton(buttonId);\n\n            this._handleCheckedCallback({\n                name: \"onValidateBatch\",\n                callback: qq.bind(this._options.callbacks.onValidateBatch, this, validationDescriptors, button),\n                onSuccess: qq.bind(this._onValidateBatchCallbackSuccess, this, validationDescriptors, items, params, endpoint, button),\n                onFailure: qq.bind(this._onValidateBatchCallbackFailure, this, items),\n                identifier: \"batch validation\"\n            });\n        },\n\n        _preventLeaveInProgress: function() {\n            var self = this;\n\n            this._disposeSupport.attach(window, \"beforeunload\", function(e) {\n                if (self.getInProgress()) {\n                    e = e || window.event;\n                    // for ie, ff\n                    e.returnValue = self._options.messages.onLeave;\n                    // for webkit\n                    return self._options.messages.onLeave;\n                }\n            });\n        },\n\n        // Attempts to refresh session data only if the `qq.Session` module exists\n        // and a session endpoint has been specified.  The `onSessionRequestComplete`\n        // callback will be invoked once the refresh is complete.\n        _refreshSessionData: function() {\n            var self = this,\n                options = this._options.session;\n\n            /* jshint eqnull:true */\n            if (qq.Session && this._options.session.endpoint != null) {\n                if (!this._session) {\n                    qq.extend(options, {cors: this._options.cors});\n\n                    options.log = qq.bind(this.log, this);\n                    options.addFileRecord = qq.bind(this._addCannedFile, this);\n\n                    this._session = new qq.Session(options);\n                }\n\n                setTimeout(function() {\n                    self._session.refresh().then(function(response, xhrOrXdr) {\n                        self._sessionRequestComplete();\n                        self._options.callbacks.onSessionRequestComplete(response, true, xhrOrXdr);\n\n                    }, function(response, xhrOrXdr) {\n\n                        self._options.callbacks.onSessionRequestComplete(response, false, xhrOrXdr);\n                    });\n                }, 0);\n            }\n        },\n\n        _sessionRequestComplete: function() {},\n\n        _setSize: function(id, newSize) {\n            this._uploadData.updateSize(id, newSize);\n            this._totalProgress && this._totalProgress.onNewSize(id);\n        },\n\n        _shouldAutoRetry: function(id, name, responseJSON) {\n            var uploadData = this._uploadData.retrieve({id: id});\n\n            /*jshint laxbreak: true */\n            if (!this._preventRetries[id]\n                && this._options.retry.enableAuto\n                && uploadData.status !== qq.status.PAUSED) {\n\n                if (this._autoRetries[id] === undefined) {\n                    this._autoRetries[id] = 0;\n                }\n\n                if (this._autoRetries[id] < this._options.retry.maxAutoAttempts) {\n                    this._autoRetries[id] += 1;\n                    return true;\n                }\n            }\n\n            return false;\n        },\n\n        _storeForLater: function(id) {\n            this._storedIds.push(id);\n        },\n\n        // Maps a file with the button that was used to select it.\n        _trackButton: function(id) {\n            var buttonId;\n\n            if (qq.supportedFeatures.ajaxUploading) {\n                buttonId = this._handler.getFile(id).qqButtonId;\n            }\n            else {\n                buttonId = this._getButtonId(this._handler.getInput(id));\n            }\n\n            if (buttonId) {\n                this._buttonIdsForFileIds[id] = buttonId;\n            }\n        },\n\n        _updateFormSupportAndParams: function(formElementOrId) {\n            this._options.form.element = formElementOrId;\n\n            this._formSupport = qq.FormSupport && new qq.FormSupport(\n                    this._options.form, qq.bind(this.uploadStoredFiles, this), qq.bind(this.log, this)\n                );\n\n            if (this._formSupport && this._formSupport.attachedToForm) {\n                this._paramsStore.addReadOnly(null, this._formSupport.getFormInputsAsObject);\n\n                this._options.autoUpload = this._formSupport.newAutoUpload;\n                if (this._formSupport.newEndpoint) {\n                    this.setEndpoint(this._formSupport.newEndpoint);\n                }\n            }\n        },\n\n        _upload: function(id, params, endpoint) {\n            var name = this.getName(id);\n\n            if (params) {\n                this.setParams(params, id);\n            }\n\n            if (endpoint) {\n                this.setEndpoint(endpoint, id);\n            }\n\n            this._handleCheckedCallback({\n                name: \"onSubmit\",\n                callback: qq.bind(this._options.callbacks.onSubmit, this, id, name),\n                onSuccess: qq.bind(this._onSubmitCallbackSuccess, this, id, name),\n                onFailure: qq.bind(this._fileOrBlobRejected, this, id, name),\n                identifier: id\n            });\n        },\n\n        _uploadFile: function(id) {\n            if (!this._handler.upload(id)) {\n                this._uploadData.setStatus(id, qq.status.QUEUED);\n            }\n        },\n\n        _uploadStoredFiles: function() {\n            var idToUpload, stillSubmitting,\n                self = this;\n\n            while (this._storedIds.length) {\n                idToUpload = this._storedIds.shift();\n                this._uploadFile(idToUpload);\n            }\n\n            // If we are still waiting for some files to clear validation, attempt to upload these again in a bit\n            stillSubmitting = this.getUploads({status: qq.status.SUBMITTING}).length;\n            if (stillSubmitting) {\n                qq.log(\"Still waiting for \" + stillSubmitting + \" files to clear submit queue. Will re-parse stored IDs array shortly.\");\n                setTimeout(function() {\n                    self._uploadStoredFiles();\n                }, 1000);\n            }\n        },\n\n        /**\n         * Performs some internal validation checks on an item, defined in the `validation` option.\n         *\n         * @param fileWrapper Wrapper containing a `file` along with an `id`\n         * @param validationDescriptor Normalized information about the item (`size`, `name`).\n         * @returns qq.Promise with appropriate callbacks invoked depending on the validity of the file\n         * @private\n         */\n        _validateFileOrBlobData: function(fileWrapper, validationDescriptor) {\n            var self = this,\n                file = (function() {\n                    if (fileWrapper.file instanceof qq.BlobProxy) {\n                        return fileWrapper.file.referenceBlob;\n                    }\n                    return fileWrapper.file;\n                }()),\n                name = validationDescriptor.name,\n                size = validationDescriptor.size,\n                buttonId = this._getButtonId(fileWrapper.file),\n                validationBase = this._getValidationBase(buttonId),\n                validityChecker = new qq.Promise();\n\n            validityChecker.then(\n                function() {},\n                function() {\n                    self._fileOrBlobRejected(fileWrapper.id, name);\n                });\n\n            if (qq.isFileOrInput(file) && !this._isAllowedExtension(validationBase.allowedExtensions, name)) {\n                this._itemError(\"typeError\", name, file);\n                return validityChecker.failure();\n            }\n\n            if (!this._options.validation.allowEmpty && size === 0) {\n                this._itemError(\"emptyError\", name, file);\n                return validityChecker.failure();\n            }\n\n            if (size > 0 && validationBase.sizeLimit && size > validationBase.sizeLimit) {\n                this._itemError(\"sizeError\", name, file);\n                return validityChecker.failure();\n            }\n\n            if (size > 0 && size < validationBase.minSizeLimit) {\n                this._itemError(\"minSizeError\", name, file);\n                return validityChecker.failure();\n            }\n\n            if (qq.ImageValidation && qq.supportedFeatures.imagePreviews && qq.isFile(file)) {\n                new qq.ImageValidation(file, qq.bind(self.log, self)).validate(validationBase.image).then(\n                    validityChecker.success,\n                    function(errorCode) {\n                        self._itemError(errorCode + \"ImageError\", name, file);\n                        validityChecker.failure();\n                    }\n                );\n            }\n            else {\n                validityChecker.success();\n            }\n\n            return validityChecker;\n        },\n\n        _wrapCallbacks: function() {\n            var self, safeCallback, prop;\n\n            self = this;\n\n            safeCallback = function(name, callback, args) {\n                var errorMsg;\n\n                try {\n                    return callback.apply(self, args);\n                }\n                catch (exception) {\n                    errorMsg = exception.message || exception.toString();\n                    self.log(\"Caught exception in '\" + name + \"' callback - \" + errorMsg, \"error\");\n                }\n            };\n\n            /* jshint forin: false, loopfunc: true */\n            for (prop in this._options.callbacks) {\n                (function() {\n                    var callbackName, callbackFunc;\n                    callbackName = prop;\n                    callbackFunc = self._options.callbacks[callbackName];\n                    self._options.callbacks[callbackName] = function() {\n                        return safeCallback(callbackName, callbackFunc, arguments);\n                    };\n                }());\n            }\n        }\n    };\n}());\n","/*globals qq*/\n(function() {\n    \"use strict\";\n\n    qq.FineUploaderBasic = function(o) {\n        var self = this;\n\n        // These options define FineUploaderBasic mode.\n        this._options = {\n            debug: false,\n            button: null,\n            multiple: true,\n            maxConnections: 3,\n            disableCancelForFormUploads: false,\n            autoUpload: true,\n\n            request: {\n                customHeaders: {},\n                endpoint: \"/server/upload\",\n                filenameParam: \"qqfilename\",\n                forceMultipart: true,\n                inputName: \"qqfile\",\n                method: \"POST\",\n                params: {},\n                paramsInBody: true,\n                totalFileSizeName: \"qqtotalfilesize\",\n                uuidName: \"qquuid\"\n            },\n\n            validation: {\n                allowedExtensions: [],\n                sizeLimit: 0,\n                minSizeLimit: 0,\n                itemLimit: 0,\n                stopOnFirstInvalidFile: true,\n                acceptFiles: null,\n                image: {\n                    maxHeight: 0,\n                    maxWidth: 0,\n                    minHeight: 0,\n                    minWidth: 0\n                },\n                allowEmpty: false\n            },\n\n            callbacks: {\n                onSubmit: function(id, name) {},\n                onSubmitted: function(id, name) {},\n                onComplete: function(id, name, responseJSON, maybeXhr) {},\n                onAllComplete: function(successful, failed) {},\n                onCancel: function(id, name) {},\n                onUpload: function(id, name) {},\n                onUploadChunk: function(id, name, chunkData) {},\n                onUploadChunkSuccess: function(id, chunkData, responseJSON, xhr) {},\n                onResume: function(id, fileName, chunkData) {},\n                onProgress: function(id, name, loaded, total) {},\n                onTotalProgress: function(loaded, total) {},\n                onError: function(id, name, reason, maybeXhrOrXdr) {},\n                onAutoRetry: function(id, name, attemptNumber) {},\n                onManualRetry: function(id, name) {},\n                onValidateBatch: function(fileOrBlobData) {},\n                onValidate: function(fileOrBlobData) {},\n                onSubmitDelete: function(id) {},\n                onDelete: function(id) {},\n                onDeleteComplete: function(id, xhrOrXdr, isError) {},\n                onPasteReceived: function(blob) {},\n                onStatusChange: function(id, oldStatus, newStatus) {},\n                onSessionRequestComplete: function(response, success, xhrOrXdr) {}\n            },\n\n            messages: {\n                typeError: \"{file} has an invalid extension. Valid extension(s): {extensions}.\",\n                sizeError: \"{file} is too large, maximum file size is {sizeLimit}.\",\n                minSizeError: \"{file} is too small, minimum file size is {minSizeLimit}.\",\n                emptyError: \"{file} is empty, please select files again without it.\",\n                noFilesError: \"No files to upload.\",\n                tooManyItemsError: \"Too many items ({netItems}) would be uploaded.  Item limit is {itemLimit}.\",\n                maxHeightImageError: \"Image is too tall.\",\n                maxWidthImageError: \"Image is too wide.\",\n                minHeightImageError: \"Image is not tall enough.\",\n                minWidthImageError: \"Image is not wide enough.\",\n                retryFailTooManyItems: \"Retry failed - you have reached your file limit.\",\n                onLeave: \"The files are being uploaded, if you leave now the upload will be canceled.\",\n                unsupportedBrowserIos8Safari: \"Unrecoverable error - this browser does not permit file uploading of any kind due to serious bugs in iOS8 Safari.  Please use iOS8 Chrome until Apple fixes these issues.\"\n            },\n\n            retry: {\n                enableAuto: false,\n                maxAutoAttempts: 3,\n                autoAttemptDelay: 5,\n                preventRetryResponseProperty: \"preventRetry\"\n            },\n\n            classes: {\n                buttonHover: \"qq-upload-button-hover\",\n                buttonFocus: \"qq-upload-button-focus\"\n            },\n\n            chunking: {\n                enabled: false,\n                concurrent: {\n                    enabled: false\n                },\n                mandatory: false,\n                paramNames: {\n                    partIndex: \"qqpartindex\",\n                    partByteOffset: \"qqpartbyteoffset\",\n                    chunkSize: \"qqchunksize\",\n                    totalFileSize: \"qqtotalfilesize\",\n                    totalParts: \"qqtotalparts\"\n                },\n                partSize: 2000000,\n                // only relevant for traditional endpoints, only required when concurrent.enabled === true\n                success: {\n                    endpoint: null\n                }\n            },\n\n            resume: {\n                enabled: false,\n                recordsExpireIn: 7, //days\n                paramNames: {\n                    resuming: \"qqresume\"\n                }\n            },\n\n            formatFileName: function(fileOrBlobName) {\n                return fileOrBlobName;\n            },\n\n            text: {\n                defaultResponseError: \"Upload failure reason unknown\",\n                fileInputTitle: \"file input\",\n                sizeSymbols: [\"kB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\"]\n            },\n\n            deleteFile: {\n                enabled: false,\n                method: \"DELETE\",\n                endpoint: \"/server/upload\",\n                customHeaders: {},\n                params: {}\n            },\n\n            cors: {\n                expected: false,\n                sendCredentials: false,\n                allowXdr: false\n            },\n\n            blobs: {\n                defaultName: \"misc_data\"\n            },\n\n            paste: {\n                targetElement: null,\n                defaultName: \"pasted_image\"\n            },\n\n            camera: {\n                ios: false,\n\n                // if ios is true: button is null means target the default button, otherwise target the button specified\n                button: null\n            },\n\n            // This refers to additional upload buttons to be handled by Fine Uploader.\n            // Each element is an object, containing `element` as the only required\n            // property.  The `element` must be a container that will ultimately\n            // contain an invisible `<input type=\"file\">` created by Fine Uploader.\n            // Optional properties of each object include `multiple`, `validation`,\n            // and `folders`.\n            extraButtons: [],\n\n            // Depends on the session module.  Used to query the server for an initial file list\n            // during initialization and optionally after a `reset`.\n            session: {\n                endpoint: null,\n                params: {},\n                customHeaders: {},\n                refreshOnReset: true\n            },\n\n            // Send parameters associated with an existing form along with the files\n            form: {\n                // Element ID, HTMLElement, or null\n                element: \"qq-form\",\n\n                // Overrides the base `autoUpload`, unless `element` is null.\n                autoUpload: false,\n\n                // true = upload files on form submission (and squelch submit event)\n                interceptSubmit: true\n            },\n\n            // scale images client side, upload a new file for each scaled version\n            scaling: {\n                customResizer: null,\n\n                // send the original file as well\n                sendOriginal: true,\n\n                // fox orientation for scaled images\n                orient: true,\n\n                // If null, scaled image type will match reference image type.  This value will be referred to\n                // for any size record that does not specific a type.\n                defaultType: null,\n\n                defaultQuality: 80,\n\n                failureText: \"Failed to scale\",\n\n                includeExif: false,\n\n                // metadata about each requested scaled version\n                sizes: []\n            },\n\n            workarounds: {\n                iosEmptyVideos: true,\n                ios8SafariUploads: true,\n                ios8BrowserCrash: false\n            }\n        };\n\n        // Replace any default options with user defined ones\n        qq.extend(this._options, o, true);\n\n        this._buttons = [];\n        this._extraButtonSpecs = {};\n        this._buttonIdsForFileIds = [];\n\n        this._wrapCallbacks();\n        this._disposeSupport =  new qq.DisposeSupport();\n\n        this._storedIds = [];\n        this._autoRetries = [];\n        this._retryTimeouts = [];\n        this._preventRetries = [];\n        this._thumbnailUrls = [];\n\n        this._netUploadedOrQueued = 0;\n        this._netUploaded = 0;\n        this._uploadData = this._createUploadDataTracker();\n\n        this._initFormSupportAndParams();\n\n        this._customHeadersStore = this._createStore(this._options.request.customHeaders);\n        this._deleteFileCustomHeadersStore = this._createStore(this._options.deleteFile.customHeaders);\n\n        this._deleteFileParamsStore = this._createStore(this._options.deleteFile.params);\n\n        this._endpointStore = this._createStore(this._options.request.endpoint);\n        this._deleteFileEndpointStore = this._createStore(this._options.deleteFile.endpoint);\n\n        this._handler = this._createUploadHandler();\n\n        this._deleteHandler = qq.DeleteFileAjaxRequester && this._createDeleteHandler();\n\n        if (this._options.button) {\n            this._defaultButtonId = this._createUploadButton({\n                element: this._options.button,\n                title: this._options.text.fileInputTitle\n            }).getButtonId();\n        }\n\n        this._generateExtraButtonSpecs();\n\n        this._handleCameraAccess();\n\n        if (this._options.paste.targetElement) {\n            if (qq.PasteSupport) {\n                this._pasteHandler = this._createPasteHandler();\n            }\n            else {\n                this.log(\"Paste support module not found\", \"error\");\n            }\n        }\n\n        this._preventLeaveInProgress();\n\n        this._imageGenerator = qq.ImageGenerator && new qq.ImageGenerator(qq.bind(this.log, this));\n        this._refreshSessionData();\n\n        this._succeededSinceLastAllComplete = [];\n        this._failedSinceLastAllComplete = [];\n\n        this._scaler = (qq.Scaler && new qq.Scaler(this._options.scaling, qq.bind(this.log, this))) || {};\n        if (this._scaler.enabled) {\n            this._customNewFileHandler = qq.bind(this._scaler.handleNewFile, this._scaler);\n        }\n\n        if (qq.TotalProgress && qq.supportedFeatures.progressBar) {\n            this._totalProgress = new qq.TotalProgress(\n                qq.bind(this._onTotalProgress, this),\n\n                function(id) {\n                    var entry = self._uploadData.retrieve({id: id});\n                    return (entry && entry.size) || 0;\n                }\n            );\n        }\n\n        this._currentItemLimit = this._options.validation.itemLimit;\n    };\n\n    // Define the private & public API methods.\n    qq.FineUploaderBasic.prototype = qq.basePublicApi;\n    qq.extend(qq.FineUploaderBasic.prototype, qq.basePrivateApi);\n}());\n","/*globals qq, XDomainRequest*/\n/** Generic class for sending non-upload ajax requests and handling the associated responses **/\nqq.AjaxRequester = function(o) {\n    \"use strict\";\n\n    var log, shouldParamsBeInQueryString,\n        queue = [],\n        requestData = {},\n        options = {\n            acceptHeader: null,\n            validMethods: [\"PATCH\", \"POST\", \"PUT\"],\n            method: \"POST\",\n            contentType: \"application/x-www-form-urlencoded\",\n            maxConnections: 3,\n            customHeaders: {},\n            endpointStore: {},\n            paramsStore: {},\n            mandatedParams: {},\n            allowXRequestedWithAndCacheControl: true,\n            successfulResponseCodes: {\n                DELETE: [200, 202, 204],\n                PATCH: [200, 201, 202, 203, 204],\n                POST: [200, 201, 202, 203, 204],\n                PUT: [200, 201, 202, 203, 204],\n                GET: [200]\n            },\n            cors: {\n                expected: false,\n                sendCredentials: false\n            },\n            log: function(str, level) {},\n            onSend: function(id) {},\n            onComplete: function(id, xhrOrXdr, isError) {},\n            onProgress: null\n        };\n\n    qq.extend(options, o);\n    log = options.log;\n\n    if (qq.indexOf(options.validMethods, options.method) < 0) {\n        throw new Error(\"'\" + options.method + \"' is not a supported method for this type of request!\");\n    }\n\n    // [Simple methods](http://www.w3.org/TR/cors/#simple-method)\n    // are defined by the W3C in the CORS spec as a list of methods that, in part,\n    // make a CORS request eligible to be exempt from preflighting.\n    function isSimpleMethod() {\n        return qq.indexOf([\"GET\", \"POST\", \"HEAD\"], options.method) >= 0;\n    }\n\n    // [Simple headers](http://www.w3.org/TR/cors/#simple-header)\n    // are defined by the W3C in the CORS spec as a list of headers that, in part,\n    // make a CORS request eligible to be exempt from preflighting.\n    function containsNonSimpleHeaders(headers) {\n        var containsNonSimple = false;\n\n        qq.each(containsNonSimple, function(idx, header) {\n            if (qq.indexOf([\"Accept\", \"Accept-Language\", \"Content-Language\", \"Content-Type\"], header) < 0) {\n                containsNonSimple = true;\n                return false;\n            }\n        });\n\n        return containsNonSimple;\n    }\n\n    function isXdr(xhr) {\n        //The `withCredentials` test is a commonly accepted way to determine if XHR supports CORS.\n        return options.cors.expected && xhr.withCredentials === undefined;\n    }\n\n    // Returns either a new `XMLHttpRequest` or `XDomainRequest` instance.\n    function getCorsAjaxTransport() {\n        var xhrOrXdr;\n\n        if (window.XMLHttpRequest || window.ActiveXObject) {\n            xhrOrXdr = qq.createXhrInstance();\n\n            if (xhrOrXdr.withCredentials === undefined) {\n                xhrOrXdr = new XDomainRequest();\n                // Workaround for XDR bug in IE9 - https://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment\n                xhrOrXdr.onload = function() {};\n                xhrOrXdr.onerror = function() {};\n                xhrOrXdr.ontimeout = function() {};\n                xhrOrXdr.onprogress = function() {};\n            }\n        }\n\n        return xhrOrXdr;\n    }\n\n    // Returns either a new XHR/XDR instance, or an existing one for the associated `File` or `Blob`.\n    function getXhrOrXdr(id, suppliedXhr) {\n        var xhrOrXdr = requestData[id].xhr;\n\n        if (!xhrOrXdr) {\n            if (suppliedXhr) {\n                xhrOrXdr = suppliedXhr;\n            }\n            else {\n                if (options.cors.expected) {\n                    xhrOrXdr = getCorsAjaxTransport();\n                }\n                else {\n                    xhrOrXdr = qq.createXhrInstance();\n                }\n            }\n\n            requestData[id].xhr = xhrOrXdr;\n        }\n\n        return xhrOrXdr;\n    }\n\n    // Removes element from queue, sends next request\n    function dequeue(id) {\n        var i = qq.indexOf(queue, id),\n            max = options.maxConnections,\n            nextId;\n\n        delete requestData[id];\n        queue.splice(i, 1);\n\n        if (queue.length >= max && i < max) {\n            nextId = queue[max - 1];\n            sendRequest(nextId);\n        }\n    }\n\n    function onComplete(id, xdrError) {\n        var xhr = getXhrOrXdr(id),\n            method = options.method,\n            isError = xdrError === true;\n\n        dequeue(id);\n\n        if (isError) {\n            log(method + \" request for \" + id + \" has failed\", \"error\");\n        }\n        else if (!isXdr(xhr) && !isResponseSuccessful(xhr.status)) {\n            isError = true;\n            log(method + \" request for \" + id + \" has failed - response code \" + xhr.status, \"error\");\n        }\n\n        options.onComplete(id, xhr, isError);\n    }\n\n    function getParams(id) {\n        var onDemandParams = requestData[id].additionalParams,\n            mandatedParams = options.mandatedParams,\n            params;\n\n        if (options.paramsStore.get) {\n            params = options.paramsStore.get(id);\n        }\n\n        if (onDemandParams) {\n            qq.each(onDemandParams, function(name, val) {\n                params = params || {};\n                params[name] = val;\n            });\n        }\n\n        if (mandatedParams) {\n            qq.each(mandatedParams, function(name, val) {\n                params = params || {};\n                params[name] = val;\n            });\n        }\n\n        return params;\n    }\n\n    function sendRequest(id, optXhr) {\n        var xhr = getXhrOrXdr(id, optXhr),\n            method = options.method,\n            params = getParams(id),\n            payload = requestData[id].payload,\n            url;\n\n        options.onSend(id);\n\n        url = createUrl(id, params, requestData[id].additionalQueryParams);\n\n        // XDR and XHR status detection APIs differ a bit.\n        if (isXdr(xhr)) {\n            xhr.onload = getXdrLoadHandler(id);\n            xhr.onerror = getXdrErrorHandler(id);\n        }\n        else {\n            xhr.onreadystatechange = getXhrReadyStateChangeHandler(id);\n        }\n\n        registerForUploadProgress(id);\n\n        // The last parameter is assumed to be ignored if we are actually using `XDomainRequest`.\n        xhr.open(method, url, true);\n\n        // Instruct the transport to send cookies along with the CORS request,\n        // unless we are using `XDomainRequest`, which is not capable of this.\n        if (options.cors.expected && options.cors.sendCredentials && !isXdr(xhr)) {\n            xhr.withCredentials = true;\n        }\n\n        setHeaders(id);\n\n        log(\"Sending \" + method + \" request for \" + id);\n\n        if (payload) {\n            xhr.send(payload);\n        }\n        else if (shouldParamsBeInQueryString || !params) {\n            xhr.send();\n        }\n        else if (params && options.contentType && options.contentType.toLowerCase().indexOf(\"application/x-www-form-urlencoded\") >= 0) {\n            xhr.send(qq.obj2url(params, \"\"));\n        }\n        else if (params && options.contentType && options.contentType.toLowerCase().indexOf(\"application/json\") >= 0) {\n            xhr.send(JSON.stringify(params));\n        }\n        else {\n            xhr.send(params);\n        }\n\n        return xhr;\n    }\n\n    function createUrl(id, params, additionalQueryParams) {\n        var endpoint = options.endpointStore.get(id),\n            addToPath = requestData[id].addToPath;\n\n        /*jshint -W116,-W041 */\n        if (addToPath != undefined) {\n            endpoint += \"/\" + addToPath;\n        }\n\n        if (shouldParamsBeInQueryString && params) {\n            endpoint = qq.obj2url(params, endpoint);\n        }\n\n        if (additionalQueryParams) {\n            endpoint = qq.obj2url(additionalQueryParams, endpoint);\n        }\n\n        return endpoint;\n    }\n\n    // Invoked by the UA to indicate a number of possible states that describe\n    // a live `XMLHttpRequest` transport.\n    function getXhrReadyStateChangeHandler(id) {\n        return function() {\n            if (getXhrOrXdr(id).readyState === 4) {\n                onComplete(id);\n            }\n        };\n    }\n\n    function registerForUploadProgress(id) {\n        var onProgress = options.onProgress;\n\n        if (onProgress) {\n            getXhrOrXdr(id).upload.onprogress = function(e) {\n                if (e.lengthComputable) {\n                    onProgress(id, e.loaded, e.total);\n                }\n            };\n        }\n    }\n\n    // This will be called by IE to indicate **success** for an associated\n    // `XDomainRequest` transported request.\n    function getXdrLoadHandler(id) {\n        return function() {\n            onComplete(id);\n        };\n    }\n\n    // This will be called by IE to indicate **failure** for an associated\n    // `XDomainRequest` transported request.\n    function getXdrErrorHandler(id) {\n        return function() {\n            onComplete(id, true);\n        };\n    }\n\n    function setHeaders(id) {\n        var xhr = getXhrOrXdr(id),\n            customHeaders = options.customHeaders,\n            onDemandHeaders = requestData[id].additionalHeaders || {},\n            method = options.method,\n            allHeaders = {};\n\n        // If XDomainRequest is being used, we can't set headers, so just ignore this block.\n        if (!isXdr(xhr)) {\n            options.acceptHeader && xhr.setRequestHeader(\"Accept\", options.acceptHeader);\n\n            // Only attempt to add X-Requested-With & Cache-Control if permitted\n            if (options.allowXRequestedWithAndCacheControl) {\n                // Do not add X-Requested-With & Cache-Control if this is a cross-origin request\n                // OR the cross-origin request contains a non-simple method or header.\n                // This is done to ensure a preflight is not triggered exclusively based on the\n                // addition of these 2 non-simple headers.\n                if (!options.cors.expected || (!isSimpleMethod() || containsNonSimpleHeaders(customHeaders))) {\n                    xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n                    xhr.setRequestHeader(\"Cache-Control\", \"no-cache\");\n                }\n            }\n\n            if (options.contentType && (method === \"POST\" || method === \"PUT\")) {\n                xhr.setRequestHeader(\"Content-Type\", options.contentType);\n            }\n\n            qq.extend(allHeaders, qq.isFunction(customHeaders) ? customHeaders(id) : customHeaders);\n            qq.extend(allHeaders, onDemandHeaders);\n\n            qq.each(allHeaders, function(name, val) {\n                xhr.setRequestHeader(name, val);\n            });\n        }\n    }\n\n    function isResponseSuccessful(responseCode) {\n        return qq.indexOf(options.successfulResponseCodes[options.method], responseCode) >= 0;\n    }\n\n    function prepareToSend(id, optXhr, addToPath, additionalParams, additionalQueryParams, additionalHeaders, payload) {\n        requestData[id] = {\n            addToPath: addToPath,\n            additionalParams: additionalParams,\n            additionalQueryParams: additionalQueryParams,\n            additionalHeaders: additionalHeaders,\n            payload: payload\n        };\n\n        var len = queue.push(id);\n\n        // if too many active connections, wait...\n        if (len <= options.maxConnections) {\n            return sendRequest(id, optXhr);\n        }\n    }\n\n    shouldParamsBeInQueryString = options.method === \"GET\" || options.method === \"DELETE\";\n\n    qq.extend(this, {\n        // Start the process of sending the request.  The ID refers to the file associated with the request.\n        initTransport: function(id) {\n            var path, params, headers, payload, cacheBuster, additionalQueryParams;\n\n            return {\n                // Optionally specify the end of the endpoint path for the request.\n                withPath: function(appendToPath) {\n                    path = appendToPath;\n                    return this;\n                },\n\n                // Optionally specify additional parameters to send along with the request.\n                // These will be added to the query string for GET/DELETE requests or the payload\n                // for POST/PUT requests.  The Content-Type of the request will be used to determine\n                // how these parameters should be formatted as well.\n                withParams: function(additionalParams) {\n                    params = additionalParams;\n                    return this;\n                },\n\n                withQueryParams: function(_additionalQueryParams_) {\n                    additionalQueryParams = _additionalQueryParams_;\n                    return this;\n                },\n\n                // Optionally specify additional headers to send along with the request.\n                withHeaders: function(additionalHeaders) {\n                    headers = additionalHeaders;\n                    return this;\n                },\n\n                // Optionally specify a payload/body for the request.\n                withPayload: function(thePayload) {\n                    payload = thePayload;\n                    return this;\n                },\n\n                // Appends a cache buster (timestamp) to the request URL as a query parameter (only if GET or DELETE)\n                withCacheBuster: function() {\n                    cacheBuster = true;\n                    return this;\n                },\n\n                // Send the constructed request.\n                send: function(optXhr) {\n                    if (cacheBuster && qq.indexOf([\"GET\", \"DELETE\"], options.method) >= 0) {\n                        params.qqtimestamp = new Date().getTime();\n                    }\n\n                    return prepareToSend(id, optXhr, path, params, additionalQueryParams, headers, payload);\n                }\n            };\n        },\n\n        canceled: function(id) {\n            dequeue(id);\n        }\n    });\n};\n","/* globals qq */\n/**\n * Common upload handler functions.\n *\n * @constructor\n */\nqq.UploadHandler = function(spec) {\n    \"use strict\";\n\n    var proxy = spec.proxy,\n        fileState = {},\n        onCancel = proxy.onCancel,\n        getName = proxy.getName;\n\n    qq.extend(this, {\n        add: function(id, fileItem) {\n            fileState[id] = fileItem;\n            fileState[id].temp = {};\n        },\n\n        cancel: function(id) {\n            var self = this,\n                cancelFinalizationEffort = new qq.Promise(),\n                onCancelRetVal = onCancel(id, getName(id), cancelFinalizationEffort);\n\n            onCancelRetVal.then(function() {\n                if (self.isValid(id)) {\n                    fileState[id].canceled = true;\n                    self.expunge(id);\n                }\n                cancelFinalizationEffort.success();\n            });\n        },\n\n        expunge: function(id) {\n            delete fileState[id];\n        },\n\n        getThirdPartyFileId: function(id) {\n            return fileState[id].key;\n        },\n\n        isValid: function(id) {\n            return fileState[id] !== undefined;\n        },\n\n        reset: function() {\n            fileState = {};\n        },\n\n        _getFileState: function(id) {\n            return fileState[id];\n        },\n\n        _setThirdPartyFileId: function(id, thirdPartyFileId) {\n            fileState[id].key = thirdPartyFileId;\n        },\n\n        _wasCanceled: function(id) {\n            return !!fileState[id].canceled;\n        }\n    });\n};\n","/*globals qq*/\n/**\n * Base upload handler module.  Controls more specific handlers.\n *\n * @param o Options.  Passed along to the specific handler submodule as well.\n * @param namespace [optional] Namespace for the specific handler.\n */\nqq.UploadHandlerController = function(o, namespace) {\n    \"use strict\";\n\n    var controller = this,\n        chunkingPossible = false,\n        concurrentChunkingPossible = false,\n        chunking, preventRetryResponse, log, handler,\n\n    options = {\n        paramsStore: {},\n        maxConnections: 3, // maximum number of concurrent uploads\n        chunking: {\n            enabled: false,\n            multiple: {\n                enabled: false\n            }\n        },\n        log: function(str, level) {},\n        onProgress: function(id, fileName, loaded, total) {},\n        onComplete: function(id, fileName, response, xhr) {},\n        onCancel: function(id, fileName) {},\n        onUploadPrep: function(id) {}, // Called if non-trivial operations will be performed before onUpload\n        onUpload: function(id, fileName) {},\n        onUploadChunk: function(id, fileName, chunkData) {},\n        onUploadChunkSuccess: function(id, chunkData, response, xhr) {},\n        onAutoRetry: function(id, fileName, response, xhr) {},\n        onResume: function(id, fileName, chunkData) {},\n        onUuidChanged: function(id, newUuid) {},\n        getName: function(id) {},\n        setSize: function(id, newSize) {},\n        isQueued: function(id) {},\n        getIdsInProxyGroup: function(id) {},\n        getIdsInBatch: function(id) {}\n    },\n\n    chunked = {\n        // Called when each chunk has uploaded successfully\n        done: function(id, chunkIdx, response, xhr) {\n            var chunkData = handler._getChunkData(id, chunkIdx);\n\n            handler._getFileState(id).attemptingResume = false;\n\n            delete handler._getFileState(id).temp.chunkProgress[chunkIdx];\n            handler._getFileState(id).loaded += chunkData.size;\n\n            options.onUploadChunkSuccess(id, handler._getChunkDataForCallback(chunkData), response, xhr);\n        },\n\n        // Called when all chunks have been successfully uploaded and we want to ask the handler to perform any\n        // logic associated with closing out the file, such as combining the chunks.\n        finalize: function(id) {\n            var size = options.getSize(id),\n                name = options.getName(id);\n\n            log(\"All chunks have been uploaded for \" + id + \" - finalizing....\");\n            handler.finalizeChunks(id).then(\n                function(response, xhr) {\n                    log(\"Finalize successful for \" + id);\n\n                    var normaizedResponse = upload.normalizeResponse(response, true);\n\n                    options.onProgress(id, name, size, size);\n                    handler._maybeDeletePersistedChunkData(id);\n                    upload.cleanup(id, normaizedResponse, xhr);\n                },\n                function(response, xhr) {\n                    var normaizedResponse = upload.normalizeResponse(response, false);\n\n                    log(\"Problem finalizing chunks for file ID \" + id + \" - \" + normaizedResponse.error, \"error\");\n\n                    if (normaizedResponse.reset) {\n                        chunked.reset(id);\n                    }\n\n                    if (!options.onAutoRetry(id, name, normaizedResponse, xhr)) {\n                        upload.cleanup(id, normaizedResponse, xhr);\n                    }\n                }\n            );\n        },\n\n        handleFailure: function(chunkIdx, id, response, xhr) {\n            var name = options.getName(id);\n\n            log(\"Chunked upload request failed for \" + id + \", chunk \" + chunkIdx);\n\n            handler.clearCachedChunk(id, chunkIdx);\n\n            var responseToReport = upload.normalizeResponse(response, false),\n                inProgressIdx;\n\n            if (responseToReport.reset) {\n                chunked.reset(id);\n            }\n            else {\n                inProgressIdx = qq.indexOf(handler._getFileState(id).chunking.inProgress, chunkIdx);\n                if (inProgressIdx >= 0) {\n                    handler._getFileState(id).chunking.inProgress.splice(inProgressIdx, 1);\n                    handler._getFileState(id).chunking.remaining.unshift(chunkIdx);\n                }\n            }\n\n            // We may have aborted all other in-progress chunks for this file due to a failure.\n            // If so, ignore the failures associated with those aborts.\n            if (!handler._getFileState(id).temp.ignoreFailure) {\n                // If this chunk has failed, we want to ignore all other failures of currently in-progress\n                // chunks since they will be explicitly aborted\n                if (concurrentChunkingPossible) {\n                    handler._getFileState(id).temp.ignoreFailure = true;\n\n                    log(qq.format(\"Going to attempt to abort these chunks: {}. These are currently in-progress: {}.\", JSON.stringify(Object.keys(handler._getXhrs(id))), JSON.stringify(handler._getFileState(id).chunking.inProgress)));\n                    qq.each(handler._getXhrs(id), function(ckid, ckXhr) {\n                        log(qq.format(\"Attempting to abort file {}.{}. XHR readyState {}. \", id, ckid, ckXhr.readyState));\n                        ckXhr.abort();\n                        // Flag the transport, in case we are waiting for some other async operation\n                        // to complete before attempting to upload the chunk\n                        ckXhr._cancelled = true;\n                    });\n\n                    // We must indicate that all aborted chunks are no longer in progress\n                    handler.moveInProgressToRemaining(id);\n\n                    // Free up any connections used by these chunks, but don't allow any\n                    // other files to take up the connections (until we have exhausted all auto-retries)\n                    connectionManager.free(id, true);\n                }\n\n                if (!options.onAutoRetry(id, name, responseToReport, xhr)) {\n                    // If one chunk fails, abort all of the others to avoid odd race conditions that occur\n                    // if a chunk succeeds immediately after one fails before we have determined if the upload\n                    // is a failure or not.\n                    upload.cleanup(id, responseToReport, xhr);\n                }\n            }\n        },\n\n        hasMoreParts: function(id) {\n            return !!handler._getFileState(id).chunking.remaining.length;\n        },\n\n        nextPart: function(id) {\n            var nextIdx = handler._getFileState(id).chunking.remaining.shift();\n\n            if (nextIdx >= handler._getTotalChunks(id)) {\n                nextIdx = null;\n            }\n\n            return nextIdx;\n        },\n\n        reset: function(id) {\n            log(\"Server or callback has ordered chunking effort to be restarted on next attempt for item ID \" + id, \"error\");\n\n            handler._maybeDeletePersistedChunkData(id);\n            handler.reevaluateChunking(id);\n            handler._getFileState(id).loaded = 0;\n        },\n\n        sendNext: function(id) {\n            var size = options.getSize(id),\n                name = options.getName(id),\n                chunkIdx = chunked.nextPart(id),\n                chunkData = handler._getChunkData(id, chunkIdx),\n                resuming = handler._getFileState(id).attemptingResume,\n                inProgressChunks = handler._getFileState(id).chunking.inProgress || [];\n\n            if (handler._getFileState(id).loaded == null) {\n                handler._getFileState(id).loaded = 0;\n            }\n\n            // Don't follow-through with the resume attempt if the integrator returns false from onResume\n            if (resuming && options.onResume(id, name, chunkData) === false) {\n                chunked.reset(id);\n                chunkIdx = chunked.nextPart(id);\n                chunkData = handler._getChunkData(id, chunkIdx);\n                resuming = false;\n            }\n\n            // If all chunks have already uploaded successfully, we must be re-attempting the finalize step.\n            if (chunkIdx == null && inProgressChunks.length === 0) {\n                chunked.finalize(id);\n            }\n\n            // Send the next chunk\n            else {\n                log(qq.format(\"Sending chunked upload request for item {}.{}, bytes {}-{} of {}.\", id, chunkIdx, chunkData.start + 1, chunkData.end, size));\n                options.onUploadChunk(id, name, handler._getChunkDataForCallback(chunkData));\n                inProgressChunks.push(chunkIdx);\n                handler._getFileState(id).chunking.inProgress = inProgressChunks;\n\n                if (concurrentChunkingPossible) {\n                    connectionManager.open(id, chunkIdx);\n                }\n\n                if (concurrentChunkingPossible && connectionManager.available() && handler._getFileState(id).chunking.remaining.length) {\n                    chunked.sendNext(id);\n                }\n\n                if (chunkData.blob.size === 0) {\n                    log(qq.format(\"Chunk {} for file {} will not be uploaded, zero sized chunk.\", chunkIdx, id), \"error\");\n                    chunked.handleFailure(chunkIdx, id, \"File is no longer available\", null);\n                }\n                else {\n                    handler.uploadChunk(id, chunkIdx, resuming).then(\n                        // upload chunk success\n                        function success(response, xhr) {\n                            log(\"Chunked upload request succeeded for \" + id + \", chunk \" + chunkIdx);\n\n                            handler.clearCachedChunk(id, chunkIdx);\n\n                            var inProgressChunks = handler._getFileState(id).chunking.inProgress || [],\n                                responseToReport = upload.normalizeResponse(response, true),\n                                inProgressChunkIdx = qq.indexOf(inProgressChunks, chunkIdx);\n\n                            log(qq.format(\"Chunk {} for file {} uploaded successfully.\", chunkIdx, id));\n\n                            chunked.done(id, chunkIdx, responseToReport, xhr);\n\n                            if (inProgressChunkIdx >= 0) {\n                                inProgressChunks.splice(inProgressChunkIdx, 1);\n                            }\n\n                            handler._maybePersistChunkedState(id);\n\n                            if (!chunked.hasMoreParts(id) && inProgressChunks.length === 0) {\n                                chunked.finalize(id);\n                            }\n                            else if (chunked.hasMoreParts(id)) {\n                                chunked.sendNext(id);\n                            }\n                            else {\n                                log(qq.format(\"File ID {} has no more chunks to send and these chunk indexes are still marked as in-progress: {}\", id, JSON.stringify(inProgressChunks)));\n                            }\n                        },\n\n                        // upload chunk failure\n                        function failure(response, xhr) {\n                            chunked.handleFailure(chunkIdx, id, response, xhr);\n                        }\n                        )\n                        .done(function() {\n                            handler.clearXhr(id, chunkIdx);\n                        });\n                }\n            }\n        }\n    },\n\n    connectionManager = {\n        _open: [],\n        _openChunks: {},\n        _waiting: [],\n\n        available: function() {\n            var max = options.maxConnections,\n                openChunkEntriesCount = 0,\n                openChunksCount = 0;\n\n            qq.each(connectionManager._openChunks, function(fileId, openChunkIndexes) {\n                openChunkEntriesCount++;\n                openChunksCount += openChunkIndexes.length;\n            });\n\n            return max - (connectionManager._open.length - openChunkEntriesCount + openChunksCount);\n        },\n\n        /**\n         * Removes element from queue, starts upload of next\n         */\n        free: function(id, dontAllowNext) {\n            var allowNext = !dontAllowNext,\n                waitingIndex = qq.indexOf(connectionManager._waiting, id),\n                connectionsIndex = qq.indexOf(connectionManager._open, id),\n                nextId;\n\n            delete connectionManager._openChunks[id];\n\n            if (upload.getProxyOrBlob(id) instanceof qq.BlobProxy) {\n                log(\"Generated blob upload has ended for \" + id + \", disposing generated blob.\");\n                delete handler._getFileState(id).file;\n            }\n\n            // If this file was not consuming a connection, it was just waiting, so remove it from the waiting array\n            if (waitingIndex >= 0) {\n                connectionManager._waiting.splice(waitingIndex, 1);\n            }\n            // If this file was consuming a connection, allow the next file to be uploaded\n            else if (allowNext && connectionsIndex >= 0) {\n                connectionManager._open.splice(connectionsIndex, 1);\n\n                nextId = connectionManager._waiting.shift();\n                if (nextId >= 0) {\n                    connectionManager._open.push(nextId);\n                    upload.start(nextId);\n                }\n            }\n        },\n\n        getWaitingOrConnected: function() {\n            var waitingOrConnected = [];\n\n            // Chunked files may have multiple connections open per chunk (if concurrent chunking is enabled)\n            // We need to grab the file ID of any file that has at least one chunk consuming a connection.\n            qq.each(connectionManager._openChunks, function(fileId, chunks) {\n                if (chunks && chunks.length) {\n                    waitingOrConnected.push(parseInt(fileId));\n                }\n            });\n\n            // For non-chunked files, only one connection will be consumed per file.\n            // This is where we aggregate those file IDs.\n            qq.each(connectionManager._open, function(idx, fileId) {\n                if (!connectionManager._openChunks[fileId]) {\n                    waitingOrConnected.push(parseInt(fileId));\n                }\n            });\n\n            // There may be files waiting for a connection.\n            waitingOrConnected = waitingOrConnected.concat(connectionManager._waiting);\n\n            return waitingOrConnected;\n        },\n\n        isUsingConnection: function(id) {\n            return qq.indexOf(connectionManager._open, id) >= 0;\n        },\n\n        open: function(id, chunkIdx) {\n            if (chunkIdx == null) {\n                connectionManager._waiting.push(id);\n            }\n\n            if (connectionManager.available()) {\n                if (chunkIdx == null) {\n                    connectionManager._waiting.pop();\n                    connectionManager._open.push(id);\n                }\n                else {\n                    (function() {\n                        var openChunksEntry = connectionManager._openChunks[id] || [];\n                        openChunksEntry.push(chunkIdx);\n                        connectionManager._openChunks[id] = openChunksEntry;\n                    }());\n                }\n\n                return true;\n            }\n\n            return false;\n        },\n\n        reset: function() {\n            connectionManager._waiting = [];\n            connectionManager._open = [];\n        }\n    },\n\n    simple = {\n        send: function(id, name) {\n            handler._getFileState(id).loaded = 0;\n\n            log(\"Sending simple upload request for \" + id);\n            handler.uploadFile(id).then(\n                function(response, optXhr) {\n                    log(\"Simple upload request succeeded for \" + id);\n\n                    var responseToReport = upload.normalizeResponse(response, true),\n                        size = options.getSize(id);\n\n                    options.onProgress(id, name, size, size);\n                    upload.maybeNewUuid(id, responseToReport);\n                    upload.cleanup(id, responseToReport, optXhr);\n                },\n\n                function(response, optXhr) {\n                    log(\"Simple upload request failed for \" + id);\n\n                    var responseToReport = upload.normalizeResponse(response, false);\n\n                    if (!options.onAutoRetry(id, name, responseToReport, optXhr)) {\n                        upload.cleanup(id, responseToReport, optXhr);\n                    }\n                }\n            );\n        }\n    },\n\n    upload = {\n        cancel: function(id) {\n            log(\"Cancelling \" + id);\n            options.paramsStore.remove(id);\n            connectionManager.free(id);\n        },\n\n        cleanup: function(id, response, optXhr) {\n            var name = options.getName(id);\n\n            options.onComplete(id, name, response, optXhr);\n\n            if (handler._getFileState(id)) {\n                handler._clearXhrs && handler._clearXhrs(id);\n            }\n\n            connectionManager.free(id);\n        },\n\n        // Returns a qq.BlobProxy, or an actual File/Blob if no proxy is involved, or undefined\n        // if none of these are available for the ID\n        getProxyOrBlob: function(id) {\n            return (handler.getProxy && handler.getProxy(id)) ||\n                (handler.getFile && handler.getFile(id));\n        },\n\n        initHandler: function() {\n            var handlerType = namespace ? qq[namespace] : qq.traditional,\n                handlerModuleSubtype = qq.supportedFeatures.ajaxUploading ? \"Xhr\" : \"Form\";\n\n            handler = new handlerType[handlerModuleSubtype + \"UploadHandler\"](\n                options,\n                {\n                    getDataByUuid: options.getDataByUuid,\n                    getName: options.getName,\n                    getSize: options.getSize,\n                    getUuid: options.getUuid,\n                    log: log,\n                    onCancel: options.onCancel,\n                    onProgress: options.onProgress,\n                    onUuidChanged: options.onUuidChanged\n                }\n            );\n\n            if (handler._removeExpiredChunkingRecords) {\n                handler._removeExpiredChunkingRecords();\n            }\n        },\n\n        isDeferredEligibleForUpload: function(id) {\n            return options.isQueued(id);\n        },\n\n        // For Blobs that are part of a group of generated images, along with a reference image,\n        // this will ensure the blobs in the group are uploaded in the order they were triggered,\n        // even if some async processing must be completed on one or more Blobs first.\n        maybeDefer: function(id, blob) {\n            // If we don't have a file/blob yet & no file/blob exists for this item, request it,\n            // and then submit the upload to the specific handler once the blob is available.\n            // ASSUMPTION: This condition will only ever be true if XHR uploading is supported.\n            if (blob && !handler.getFile(id) && blob instanceof qq.BlobProxy) {\n\n                // Blob creation may take some time, so the caller may want to update the\n                // UI to indicate that an operation is in progress, even before the actual\n                // upload begins and an onUpload callback is invoked.\n                options.onUploadPrep(id);\n\n                log(\"Attempting to generate a blob on-demand for \" + id);\n                blob.create().then(function(generatedBlob) {\n                    log(\"Generated an on-demand blob for \" + id);\n\n                    // Update record associated with this file by providing the generated Blob\n                    handler.updateBlob(id, generatedBlob);\n\n                    // Propagate the size for this generated Blob\n                    options.setSize(id, generatedBlob.size);\n\n                    // Order handler to recalculate chunking possibility, if applicable\n                    handler.reevaluateChunking(id);\n\n                    upload.maybeSendDeferredFiles(id);\n                },\n\n                // Blob could not be generated.  Fail the upload & attempt to prevent retries.  Also bubble error message.\n                function(errorMessage) {\n                    var errorResponse = {};\n\n                    if (errorMessage) {\n                        errorResponse.error = errorMessage;\n                    }\n\n                    log(qq.format(\"Failed to generate blob for ID {}.  Error message: {}.\", id, errorMessage), \"error\");\n\n                    options.onComplete(id, options.getName(id), qq.extend(errorResponse, preventRetryResponse), null);\n                    upload.maybeSendDeferredFiles(id);\n                    connectionManager.free(id);\n                });\n            }\n            else {\n                return upload.maybeSendDeferredFiles(id);\n            }\n\n            return false;\n        },\n\n        // Upload any grouped blobs, in the proper order, that are ready to be uploaded\n        maybeSendDeferredFiles: function(id) {\n            var idsInGroup = options.getIdsInProxyGroup(id),\n                uploadedThisId = false;\n\n            if (idsInGroup && idsInGroup.length) {\n                log(\"Maybe ready to upload proxy group file \" + id);\n\n                qq.each(idsInGroup, function(idx, idInGroup) {\n                    if (upload.isDeferredEligibleForUpload(idInGroup) && !!handler.getFile(idInGroup)) {\n                        uploadedThisId = idInGroup === id;\n                        upload.now(idInGroup);\n                    }\n                    else if (upload.isDeferredEligibleForUpload(idInGroup)) {\n                        return false;\n                    }\n                });\n            }\n            else {\n                uploadedThisId = true;\n                upload.now(id);\n            }\n\n            return uploadedThisId;\n        },\n\n        maybeNewUuid: function(id, response) {\n            if (response.newUuid !== undefined) {\n                options.onUuidChanged(id, response.newUuid);\n            }\n        },\n\n        // The response coming from handler implementations may be in various formats.\n        // Instead of hoping a promise nested 5 levels deep will always return an object\n        // as its first param, let's just normalize the response here.\n        normalizeResponse: function(originalResponse, successful) {\n            var response = originalResponse;\n\n            // The passed \"response\" param may not be a response at all.\n            // It could be a string, detailing the error, for example.\n            if (!qq.isObject(originalResponse)) {\n                response = {};\n\n                if (qq.isString(originalResponse) && !successful) {\n                    response.error = originalResponse;\n                }\n            }\n\n            response.success = successful;\n\n            return response;\n        },\n\n        now: function(id) {\n            var name = options.getName(id);\n\n            if (!controller.isValid(id)) {\n                throw new qq.Error(id + \" is not a valid file ID to upload!\");\n            }\n\n            options.onUpload(id, name);\n\n            if (chunkingPossible && handler._shouldChunkThisFile(id)) {\n                chunked.sendNext(id);\n            }\n            else {\n                simple.send(id, name);\n            }\n        },\n\n        start: function(id) {\n            var blobToUpload = upload.getProxyOrBlob(id);\n\n            if (blobToUpload) {\n                return upload.maybeDefer(id, blobToUpload);\n            }\n            else {\n                upload.now(id);\n                return true;\n            }\n        }\n    };\n\n    qq.extend(this, {\n        /**\n         * Adds file or file input to the queue\n         **/\n        add: function(id, file) {\n            handler.add.apply(this, arguments);\n        },\n\n        /**\n         * Sends the file identified by id\n         */\n        upload: function(id) {\n            if (connectionManager.open(id)) {\n                return upload.start(id);\n            }\n            return false;\n        },\n\n        retry: function(id) {\n            // On retry, if concurrent chunking has been enabled, we may have aborted all other in-progress chunks\n            // for a file when encountering a failed chunk upload.  We then signaled the controller to ignore\n            // all failures associated with these aborts.  We are now retrying, so we don't want to ignore\n            // any more failures at this point.\n            if (concurrentChunkingPossible) {\n                handler._getFileState(id).temp.ignoreFailure = false;\n            }\n\n            // If we are attempting to retry a file that is already consuming a connection, this is likely an auto-retry.\n            // Just go ahead and ask the handler to upload again.\n            if (connectionManager.isUsingConnection(id)) {\n                return upload.start(id);\n            }\n\n            // If we are attempting to retry a file that is not currently consuming a connection,\n            // this is likely a manual retry attempt.  We will need to ensure a connection is available\n            // before the retry commences.\n            else {\n                return controller.upload(id);\n            }\n        },\n\n        /**\n         * Cancels file upload by id\n         */\n        cancel: function(id) {\n            var cancelRetVal = handler.cancel(id);\n\n            if (qq.isGenericPromise(cancelRetVal)) {\n                cancelRetVal.then(function() {\n                    upload.cancel(id);\n                });\n            }\n            else if (cancelRetVal !== false) {\n                upload.cancel(id);\n            }\n        },\n\n        /**\n         * Cancels all queued or in-progress uploads\n         */\n        cancelAll: function() {\n            var waitingOrConnected = connectionManager.getWaitingOrConnected(),\n                i;\n\n            // ensure files are cancelled in reverse order which they were added\n            // to avoid a flash of time where a queued file begins to upload before it is canceled\n            if (waitingOrConnected.length) {\n                for (i = waitingOrConnected.length - 1; i >= 0; i--) {\n                    controller.cancel(waitingOrConnected[i]);\n                }\n            }\n\n            connectionManager.reset();\n        },\n\n        // Returns a File, Blob, or the Blob/File for the reference/parent file if the targeted blob is a proxy.\n        // Undefined if no file record is available.\n        getFile: function(id) {\n            if (handler.getProxy && handler.getProxy(id)) {\n                return handler.getProxy(id).referenceBlob;\n            }\n\n            return handler.getFile && handler.getFile(id);\n        },\n\n        // Returns true if the Blob associated with the ID is related to a proxy s\n        isProxied: function(id) {\n            return !!(handler.getProxy && handler.getProxy(id));\n        },\n\n        getInput: function(id) {\n            if (handler.getInput) {\n                return handler.getInput(id);\n            }\n        },\n\n        reset: function() {\n            log(\"Resetting upload handler\");\n            controller.cancelAll();\n            connectionManager.reset();\n            handler.reset();\n        },\n\n        expunge: function(id) {\n            if (controller.isValid(id)) {\n                return handler.expunge(id);\n            }\n        },\n\n        /**\n         * Determine if the file exists.\n         */\n        isValid: function(id) {\n            return handler.isValid(id);\n        },\n\n        getResumableFilesData: function() {\n            if (handler.getResumableFilesData) {\n                return handler.getResumableFilesData();\n            }\n            return [];\n        },\n\n        /**\n         * This may or may not be implemented, depending on the handler.  For handlers where a third-party ID is\n         * available (such as the \"key\" for Amazon S3), this will return that value.  Otherwise, the return value\n         * will be undefined.\n         *\n         * @param id Internal file ID\n         * @returns {*} Some identifier used by a 3rd-party service involved in the upload process\n         */\n        getThirdPartyFileId: function(id) {\n            if (controller.isValid(id)) {\n                return handler.getThirdPartyFileId(id);\n            }\n        },\n\n        /**\n         * Attempts to pause the associated upload if the specific handler supports this and the file is \"valid\".\n         * @param id ID of the upload/file to pause\n         * @returns {boolean} true if the upload was paused\n         */\n        pause: function(id) {\n            if (controller.isResumable(id) && handler.pause && controller.isValid(id) && handler.pause(id)) {\n                connectionManager.free(id);\n                handler.moveInProgressToRemaining(id);\n                return true;\n            }\n            return false;\n        },\n\n        // True if the file is eligible for pause/resume.\n        isResumable: function(id) {\n            return !!handler.isResumable && handler.isResumable(id);\n        }\n    });\n\n    qq.extend(options, o);\n    log = options.log;\n    chunkingPossible = options.chunking.enabled && qq.supportedFeatures.chunking;\n    concurrentChunkingPossible = chunkingPossible && options.chunking.concurrent.enabled;\n\n    preventRetryResponse = (function() {\n        var response = {};\n\n        response[options.preventRetryParam] = true;\n\n        return response;\n    }());\n\n    upload.initHandler();\n};\n","/*globals qq */\n/*jshint -W117 */\nqq.WindowReceiveMessage = function(o) {\n    \"use strict\";\n\n    var options = {\n            log: function(message, level) {}\n        },\n        callbackWrapperDetachers = {};\n\n    qq.extend(options, o);\n\n    qq.extend(this, {\n        receiveMessage: function(id, callback) {\n            var onMessageCallbackWrapper = function(event) {\n                    callback(event.data);\n                };\n\n            if (window.postMessage) {\n                callbackWrapperDetachers[id] = qq(window).attach(\"message\", onMessageCallbackWrapper);\n            }\n            else {\n                log(\"iframe message passing not supported in this browser!\", \"error\");\n            }\n        },\n\n        stopReceivingMessages: function(id) {\n            if (window.postMessage) {\n                var detacher = callbackWrapperDetachers[id];\n                if (detacher) {\n                    detacher();\n                }\n            }\n        }\n    });\n};\n","/* globals qq */\n/**\n * Common APIs exposed to creators of upload via form/iframe handlers.  This is reused and possibly overridden\n * in some cases by specific form upload handlers.\n *\n * @constructor\n */\nqq.FormUploadHandler = function(spec) {\n    \"use strict\";\n\n    var options = spec.options,\n        handler = this,\n        proxy = spec.proxy,\n        formHandlerInstanceId = qq.getUniqueId(),\n        onloadCallbacks = {},\n        detachLoadEvents = {},\n        postMessageCallbackTimers = {},\n        isCors = options.isCors,\n        inputName = options.inputName,\n        getUuid = proxy.getUuid,\n        log = proxy.log,\n        corsMessageReceiver = new qq.WindowReceiveMessage({log: log});\n\n    /**\n     * Remove any trace of the file from the handler.\n     *\n     * @param id ID of the associated file\n     */\n    function expungeFile(id) {\n        delete detachLoadEvents[id];\n\n        // If we are dealing with CORS, we might still be waiting for a response from a loaded iframe.\n        // In that case, terminate the timer waiting for a message from the loaded iframe\n        // and stop listening for any more messages coming from this iframe.\n        if (isCors) {\n            clearTimeout(postMessageCallbackTimers[id]);\n            delete postMessageCallbackTimers[id];\n            corsMessageReceiver.stopReceivingMessages(id);\n        }\n\n        var iframe = document.getElementById(handler._getIframeName(id));\n        if (iframe) {\n            // To cancel request set src to something else.  We use src=\"javascript:false;\"\n            // because it doesn't trigger ie6 prompt on https\n            /* jshint scripturl:true */\n            iframe.setAttribute(\"src\", \"javascript:false;\");\n\n            qq(iframe).remove();\n        }\n    }\n\n    /**\n     * @param iframeName `document`-unique Name of the associated iframe\n     * @returns {*} ID of the associated file\n     */\n    function getFileIdForIframeName(iframeName) {\n        return iframeName.split(\"_\")[0];\n    }\n\n    /**\n     * Generates an iframe to be used as a target for upload-related form submits.  This also adds the iframe\n     * to the current `document`.  Note that the iframe is hidden from view.\n     *\n     * @param name Name of the iframe.\n     * @returns {HTMLIFrameElement} The created iframe\n     */\n    function initIframeForUpload(name) {\n        var iframe = qq.toElement(\"<iframe src='javascript:false;' name='\" + name + \"' />\");\n\n        iframe.setAttribute(\"id\", name);\n\n        iframe.style.display = \"none\";\n        document.body.appendChild(iframe);\n\n        return iframe;\n    }\n\n    /**\n     * If we are in CORS mode, we must listen for messages (containing the server response) from the associated\n     * iframe, since we cannot directly parse the content of the iframe due to cross-origin restrictions.\n     *\n     * @param iframe Listen for messages on this iframe.\n     * @param callback Invoke this callback with the message from the iframe.\n     */\n    function registerPostMessageCallback(iframe, callback) {\n        var iframeName = iframe.id,\n            fileId = getFileIdForIframeName(iframeName),\n            uuid = getUuid(fileId);\n\n        onloadCallbacks[uuid] = callback;\n\n        // When the iframe has loaded (after the server responds to an upload request)\n        // declare the attempt a failure if we don't receive a valid message shortly after the response comes in.\n        detachLoadEvents[fileId] = qq(iframe).attach(\"load\", function() {\n            if (handler.getInput(fileId)) {\n                log(\"Received iframe load event for CORS upload request (iframe name \" + iframeName + \")\");\n\n                postMessageCallbackTimers[iframeName] = setTimeout(function() {\n                    var errorMessage = \"No valid message received from loaded iframe for iframe name \" + iframeName;\n                    log(errorMessage, \"error\");\n                    callback({\n                        error: errorMessage\n                    });\n                }, 1000);\n            }\n        });\n\n        // Listen for messages coming from this iframe.  When a message has been received, cancel the timer\n        // that declares the upload a failure if a message is not received within a reasonable amount of time.\n        corsMessageReceiver.receiveMessage(iframeName, function(message) {\n            log(\"Received the following window message: '\" + message + \"'\");\n            var fileId = getFileIdForIframeName(iframeName),\n                response = handler._parseJsonResponse(message),\n                uuid = response.uuid,\n                onloadCallback;\n\n            if (uuid && onloadCallbacks[uuid]) {\n                log(\"Handling response for iframe name \" + iframeName);\n                clearTimeout(postMessageCallbackTimers[iframeName]);\n                delete postMessageCallbackTimers[iframeName];\n\n                handler._detachLoadEvent(iframeName);\n\n                onloadCallback = onloadCallbacks[uuid];\n\n                delete onloadCallbacks[uuid];\n                corsMessageReceiver.stopReceivingMessages(iframeName);\n                onloadCallback(response);\n            }\n            else if (!uuid) {\n                log(\"'\" + message + \"' does not contain a UUID - ignoring.\");\n            }\n        });\n    }\n\n    qq.extend(this, new qq.UploadHandler(spec));\n\n    qq.override(this, function(super_) {\n        return {\n            /**\n             * Adds File or Blob to the queue\n             **/\n            add: function(id, fileInput) {\n                super_.add(id, {input: fileInput});\n\n                fileInput.setAttribute(\"name\", inputName);\n\n                // remove file input from DOM\n                if (fileInput.parentNode) {\n                    qq(fileInput).remove();\n                }\n            },\n\n            expunge: function(id) {\n                expungeFile(id);\n                super_.expunge(id);\n            },\n\n            isValid: function(id) {\n                return super_.isValid(id) &&\n                    handler._getFileState(id).input !== undefined;\n            }\n        };\n    });\n\n    qq.extend(this, {\n        getInput: function(id) {\n            return handler._getFileState(id).input;\n        },\n\n        /**\n         * This function either delegates to a more specific message handler if CORS is involved,\n         * or simply registers a callback when the iframe has been loaded that invokes the passed callback\n         * after determining if the content of the iframe is accessible.\n         *\n         * @param iframe Associated iframe\n         * @param callback Callback to invoke after we have determined if the iframe content is accessible.\n         */\n        _attachLoadEvent: function(iframe, callback) {\n            /*jslint eqeq: true*/\n            var responseDescriptor;\n\n            if (isCors) {\n                registerPostMessageCallback(iframe, callback);\n            }\n            else {\n                detachLoadEvents[iframe.id] = qq(iframe).attach(\"load\", function() {\n                    log(\"Received response for \" + iframe.id);\n\n                    // when we remove iframe from dom\n                    // the request stops, but in IE load\n                    // event fires\n                    if (!iframe.parentNode) {\n                        return;\n                    }\n\n                    try {\n                        // fixing Opera 10.53\n                        if (iframe.contentDocument &&\n                            iframe.contentDocument.body &&\n                            iframe.contentDocument.body.innerHTML == \"false\") {\n                            // In Opera event is fired second time\n                            // when body.innerHTML changed from false\n                            // to server response approx. after 1 sec\n                            // when we upload file with iframe\n                            return;\n                        }\n                    }\n                    catch (error) {\n                        //IE may throw an \"access is denied\" error when attempting to access contentDocument on the iframe in some cases\n                        log(\"Error when attempting to access iframe during handling of upload response (\" + error.message + \")\", \"error\");\n                        responseDescriptor = {success: false};\n                    }\n\n                    callback(responseDescriptor);\n                });\n            }\n        },\n\n        /**\n         * Creates an iframe with a specific document-unique name.\n         *\n         * @param id ID of the associated file\n         * @returns {HTMLIFrameElement}\n         */\n        _createIframe: function(id) {\n            var iframeName = handler._getIframeName(id);\n\n            return initIframeForUpload(iframeName);\n        },\n\n        /**\n         * Called when we are no longer interested in being notified when an iframe has loaded.\n         *\n         * @param id Associated file ID\n         */\n        _detachLoadEvent: function(id) {\n            if (detachLoadEvents[id] !== undefined) {\n                detachLoadEvents[id]();\n                delete detachLoadEvents[id];\n            }\n        },\n\n        /**\n         * @param fileId ID of the associated file\n         * @returns {string} The `document`-unique name of the iframe\n         */\n        _getIframeName: function(fileId) {\n            return fileId + \"_\" + formHandlerInstanceId;\n        },\n\n        /**\n         * Generates a form element and appends it to the `document`.  When the form is submitted, a specific iframe is targeted.\n         * The name of the iframe is passed in as a property of the spec parameter, and must be unique in the `document`.  Note\n         * that the form is hidden from view.\n         *\n         * @param spec An object containing various properties to be used when constructing the form.  Required properties are\n         * currently: `method`, `endpoint`, `params`, `paramsInBody`, and `targetName`.\n         * @returns {HTMLFormElement} The created form\n         */\n        _initFormForUpload: function(spec) {\n            var method = spec.method,\n                endpoint = spec.endpoint,\n                params = spec.params,\n                paramsInBody = spec.paramsInBody,\n                targetName = spec.targetName,\n                form = qq.toElement(\"<form method='\" + method + \"' enctype='multipart/form-data'></form>\"),\n                url = endpoint;\n\n            if (paramsInBody) {\n                qq.obj2Inputs(params, form);\n            }\n            else {\n                url = qq.obj2url(params, endpoint);\n            }\n\n            form.setAttribute(\"action\", url);\n            form.setAttribute(\"target\", targetName);\n            form.style.display = \"none\";\n            document.body.appendChild(form);\n\n            return form;\n        },\n\n        /**\n         * @param innerHtmlOrMessage JSON message\n         * @returns {*} The parsed response, or an empty object if the response could not be parsed\n         */\n        _parseJsonResponse: function(innerHtmlOrMessage) {\n            var response = {};\n\n            try {\n                response = qq.parseJson(innerHtmlOrMessage);\n            }\n            catch (error) {\n                log(\"Error when attempting to parse iframe upload response (\" + error.message + \")\", \"error\");\n            }\n\n            return response;\n        }\n    });\n};\n","/* globals qq */\n/**\n * Common API exposed to creators of XHR handlers.  This is reused and possibly overriding in some cases by specific\n * XHR upload handlers.\n *\n * @constructor\n */\nqq.XhrUploadHandler = function(spec) {\n    \"use strict\";\n\n    var handler = this,\n        namespace = spec.options.namespace,\n        proxy = spec.proxy,\n        chunking = spec.options.chunking,\n        resume = spec.options.resume,\n        chunkFiles = chunking && spec.options.chunking.enabled && qq.supportedFeatures.chunking,\n        resumeEnabled = resume && spec.options.resume.enabled && chunkFiles && qq.supportedFeatures.resume,\n        getName = proxy.getName,\n        getSize = proxy.getSize,\n        getUuid = proxy.getUuid,\n        getEndpoint = proxy.getEndpoint,\n        getDataByUuid = proxy.getDataByUuid,\n        onUuidChanged = proxy.onUuidChanged,\n        onProgress = proxy.onProgress,\n        log = proxy.log;\n\n    function abort(id) {\n        qq.each(handler._getXhrs(id), function(xhrId, xhr) {\n            var ajaxRequester = handler._getAjaxRequester(id, xhrId);\n\n            xhr.onreadystatechange = null;\n            xhr.upload.onprogress = null;\n            xhr.abort();\n            ajaxRequester && ajaxRequester.canceled && ajaxRequester.canceled(id);\n        });\n    }\n\n    qq.extend(this, new qq.UploadHandler(spec));\n\n    qq.override(this, function(super_) {\n        return {\n            /**\n             * Adds File or Blob to the queue\n             **/\n            add: function(id, blobOrProxy) {\n                if (qq.isFile(blobOrProxy) || qq.isBlob(blobOrProxy)) {\n                    super_.add(id, {file: blobOrProxy});\n                }\n                else if (blobOrProxy instanceof qq.BlobProxy) {\n                    super_.add(id, {proxy: blobOrProxy});\n                }\n                else {\n                    throw new Error(\"Passed obj is not a File, Blob, or proxy\");\n                }\n\n                handler._initTempState(id);\n                resumeEnabled && handler._maybePrepareForResume(id);\n            },\n\n            expunge: function(id) {\n                abort(id);\n                handler._maybeDeletePersistedChunkData(id);\n                handler._clearXhrs(id);\n                super_.expunge(id);\n            }\n        };\n    });\n\n    qq.extend(this, {\n        // Clear the cached chunk `Blob` after we are done with it, just in case the `Blob` bytes are stored in memory.\n        clearCachedChunk: function(id, chunkIdx) {\n            delete handler._getFileState(id).temp.cachedChunks[chunkIdx];\n        },\n\n        clearXhr: function(id, chunkIdx) {\n            var tempState = handler._getFileState(id).temp;\n\n            if (tempState.xhrs) {\n                delete tempState.xhrs[chunkIdx];\n            }\n            if (tempState.ajaxRequesters) {\n                delete tempState.ajaxRequesters[chunkIdx];\n            }\n        },\n\n        // Called when all chunks have been successfully uploaded.  Expected promissory return type.\n        // This defines the default behavior if nothing further is required when all chunks have been uploaded.\n        finalizeChunks: function(id, responseParser) {\n            var lastChunkIdx = handler._getTotalChunks(id) - 1,\n                xhr = handler._getXhr(id, lastChunkIdx);\n\n            if (responseParser) {\n                return new qq.Promise().success(responseParser(xhr), xhr);\n            }\n\n            return new qq.Promise().success({}, xhr);\n        },\n\n        getFile: function(id) {\n            return handler.isValid(id) && handler._getFileState(id).file;\n        },\n\n        getProxy: function(id) {\n            return handler.isValid(id) && handler._getFileState(id).proxy;\n        },\n\n        /**\n         * @returns {Array} Array of objects containing properties useful to integrators\n         * when it is important to determine which files are potentially resumable.\n         */\n        getResumableFilesData: function() {\n            var resumableFilesData = [];\n\n            handler._iterateResumeRecords(function(key, uploadData) {\n                handler.moveInProgressToRemaining(null, uploadData.chunking.inProgress,  uploadData.chunking.remaining);\n\n                var data = {\n                    name: uploadData.name,\n                    remaining: uploadData.chunking.remaining,\n                    size: uploadData.size,\n                    uuid: uploadData.uuid\n                };\n\n                if (uploadData.key) {\n                    data.key = uploadData.key;\n                }\n\n                resumableFilesData.push(data);\n            });\n\n            return resumableFilesData;\n        },\n\n        isResumable: function(id) {\n            return !!chunking && handler.isValid(id) && !handler._getFileState(id).notResumable;\n        },\n\n        moveInProgressToRemaining: function(id, optInProgress, optRemaining) {\n            var inProgress = optInProgress || handler._getFileState(id).chunking.inProgress,\n                remaining = optRemaining || handler._getFileState(id).chunking.remaining;\n\n            if (inProgress) {\n                log(qq.format(\"Moving these chunks from in-progress {}, to remaining.\", JSON.stringify(inProgress)));\n                inProgress.reverse();\n                qq.each(inProgress, function(idx, chunkIdx) {\n                    remaining.unshift(chunkIdx);\n                });\n                inProgress.length = 0;\n            }\n        },\n\n        pause: function(id) {\n            if (handler.isValid(id)) {\n                log(qq.format(\"Aborting XHR upload for {} '{}' due to pause instruction.\", id, getName(id)));\n                handler._getFileState(id).paused = true;\n                abort(id);\n                return true;\n            }\n        },\n\n        reevaluateChunking: function(id) {\n            if (chunking && handler.isValid(id)) {\n                var state = handler._getFileState(id),\n                    totalChunks,\n                    i;\n\n                delete state.chunking;\n\n                state.chunking = {};\n                totalChunks = handler._getTotalChunks(id);\n                if (totalChunks > 1 || chunking.mandatory) {\n                    state.chunking.enabled = true;\n                    state.chunking.parts = totalChunks;\n                    state.chunking.remaining = [];\n\n                    for (i = 0; i < totalChunks; i++) {\n                        state.chunking.remaining.push(i);\n                    }\n\n                    handler._initTempState(id);\n                }\n                else {\n                    state.chunking.enabled = false;\n                }\n            }\n        },\n\n        updateBlob: function(id, newBlob) {\n            if (handler.isValid(id)) {\n                handler._getFileState(id).file = newBlob;\n            }\n        },\n\n        _clearXhrs: function(id) {\n            var tempState = handler._getFileState(id).temp;\n\n            qq.each(tempState.ajaxRequesters, function(chunkId) {\n                delete tempState.ajaxRequesters[chunkId];\n            });\n\n            qq.each(tempState.xhrs, function(chunkId) {\n                delete tempState.xhrs[chunkId];\n            });\n        },\n\n        /**\n         * Creates an XHR instance for this file and stores it in the fileState.\n         *\n         * @param id File ID\n         * @param optChunkIdx The chunk index associated with this XHR, if applicable\n         * @returns {XMLHttpRequest}\n         */\n        _createXhr: function(id, optChunkIdx) {\n            return handler._registerXhr(id, optChunkIdx, qq.createXhrInstance());\n        },\n\n        _getAjaxRequester: function(id, optChunkIdx) {\n            var chunkIdx = optChunkIdx == null ? -1 : optChunkIdx;\n            return handler._getFileState(id).temp.ajaxRequesters[chunkIdx];\n        },\n\n        _getChunkData: function(id, chunkIndex) {\n            var chunkSize = chunking.partSize,\n                fileSize = getSize(id),\n                fileOrBlob = handler.getFile(id),\n                startBytes = chunkSize * chunkIndex,\n                endBytes = startBytes + chunkSize >= fileSize ? fileSize : startBytes + chunkSize,\n                totalChunks = handler._getTotalChunks(id),\n                cachedChunks = this._getFileState(id).temp.cachedChunks,\n\n            // To work around a Webkit GC bug, we must keep each chunk `Blob` in scope until we are done with it.\n            // See https://github.com/Widen/fine-uploader/issues/937#issuecomment-41418760\n                blob = cachedChunks[chunkIndex] || qq.sliceBlob(fileOrBlob, startBytes, endBytes);\n\n            cachedChunks[chunkIndex] = blob;\n\n            return {\n                part: chunkIndex,\n                start: startBytes,\n                end: endBytes,\n                count: totalChunks,\n                blob: blob,\n                size: endBytes - startBytes\n            };\n        },\n\n        _getChunkDataForCallback: function(chunkData) {\n            return {\n                partIndex: chunkData.part,\n                startByte: chunkData.start + 1,\n                endByte: chunkData.end,\n                totalParts: chunkData.count\n            };\n        },\n\n        /**\n         * @param id File ID\n         * @returns {string} Identifier for this item that may appear in the browser's local storage\n         */\n        _getLocalStorageId: function(id) {\n            var formatVersion = \"5.0\",\n                name = getName(id),\n                size = getSize(id),\n                chunkSize = chunking.partSize,\n                endpoint = getEndpoint(id);\n\n            return qq.format(\"qq{}resume{}-{}-{}-{}-{}\", namespace, formatVersion, name, size, chunkSize, endpoint);\n        },\n\n        _getMimeType: function(id) {\n            return handler.getFile(id).type;\n        },\n\n        _getPersistableData: function(id) {\n            return handler._getFileState(id).chunking;\n        },\n\n        /**\n         * @param id ID of the associated file\n         * @returns {number} Number of parts this file can be divided into, or undefined if chunking is not supported in this UA\n         */\n        _getTotalChunks: function(id) {\n            if (chunking) {\n                var fileSize = getSize(id),\n                    chunkSize = chunking.partSize;\n\n                return Math.ceil(fileSize / chunkSize);\n            }\n        },\n\n        _getXhr: function(id, optChunkIdx) {\n            var chunkIdx = optChunkIdx == null ? -1 : optChunkIdx;\n            return handler._getFileState(id).temp.xhrs[chunkIdx];\n        },\n\n        _getXhrs: function(id) {\n            return handler._getFileState(id).temp.xhrs;\n        },\n\n        // Iterates through all XHR handler-created resume records (in local storage),\n        // invoking the passed callback and passing in the key and value of each local storage record.\n        _iterateResumeRecords: function(callback) {\n            if (resumeEnabled) {\n                qq.each(localStorage, function(key, item) {\n                    if (key.indexOf(qq.format(\"qq{}resume\", namespace)) === 0) {\n                        var uploadData = JSON.parse(item);\n                        callback(key, uploadData);\n                    }\n                });\n            }\n        },\n\n        _initTempState: function(id) {\n            handler._getFileState(id).temp = {\n                ajaxRequesters: {},\n                chunkProgress: {},\n                xhrs: {},\n                cachedChunks: {}\n            };\n        },\n\n        _markNotResumable: function(id) {\n            handler._getFileState(id).notResumable = true;\n        },\n\n        // Removes a chunked upload record from local storage, if possible.\n        // Returns true if the item was removed, false otherwise.\n        _maybeDeletePersistedChunkData: function(id) {\n            var localStorageId;\n\n            if (resumeEnabled && handler.isResumable(id)) {\n                localStorageId = handler._getLocalStorageId(id);\n\n                if (localStorageId && localStorage.getItem(localStorageId)) {\n                    localStorage.removeItem(localStorageId);\n                    return true;\n                }\n            }\n\n            return false;\n        },\n\n        // If this is a resumable upload, grab the relevant data from storage and items in memory that track this upload\n        // so we can pick up from where we left off.\n        _maybePrepareForResume: function(id) {\n            var state = handler._getFileState(id),\n                localStorageId, persistedData;\n\n            // Resume is enabled and possible and this is the first time we've tried to upload this file in this session,\n            // so prepare for a resume attempt.\n            if (resumeEnabled && state.key === undefined) {\n                localStorageId = handler._getLocalStorageId(id);\n                persistedData = localStorage.getItem(localStorageId);\n\n                // If we found this item in local storage, maybe we should resume it.\n                if (persistedData) {\n                    persistedData = JSON.parse(persistedData);\n\n                    // If we found a resume record but we have already handled this file in this session,\n                    // don't try to resume it & ensure we don't persist future check data\n                    if (getDataByUuid(persistedData.uuid)) {\n                        handler._markNotResumable(id);\n                    }\n                    else {\n                        log(qq.format(\"Identified file with ID {} and name of {} as resumable.\", id, getName(id)));\n\n                        onUuidChanged(id, persistedData.uuid);\n\n                        state.key = persistedData.key;\n                        state.chunking = persistedData.chunking;\n                        state.loaded = persistedData.loaded;\n                        state.attemptingResume = true;\n\n                        handler.moveInProgressToRemaining(id);\n                    }\n                }\n            }\n        },\n\n        // Persist any data needed to resume this upload in a new session.\n        _maybePersistChunkedState: function(id) {\n            var state = handler._getFileState(id),\n                localStorageId, persistedData;\n\n            // If local storage isn't supported by the browser, or if resume isn't enabled or possible, give up\n            if (resumeEnabled && handler.isResumable(id)) {\n                localStorageId = handler._getLocalStorageId(id);\n\n                persistedData = {\n                    name: getName(id),\n                    size: getSize(id),\n                    uuid: getUuid(id),\n                    key: state.key,\n                    chunking: state.chunking,\n                    loaded: state.loaded,\n                    lastUpdated: Date.now()\n                };\n\n                try {\n                    localStorage.setItem(localStorageId, JSON.stringify(persistedData));\n                }\n                catch (error) {\n                    log(qq.format(\"Unable to save resume data for '{}' due to error: '{}'.\", id, error.toString()), \"warn\");\n                }\n            }\n        },\n\n        _registerProgressHandler: function(id, chunkIdx, chunkSize) {\n            var xhr = handler._getXhr(id, chunkIdx),\n                name = getName(id),\n                progressCalculator = {\n                    simple: function(loaded, total) {\n                        var fileSize = getSize(id);\n\n                        if (loaded === total) {\n                            onProgress(id, name, fileSize, fileSize);\n                        }\n                        else {\n                            onProgress(id, name, (loaded >= fileSize ? fileSize - 1 : loaded), fileSize);\n                        }\n                    },\n\n                    chunked: function(loaded, total) {\n                        var chunkProgress = handler._getFileState(id).temp.chunkProgress,\n                            totalSuccessfullyLoadedForFile = handler._getFileState(id).loaded,\n                            loadedForRequest = loaded,\n                            totalForRequest = total,\n                            totalFileSize = getSize(id),\n                            estActualChunkLoaded = loadedForRequest - (totalForRequest - chunkSize),\n                            totalLoadedForFile = totalSuccessfullyLoadedForFile;\n\n                        chunkProgress[chunkIdx] = estActualChunkLoaded;\n\n                        qq.each(chunkProgress, function(chunkIdx, chunkLoaded) {\n                            totalLoadedForFile += chunkLoaded;\n                        });\n\n                        onProgress(id, name, totalLoadedForFile, totalFileSize);\n                    }\n                };\n\n            xhr.upload.onprogress = function(e) {\n                if (e.lengthComputable) {\n                    /* jshint eqnull: true */\n                    var type = chunkSize == null ? \"simple\" : \"chunked\";\n                    progressCalculator[type](e.loaded, e.total);\n                }\n            };\n        },\n\n        /**\n         * Registers an XHR transport instance created elsewhere.\n         *\n         * @param id ID of the associated file\n         * @param optChunkIdx The chunk index associated with this XHR, if applicable\n         * @param xhr XMLHttpRequest object instance\n         * @param optAjaxRequester `qq.AjaxRequester` associated with this request, if applicable.\n         * @returns {XMLHttpRequest}\n         */\n        _registerXhr: function(id, optChunkIdx, xhr, optAjaxRequester) {\n            var xhrsId = optChunkIdx == null ? -1 : optChunkIdx,\n                tempState = handler._getFileState(id).temp;\n\n            tempState.xhrs = tempState.xhrs || {};\n            tempState.ajaxRequesters = tempState.ajaxRequesters || {};\n\n            tempState.xhrs[xhrsId] = xhr;\n\n            if (optAjaxRequester) {\n                tempState.ajaxRequesters[xhrsId] = optAjaxRequester;\n            }\n\n            return xhr;\n        },\n\n        // Deletes any local storage records that are \"expired\".\n        _removeExpiredChunkingRecords: function() {\n            var expirationDays = resume.recordsExpireIn;\n\n            handler._iterateResumeRecords(function(key, uploadData) {\n                var expirationDate = new Date(uploadData.lastUpdated);\n\n                // transform updated date into expiration date\n                expirationDate.setDate(expirationDate.getDate() + expirationDays);\n\n                if (expirationDate.getTime() <= Date.now()) {\n                    log(\"Removing expired resume record with key \" + key);\n                    localStorage.removeItem(key);\n                }\n            });\n        },\n\n        /**\n         * Determine if the associated file should be chunked.\n         *\n         * @param id ID of the associated file\n         * @returns {*} true if chunking is enabled, possible, and the file can be split into more than 1 part\n         */\n        _shouldChunkThisFile: function(id) {\n            var state = handler._getFileState(id);\n\n            if (!state.chunking) {\n                handler.reevaluateChunking(id);\n            }\n\n            return state.chunking.enabled;\n        }\n    });\n};\n","/*globals qq, XMLHttpRequest*/\nqq.DeleteFileAjaxRequester = function(o) {\n    \"use strict\";\n\n    var requester,\n        options = {\n            method: \"DELETE\",\n            uuidParamName: \"qquuid\",\n            endpointStore: {},\n            maxConnections: 3,\n            customHeaders: function(id) {return {};},\n            paramsStore: {},\n            cors: {\n                expected: false,\n                sendCredentials: false\n            },\n            log: function(str, level) {},\n            onDelete: function(id) {},\n            onDeleteComplete: function(id, xhrOrXdr, isError) {}\n        };\n\n    qq.extend(options, o);\n\n    function getMandatedParams() {\n        if (options.method.toUpperCase() === \"POST\") {\n            return {\n                _method: \"DELETE\"\n            };\n        }\n\n        return {};\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        acceptHeader: \"application/json\",\n        validMethods: [\"POST\", \"DELETE\"],\n        method: options.method,\n        endpointStore: options.endpointStore,\n        paramsStore: options.paramsStore,\n        mandatedParams: getMandatedParams(),\n        maxConnections: options.maxConnections,\n        customHeaders: function(id) {\n            return options.customHeaders.get(id);\n        },\n        log: options.log,\n        onSend: options.onDelete,\n        onComplete: options.onDeleteComplete,\n        cors: options.cors\n    }));\n\n    qq.extend(this, {\n        sendDelete: function(id, uuid, additionalMandatedParams) {\n            var additionalOptions = additionalMandatedParams || {};\n\n            options.log(\"Submitting delete file request for \" + id);\n\n            if (options.method === \"DELETE\") {\n                requester.initTransport(id)\n                    .withPath(uuid)\n                    .withParams(additionalOptions)\n                    .send();\n            }\n            else {\n                additionalOptions[options.uuidParamName] = uuid;\n                requester.initTransport(id)\n                    .withParams(additionalOptions)\n                    .send();\n            }\n        }\n    });\n};\n","/*global qq, define */\n/*jshint strict:false,bitwise:false,nonew:false,asi:true,-W064,-W116,-W089 */\n/**\n * Mega pixel image rendering library for iOS6+\n *\n * Fixes iOS6+'s image file rendering issue for large size image (over mega-pixel),\n * which causes unexpected subsampling when drawing it in canvas.\n * By using this library, you can safely render the image with proper stretching.\n *\n * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>\n * Released under the MIT license\n *\n * Heavily modified by Widen for Fine Uploader\n */\n(function() {\n\n    /**\n     * Detect subsampling in loaded image.\n     * In iOS, larger images than 2M pixels may be subsampled in rendering.\n     */\n    function detectSubsampling(img) {\n        var iw = img.naturalWidth,\n            ih = img.naturalHeight,\n            canvas = document.createElement(\"canvas\"),\n            ctx;\n\n        if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image\n            canvas.width = canvas.height = 1;\n            ctx = canvas.getContext(\"2d\");\n            ctx.drawImage(img, -iw + 1, 0);\n            // subsampled image becomes half smaller in rendering size.\n            // check alpha channel value to confirm image is covering edge pixel or not.\n            // if alpha value is 0 image is not covering, hence subsampled.\n            return ctx.getImageData(0, 0, 1, 1).data[3] === 0;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * Detecting vertical squash in loaded image.\n     * Fixes a bug which squash image vertically while drawing into canvas for some images.\n     */\n    function detectVerticalSquash(img, iw, ih) {\n        var canvas = document.createElement(\"canvas\"),\n            sy = 0,\n            ey = ih,\n            py = ih,\n            ctx, data, alpha, ratio;\n\n        canvas.width = 1;\n        canvas.height = ih;\n        ctx = canvas.getContext(\"2d\");\n        ctx.drawImage(img, 0, 0);\n        data = ctx.getImageData(0, 0, 1, ih).data;\n\n        // search image edge pixel position in case it is squashed vertically.\n        while (py > sy) {\n            alpha = data[(py - 1) * 4 + 3];\n            if (alpha === 0) {\n                ey = py;\n            } else {\n                sy = py;\n            }\n            py = (ey + sy) >> 1;\n        }\n\n        ratio = (py / ih);\n        return (ratio === 0) ? 1 : ratio;\n    }\n\n    /**\n     * Rendering image element (with resizing) and get its data URL\n     */\n    function renderImageToDataURL(img, blob, options, doSquash) {\n        var canvas = document.createElement(\"canvas\"),\n            mime = options.mime || \"image/jpeg\",\n            promise = new qq.Promise();\n\n        renderImageToCanvas(img, blob, canvas, options, doSquash)\n            .then(function() {\n                promise.success(\n                    canvas.toDataURL(mime, options.quality || 0.8)\n                );\n            });\n\n        return promise;\n    }\n\n    function maybeCalculateDownsampledDimensions(spec) {\n        var maxPixels = 5241000; //iOS specific value\n\n        if (!qq.ios()) {\n            throw new qq.Error(\"Downsampled dimensions can only be reliably calculated for iOS!\");\n        }\n\n        if (spec.origHeight * spec.origWidth > maxPixels) {\n            return {\n                newHeight: Math.round(Math.sqrt(maxPixels * (spec.origHeight / spec.origWidth))),\n                newWidth: Math.round(Math.sqrt(maxPixels * (spec.origWidth / spec.origHeight)))\n            };\n        }\n    }\n\n    /**\n     * Rendering image element (with resizing) into the canvas element\n     */\n    function renderImageToCanvas(img, blob, canvas, options, doSquash) {\n        var iw = img.naturalWidth,\n            ih = img.naturalHeight,\n            width = options.width,\n            height = options.height,\n            ctx = canvas.getContext(\"2d\"),\n            promise = new qq.Promise(),\n            modifiedDimensions;\n\n        ctx.save();\n\n        if (options.resize) {\n            return renderImageToCanvasWithCustomResizer({\n                blob: blob,\n                canvas: canvas,\n                image: img,\n                imageHeight: ih,\n                imageWidth: iw,\n                orientation: options.orientation,\n                resize: options.resize,\n                targetHeight: height,\n                targetWidth: width\n            });\n        }\n\n        if (!qq.supportedFeatures.unlimitedScaledImageSize) {\n            modifiedDimensions = maybeCalculateDownsampledDimensions({\n                origWidth: width,\n                origHeight: height\n            });\n\n            if (modifiedDimensions) {\n                qq.log(qq.format(\"Had to reduce dimensions due to device limitations from {}w / {}h to {}w / {}h\",\n                    width, height, modifiedDimensions.newWidth, modifiedDimensions.newHeight),\n                    \"warn\");\n\n                width = modifiedDimensions.newWidth;\n                height = modifiedDimensions.newHeight;\n            }\n        }\n\n        transformCoordinate(canvas, width, height, options.orientation);\n\n        // Fine Uploader specific: Save some CPU cycles if not using iOS\n        // Assumption: This logic is only needed to overcome iOS image sampling issues\n        if (qq.ios()) {\n            (function() {\n                if (detectSubsampling(img)) {\n                    iw /= 2;\n                    ih /= 2;\n                }\n\n                var d = 1024, // size of tiling canvas\n                    tmpCanvas = document.createElement(\"canvas\"),\n                    vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1,\n                    dw = Math.ceil(d * width / iw),\n                    dh = Math.ceil(d * height / ih / vertSquashRatio),\n                    sy = 0,\n                    dy = 0,\n                    tmpCtx, sx, dx;\n\n                tmpCanvas.width = tmpCanvas.height = d;\n                tmpCtx = tmpCanvas.getContext(\"2d\");\n\n                while (sy < ih) {\n                    sx = 0;\n                    dx = 0;\n                    while (sx < iw) {\n                        tmpCtx.clearRect(0, 0, d, d);\n                        tmpCtx.drawImage(img, -sx, -sy);\n                        ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh);\n                        sx += d;\n                        dx += dw;\n                    }\n                    sy += d;\n                    dy += dh;\n                }\n                ctx.restore();\n                tmpCanvas = tmpCtx = null;\n            }());\n        }\n        else {\n            ctx.drawImage(img, 0, 0, width, height);\n        }\n\n        canvas.qqImageRendered && canvas.qqImageRendered();\n        promise.success();\n\n        return promise;\n    }\n\n    function renderImageToCanvasWithCustomResizer(resizeInfo) {\n        var blob = resizeInfo.blob,\n            image = resizeInfo.image,\n            imageHeight = resizeInfo.imageHeight,\n            imageWidth = resizeInfo.imageWidth,\n            orientation = resizeInfo.orientation,\n            promise = new qq.Promise(),\n            resize = resizeInfo.resize,\n            sourceCanvas = document.createElement(\"canvas\"),\n            sourceCanvasContext = sourceCanvas.getContext(\"2d\"),\n            targetCanvas = resizeInfo.canvas,\n            targetHeight = resizeInfo.targetHeight,\n            targetWidth = resizeInfo.targetWidth;\n\n        transformCoordinate(sourceCanvas, imageWidth, imageHeight, orientation);\n\n        targetCanvas.height = targetHeight;\n        targetCanvas.width = targetWidth;\n\n        sourceCanvasContext.drawImage(image, 0, 0);\n\n        resize({\n            blob: blob,\n            height: targetHeight,\n            image: image,\n            sourceCanvas: sourceCanvas,\n            targetCanvas: targetCanvas,\n            width: targetWidth\n        })\n            .then(\n                function success() {\n                    targetCanvas.qqImageRendered && targetCanvas.qqImageRendered();\n                    promise.success();\n                },\n                promise.failure\n            );\n\n        return promise;\n    }\n\n    /**\n     * Transform canvas coordination according to specified frame size and orientation\n     * Orientation value is from EXIF tag\n     */\n    function transformCoordinate(canvas, width, height, orientation) {\n        switch (orientation) {\n            case 5:\n            case 6:\n            case 7:\n            case 8:\n                canvas.width = height;\n                canvas.height = width;\n                break;\n            default:\n                canvas.width = width;\n                canvas.height = height;\n        }\n        var ctx = canvas.getContext(\"2d\");\n        switch (orientation) {\n            case 2:\n                // horizontal flip\n                ctx.translate(width, 0);\n                ctx.scale(-1, 1);\n                break;\n            case 3:\n                // 180 rotate left\n                ctx.translate(width, height);\n                ctx.rotate(Math.PI);\n                break;\n            case 4:\n                // vertical flip\n                ctx.translate(0, height);\n                ctx.scale(1, -1);\n                break;\n            case 5:\n                // vertical flip + 90 rotate right\n                ctx.rotate(0.5 * Math.PI);\n                ctx.scale(1, -1);\n                break;\n            case 6:\n                // 90 rotate right\n                ctx.rotate(0.5 * Math.PI);\n                ctx.translate(0, -height);\n                break;\n            case 7:\n                // horizontal flip + 90 rotate right\n                ctx.rotate(0.5 * Math.PI);\n                ctx.translate(width, -height);\n                ctx.scale(-1, 1);\n                break;\n            case 8:\n                // 90 rotate left\n                ctx.rotate(-0.5 * Math.PI);\n                ctx.translate(-width, 0);\n                break;\n            default:\n                break;\n        }\n    }\n\n    /**\n     * MegaPixImage class\n     */\n    function MegaPixImage(srcImage, errorCallback) {\n        var self = this;\n\n        if (window.Blob && srcImage instanceof Blob) {\n            (function() {\n                var img = new Image(),\n                    URL = window.URL && window.URL.createObjectURL ? window.URL :\n                        window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : null;\n                if (!URL) { throw Error(\"No createObjectURL function found to create blob url\"); }\n                img.src = URL.createObjectURL(srcImage);\n                self.blob = srcImage;\n                srcImage = img;\n            }());\n        }\n        if (!srcImage.naturalWidth && !srcImage.naturalHeight) {\n            srcImage.onload = function() {\n                var listeners = self.imageLoadListeners;\n                if (listeners) {\n                    self.imageLoadListeners = null;\n                    // IE11 doesn't reliably report actual image dimensions immediately after onload for small files,\n                    // so let's push this to the end of the UI thread queue.\n                    setTimeout(function() {\n                        for (var i = 0, len = listeners.length; i < len; i++) {\n                            listeners[i]();\n                        }\n                    }, 0);\n                }\n            };\n            srcImage.onerror = errorCallback;\n            this.imageLoadListeners = [];\n        }\n        this.srcImage = srcImage;\n    }\n\n    /**\n     * Rendering megapix image into specified target element\n     */\n    MegaPixImage.prototype.render = function(target, options) {\n        options = options || {};\n\n        var self = this,\n            imgWidth = this.srcImage.naturalWidth,\n            imgHeight = this.srcImage.naturalHeight,\n            width = options.width,\n            height = options.height,\n            maxWidth = options.maxWidth,\n            maxHeight = options.maxHeight,\n            doSquash = !this.blob || this.blob.type === \"image/jpeg\",\n            tagName = target.tagName.toLowerCase(),\n            opt;\n\n        if (this.imageLoadListeners) {\n            this.imageLoadListeners.push(function() { self.render(target, options); });\n            return;\n        }\n\n        if (width && !height) {\n            height = (imgHeight * width / imgWidth) << 0;\n        } else if (height && !width) {\n            width = (imgWidth * height / imgHeight) << 0;\n        } else {\n            width = imgWidth;\n            height = imgHeight;\n        }\n        if (maxWidth && width > maxWidth) {\n            width = maxWidth;\n            height = (imgHeight * width / imgWidth) << 0;\n        }\n        if (maxHeight && height > maxHeight) {\n            height = maxHeight;\n            width = (imgWidth * height / imgHeight) << 0;\n        }\n\n        opt = { width: width, height: height },\n        qq.each(options, function(optionsKey, optionsValue) {\n            opt[optionsKey] = optionsValue;\n        });\n\n        if (tagName === \"img\") {\n            (function() {\n                var oldTargetSrc = target.src;\n                renderImageToDataURL(self.srcImage, self.blob, opt, doSquash)\n                    .then(function(dataUri) {\n                        target.src = dataUri;\n                        oldTargetSrc === target.src && target.onload();\n                    });\n            }());\n        } else if (tagName === \"canvas\") {\n            renderImageToCanvas(this.srcImage, this.blob, target, opt, doSquash);\n        }\n        if (typeof this.onrender === \"function\") {\n            this.onrender(target);\n        }\n    };\n\n    qq.MegaPixImage = MegaPixImage;\n})();\n","/*globals qq */\n/**\n * Draws a thumbnail of a Blob/File/URL onto an <img> or <canvas>.\n *\n * @constructor\n */\nqq.ImageGenerator = function(log) {\n    \"use strict\";\n\n    function isImg(el) {\n        return el.tagName.toLowerCase() === \"img\";\n    }\n\n    function isCanvas(el) {\n        return el.tagName.toLowerCase() === \"canvas\";\n    }\n\n    function isImgCorsSupported() {\n        return new Image().crossOrigin !== undefined;\n    }\n\n    function isCanvasSupported() {\n        var canvas = document.createElement(\"canvas\");\n\n        return canvas.getContext && canvas.getContext(\"2d\");\n    }\n\n    // This is only meant to determine the MIME type of a renderable image file.\n    // It is used to ensure images drawn from a URL that have transparent backgrounds\n    // are rendered correctly, among other things.\n    function determineMimeOfFileName(nameWithPath) {\n        /*jshint -W015 */\n        var pathSegments = nameWithPath.split(\"/\"),\n            name = pathSegments[pathSegments.length - 1].split(\"?\")[0],\n            extension = qq.getExtension(name);\n\n        extension = extension && extension.toLowerCase();\n\n        switch (extension) {\n            case \"jpeg\":\n            case \"jpg\":\n                return \"image/jpeg\";\n            case \"png\":\n                return \"image/png\";\n            case \"bmp\":\n                return \"image/bmp\";\n            case \"gif\":\n                return \"image/gif\";\n            case \"tiff\":\n            case \"tif\":\n                return \"image/tiff\";\n        }\n    }\n\n    // This will likely not work correctly in IE8 and older.\n    // It's only used as part of a formula to determine\n    // if a canvas can be used to scale a server-hosted thumbnail.\n    // If canvas isn't supported by the UA (IE8 and older)\n    // this method should not even be called.\n    function isCrossOrigin(url) {\n        var targetAnchor = document.createElement(\"a\"),\n            targetProtocol, targetHostname, targetPort;\n\n        targetAnchor.href = url;\n\n        targetProtocol = targetAnchor.protocol;\n        targetPort = targetAnchor.port;\n        targetHostname = targetAnchor.hostname;\n\n        if (targetProtocol.toLowerCase() !== window.location.protocol.toLowerCase()) {\n            return true;\n        }\n\n        if (targetHostname.toLowerCase() !== window.location.hostname.toLowerCase()) {\n            return true;\n        }\n\n        // IE doesn't take ports into consideration when determining if two endpoints are same origin.\n        if (targetPort !== window.location.port && !qq.ie()) {\n            return true;\n        }\n\n        return false;\n    }\n\n    function registerImgLoadListeners(img, promise) {\n        img.onload = function() {\n            img.onload = null;\n            img.onerror = null;\n            promise.success(img);\n        };\n\n        img.onerror = function() {\n            img.onload = null;\n            img.onerror = null;\n            log(\"Problem drawing thumbnail!\", \"error\");\n            promise.failure(img, \"Problem drawing thumbnail!\");\n        };\n    }\n\n    function registerCanvasDrawImageListener(canvas, promise) {\n        // The image is drawn on the canvas by a third-party library,\n        // and we want to know when this is completed.  Since the library\n        // may invoke drawImage many times in a loop, we need to be called\n        // back when the image is fully rendered.  So, we are expecting the\n        // code that draws this image to follow a convention that involves a\n        // function attached to the canvas instance be invoked when it is done.\n        canvas.qqImageRendered = function() {\n            promise.success(canvas);\n        };\n    }\n\n    // Fulfills a `qq.Promise` when an image has been drawn onto the target,\n    // whether that is a <canvas> or an <img>.  The attempt is considered a\n    // failure if the target is not an <img> or a <canvas>, or if the drawing\n    // attempt was not successful.\n    function registerThumbnailRenderedListener(imgOrCanvas, promise) {\n        var registered = isImg(imgOrCanvas) || isCanvas(imgOrCanvas);\n\n        if (isImg(imgOrCanvas)) {\n            registerImgLoadListeners(imgOrCanvas, promise);\n        }\n        else if (isCanvas(imgOrCanvas)) {\n            registerCanvasDrawImageListener(imgOrCanvas, promise);\n        }\n        else {\n            promise.failure(imgOrCanvas);\n            log(qq.format(\"Element container of type {} is not supported!\", imgOrCanvas.tagName), \"error\");\n        }\n\n        return registered;\n    }\n\n    // Draw a preview iff the current UA can natively display it.\n    // Also rotate the image if necessary.\n    function draw(fileOrBlob, container, options) {\n        var drawPreview = new qq.Promise(),\n            identifier = new qq.Identify(fileOrBlob, log),\n            maxSize = options.maxSize,\n            // jshint eqnull:true\n            orient = options.orient == null ? true : options.orient,\n            megapixErrorHandler = function() {\n                container.onerror = null;\n                container.onload = null;\n                log(\"Could not render preview, file may be too large!\", \"error\");\n                drawPreview.failure(container, \"Browser cannot render image!\");\n            };\n\n        identifier.isPreviewable().then(\n            function(mime) {\n                // If options explicitly specify that Orientation is not desired,\n                // replace the orient task with a dummy promise that \"succeeds\" immediately.\n                var dummyExif = {\n                        parse: function() {\n                            return new qq.Promise().success();\n                        }\n                    },\n                    exif = orient ? new qq.Exif(fileOrBlob, log) : dummyExif,\n                    mpImg = new qq.MegaPixImage(fileOrBlob, megapixErrorHandler);\n\n                if (registerThumbnailRenderedListener(container, drawPreview)) {\n                    exif.parse().then(\n                        function(exif) {\n                            var orientation = exif && exif.Orientation;\n\n                            mpImg.render(container, {\n                                maxWidth: maxSize,\n                                maxHeight: maxSize,\n                                orientation: orientation,\n                                mime: mime,\n                                resize: options.customResizeFunction\n                            });\n                        },\n\n                        function(failureMsg) {\n                            log(qq.format(\"EXIF data could not be parsed ({}).  Assuming orientation = 1.\", failureMsg));\n\n                            mpImg.render(container, {\n                                maxWidth: maxSize,\n                                maxHeight: maxSize,\n                                mime: mime,\n                                resize: options.customResizeFunction\n                            });\n                        }\n                    );\n                }\n            },\n\n            function() {\n                log(\"Not previewable\");\n                drawPreview.failure(container, \"Not previewable\");\n            }\n        );\n\n        return drawPreview;\n    }\n\n    function drawOnCanvasOrImgFromUrl(url, canvasOrImg, draw, maxSize, customResizeFunction) {\n        var tempImg = new Image(),\n            tempImgRender = new qq.Promise();\n\n        registerThumbnailRenderedListener(tempImg, tempImgRender);\n\n        if (isCrossOrigin(url)) {\n            tempImg.crossOrigin = \"anonymous\";\n        }\n\n        tempImg.src = url;\n\n        tempImgRender.then(\n            function rendered() {\n                registerThumbnailRenderedListener(canvasOrImg, draw);\n\n                var mpImg = new qq.MegaPixImage(tempImg);\n                mpImg.render(canvasOrImg, {\n                    maxWidth: maxSize,\n                    maxHeight: maxSize,\n                    mime: determineMimeOfFileName(url),\n                    resize: customResizeFunction\n                });\n            },\n\n            draw.failure\n        );\n    }\n\n    function drawOnImgFromUrlWithCssScaling(url, img, draw, maxSize) {\n        registerThumbnailRenderedListener(img, draw);\n        // NOTE: The fact that maxWidth/height is set on the thumbnail for scaled images\n        // that must drop back to CSS is known and exploited by the templating module.\n        // In this module, we pre-render \"waiting\" thumbs for all files immediately after they\n        // are submitted, and we must be sure to pass any style associated with the \"waiting\" preview.\n        qq(img).css({\n            maxWidth: maxSize + \"px\",\n            maxHeight: maxSize + \"px\"\n        });\n\n        img.src = url;\n    }\n\n    // Draw a (server-hosted) thumbnail given a URL.\n    // This will optionally scale the thumbnail as well.\n    // It attempts to use <canvas> to scale, but will fall back\n    // to max-width and max-height style properties if the UA\n    // doesn't support canvas or if the images is cross-domain and\n    // the UA doesn't support the crossorigin attribute on img tags,\n    // which is required to scale a cross-origin image using <canvas> &\n    // then export it back to an <img>.\n    function drawFromUrl(url, container, options) {\n        var draw = new qq.Promise(),\n            scale = options.scale,\n            maxSize = scale ? options.maxSize : null;\n\n        // container is an img, scaling needed\n        if (scale && isImg(container)) {\n            // Iff canvas is available in this UA, try to use it for scaling.\n            // Otherwise, fall back to CSS scaling\n            if (isCanvasSupported()) {\n                // Attempt to use <canvas> for image scaling,\n                // but we must fall back to scaling via CSS/styles\n                // if this is a cross-origin image and the UA doesn't support <img> CORS.\n                if (isCrossOrigin(url) && !isImgCorsSupported()) {\n                    drawOnImgFromUrlWithCssScaling(url, container, draw, maxSize);\n                }\n                else {\n                    drawOnCanvasOrImgFromUrl(url, container, draw, maxSize);\n                }\n            }\n            else {\n                drawOnImgFromUrlWithCssScaling(url, container, draw, maxSize);\n            }\n        }\n        // container is a canvas, scaling optional\n        else if (isCanvas(container)) {\n            drawOnCanvasOrImgFromUrl(url, container, draw, maxSize);\n        }\n        // container is an img & no scaling: just set the src attr to the passed url\n        else if (registerThumbnailRenderedListener(container, draw)) {\n            container.src = url;\n        }\n\n        return draw;\n    }\n\n    qq.extend(this, {\n        /**\n         * Generate a thumbnail.  Depending on the arguments, this may either result in\n         * a client-side rendering of an image (if a `Blob` is supplied) or a server-generated\n         * image that may optionally be scaled client-side using <canvas> or CSS/styles (as a fallback).\n         *\n         * @param fileBlobOrUrl a `File`, `Blob`, or a URL pointing to the image\n         * @param container <img> or <canvas> to contain the preview\n         * @param options possible properties include `maxSize` (int), `orient` (bool - default true), resize` (bool - default true), and `customResizeFunction`.\n         * @returns qq.Promise fulfilled when the preview has been drawn, or the attempt has failed\n         */\n        generate: function(fileBlobOrUrl, container, options) {\n            if (qq.isString(fileBlobOrUrl)) {\n                log(\"Attempting to update thumbnail based on server response.\");\n                return drawFromUrl(fileBlobOrUrl, container, options || {});\n            }\n            else {\n                log(\"Attempting to draw client-side image preview.\");\n                return draw(fileBlobOrUrl, container, options || {});\n            }\n        }\n    });\n\n    /*<testing>*/\n    this._testing = {};\n    this._testing.isImg = isImg;\n    this._testing.isCanvas = isCanvas;\n    this._testing.isCrossOrigin = isCrossOrigin;\n    this._testing.determineMimeOfFileName = determineMimeOfFileName;\n    /*</testing>*/\n};\n","/*globals qq */\n/**\n * EXIF image data parser.  Currently only parses the Orientation tag value,\n * but this may be expanded to other tags in the future.\n *\n * @param fileOrBlob Attempt to parse EXIF data in this `Blob`\n * @constructor\n */\nqq.Exif = function(fileOrBlob, log) {\n    \"use strict\";\n\n    // Orientation is the only tag parsed here at this time.\n    var TAG_IDS = [274],\n        TAG_INFO = {\n            274: {\n                name: \"Orientation\",\n                bytes: 2\n            }\n        };\n\n    // Convert a little endian (hex string) to big endian (decimal).\n    function parseLittleEndian(hex) {\n        var result = 0,\n            pow = 0;\n\n        while (hex.length > 0) {\n            result += parseInt(hex.substring(0, 2), 16) * Math.pow(2, pow);\n            hex = hex.substring(2, hex.length);\n            pow += 8;\n        }\n\n        return result;\n    }\n\n    // Find the byte offset, of Application Segment 1 (EXIF).\n    // External callers need not supply any arguments.\n    function seekToApp1(offset, promise) {\n        var theOffset = offset,\n            thePromise = promise;\n        if (theOffset === undefined) {\n            theOffset = 2;\n            thePromise = new qq.Promise();\n        }\n\n        qq.readBlobToHex(fileOrBlob, theOffset, 4).then(function(hex) {\n            var match = /^ffe([0-9])/.exec(hex),\n                segmentLength;\n\n            if (match) {\n                if (match[1] !== \"1\") {\n                    segmentLength = parseInt(hex.slice(4, 8), 16);\n                    seekToApp1(theOffset + segmentLength + 2, thePromise);\n                }\n                else {\n                    thePromise.success(theOffset);\n                }\n            }\n            else {\n                thePromise.failure(\"No EXIF header to be found!\");\n            }\n        });\n\n        return thePromise;\n    }\n\n    // Find the byte offset of Application Segment 1 (EXIF) for valid JPEGs only.\n    function getApp1Offset() {\n        var promise = new qq.Promise();\n\n        qq.readBlobToHex(fileOrBlob, 0, 6).then(function(hex) {\n            if (hex.indexOf(\"ffd8\") !== 0) {\n                promise.failure(\"Not a valid JPEG!\");\n            }\n            else {\n                seekToApp1().then(function(offset) {\n                    promise.success(offset);\n                },\n                function(error) {\n                    promise.failure(error);\n                });\n            }\n        });\n\n        return promise;\n    }\n\n    // Determine the byte ordering of the EXIF header.\n    function isLittleEndian(app1Start) {\n        var promise = new qq.Promise();\n\n        qq.readBlobToHex(fileOrBlob, app1Start + 10, 2).then(function(hex) {\n            promise.success(hex === \"4949\");\n        });\n\n        return promise;\n    }\n\n    // Determine the number of directory entries in the EXIF header.\n    function getDirEntryCount(app1Start, littleEndian) {\n        var promise = new qq.Promise();\n\n        qq.readBlobToHex(fileOrBlob, app1Start + 18, 2).then(function(hex) {\n            if (littleEndian) {\n                return promise.success(parseLittleEndian(hex));\n            }\n            else {\n                promise.success(parseInt(hex, 16));\n            }\n        });\n\n        return promise;\n    }\n\n    // Get the IFD portion of the EXIF header as a hex string.\n    function getIfd(app1Start, dirEntries) {\n        var offset = app1Start + 20,\n            bytes = dirEntries * 12;\n\n        return qq.readBlobToHex(fileOrBlob, offset, bytes);\n    }\n\n    // Obtain an array of all directory entries (as hex strings) in the EXIF header.\n    function getDirEntries(ifdHex) {\n        var entries = [],\n            offset = 0;\n\n        while (offset + 24 <= ifdHex.length) {\n            entries.push(ifdHex.slice(offset, offset + 24));\n            offset += 24;\n        }\n\n        return entries;\n    }\n\n    // Obtain values for all relevant tags and return them.\n    function getTagValues(littleEndian, dirEntries) {\n        var TAG_VAL_OFFSET = 16,\n            tagsToFind = qq.extend([], TAG_IDS),\n            vals = {};\n\n        qq.each(dirEntries, function(idx, entry) {\n            var idHex = entry.slice(0, 4),\n                id = littleEndian ? parseLittleEndian(idHex) : parseInt(idHex, 16),\n                tagsToFindIdx = tagsToFind.indexOf(id),\n                tagValHex, tagName, tagValLength;\n\n            if (tagsToFindIdx >= 0) {\n                tagName = TAG_INFO[id].name;\n                tagValLength = TAG_INFO[id].bytes;\n                tagValHex = entry.slice(TAG_VAL_OFFSET, TAG_VAL_OFFSET + (tagValLength * 2));\n                vals[tagName] = littleEndian ? parseLittleEndian(tagValHex) : parseInt(tagValHex, 16);\n\n                tagsToFind.splice(tagsToFindIdx, 1);\n            }\n\n            if (tagsToFind.length === 0) {\n                return false;\n            }\n        });\n\n        return vals;\n    }\n\n    qq.extend(this, {\n        /**\n         * Attempt to parse the EXIF header for the `Blob` associated with this instance.\n         *\n         * @returns {qq.Promise} To be fulfilled when the parsing is complete.\n         * If successful, the parsed EXIF header as an object will be included.\n         */\n        parse: function() {\n            var parser = new qq.Promise(),\n                onParseFailure = function(message) {\n                    log(qq.format(\"EXIF header parse failed: '{}' \", message));\n                    parser.failure(message);\n                };\n\n            getApp1Offset().then(function(app1Offset) {\n                log(qq.format(\"Moving forward with EXIF header parsing for '{}'\", fileOrBlob.name === undefined ? \"blob\" : fileOrBlob.name));\n\n                isLittleEndian(app1Offset).then(function(littleEndian) {\n\n                    log(qq.format(\"EXIF Byte order is {} endian\", littleEndian ? \"little\" : \"big\"));\n\n                    getDirEntryCount(app1Offset, littleEndian).then(function(dirEntryCount) {\n\n                        log(qq.format(\"Found {} APP1 directory entries\", dirEntryCount));\n\n                        getIfd(app1Offset, dirEntryCount).then(function(ifdHex) {\n                            var dirEntries = getDirEntries(ifdHex),\n                                tagValues = getTagValues(littleEndian, dirEntries);\n\n                            log(\"Successfully parsed some EXIF tags\");\n\n                            parser.success(tagValues);\n                        }, onParseFailure);\n                    }, onParseFailure);\n                }, onParseFailure);\n            }, onParseFailure);\n\n            return parser;\n        }\n    });\n\n    /*<testing>*/\n    this._testing = {};\n    this._testing.parseLittleEndian = parseLittleEndian;\n    /*</testing>*/\n};\n","/*globals qq */\nqq.Identify = function(fileOrBlob, log) {\n    \"use strict\";\n\n    function isIdentifiable(magicBytes, questionableBytes) {\n        var identifiable = false,\n            magicBytesEntries = [].concat(magicBytes);\n\n        qq.each(magicBytesEntries, function(idx, magicBytesArrayEntry) {\n            if (questionableBytes.indexOf(magicBytesArrayEntry) === 0) {\n                identifiable = true;\n                return false;\n            }\n        });\n\n        return identifiable;\n    }\n\n    qq.extend(this, {\n        /**\n         * Determines if a Blob can be displayed natively in the current browser.  This is done by reading magic\n         * bytes in the beginning of the file, so this is an asynchronous operation.  Before we attempt to read the\n         * file, we will examine the blob's type attribute to save CPU cycles.\n         *\n         * @returns {qq.Promise} Promise that is fulfilled when identification is complete.\n         * If successful, the MIME string is passed to the success handler.\n         */\n        isPreviewable: function() {\n            var self = this,\n                identifier = new qq.Promise(),\n                previewable = false,\n                name = fileOrBlob.name === undefined ? \"blob\" : fileOrBlob.name;\n\n            log(qq.format(\"Attempting to determine if {} can be rendered in this browser\", name));\n\n            log(\"First pass: check type attribute of blob object.\");\n\n            if (this.isPreviewableSync()) {\n                log(\"Second pass: check for magic bytes in file header.\");\n\n                qq.readBlobToHex(fileOrBlob, 0, 4).then(function(hex) {\n                    qq.each(self.PREVIEWABLE_MIME_TYPES, function(mime, bytes) {\n                        if (isIdentifiable(bytes, hex)) {\n                            // Safari is the only supported browser that can deal with TIFFs natively,\n                            // so, if this is a TIFF and the UA isn't Safari, declare this file \"non-previewable\".\n                            if (mime !== \"image/tiff\" || qq.supportedFeatures.tiffPreviews) {\n                                previewable = true;\n                                identifier.success(mime);\n                            }\n\n                            return false;\n                        }\n                    });\n\n                    log(qq.format(\"'{}' is {} able to be rendered in this browser\", name, previewable ? \"\" : \"NOT\"));\n\n                    if (!previewable) {\n                        identifier.failure();\n                    }\n                },\n                function() {\n                    log(\"Error reading file w/ name '\" + name + \"'.  Not able to be rendered in this browser.\");\n                    identifier.failure();\n                });\n            }\n            else {\n                identifier.failure();\n            }\n\n            return identifier;\n        },\n\n        /**\n         * Determines if a Blob can be displayed natively in the current browser.  This is done by checking the\n         * blob's type attribute.  This is a synchronous operation, useful for situations where an asynchronous operation\n         * would be challenging to support.  Note that the blob's type property is not as accurate as reading the\n         * file's magic bytes.\n         *\n         * @returns {Boolean} true if the blob can be rendered in the current browser\n         */\n        isPreviewableSync: function() {\n            var fileMime = fileOrBlob.type,\n                // Assumption: This will only ever be executed in browsers that support `Object.keys`.\n                isRecognizedImage = qq.indexOf(Object.keys(this.PREVIEWABLE_MIME_TYPES), fileMime) >= 0,\n                previewable = false,\n                name = fileOrBlob.name === undefined ? \"blob\" : fileOrBlob.name;\n\n            if (isRecognizedImage) {\n                if (fileMime === \"image/tiff\") {\n                    previewable = qq.supportedFeatures.tiffPreviews;\n                }\n                else {\n                    previewable = true;\n                }\n            }\n\n            !previewable && log(name + \" is not previewable in this browser per the blob's type attr\");\n\n            return previewable;\n        }\n    });\n};\n\nqq.Identify.prototype.PREVIEWABLE_MIME_TYPES = {\n    \"image/jpeg\": \"ffd8ff\",\n    \"image/gif\": \"474946\",\n    \"image/png\": \"89504e\",\n    \"image/bmp\": \"424d\",\n    \"image/tiff\": [\"49492a00\", \"4d4d002a\"]\n};\n","/*globals qq*/\n/**\n * Attempts to validate an image, wherever possible.\n *\n * @param blob File or Blob representing a user-selecting image.\n * @param log Uses this to post log messages to the console.\n * @constructor\n */\nqq.ImageValidation = function(blob, log) {\n    \"use strict\";\n\n    /**\n     * @param limits Object with possible image-related limits to enforce.\n     * @returns {boolean} true if at least one of the limits has a non-zero value\n     */\n    function hasNonZeroLimits(limits) {\n        var atLeastOne = false;\n\n        qq.each(limits, function(limit, value) {\n            if (value > 0) {\n                atLeastOne = true;\n                return false;\n            }\n        });\n\n        return atLeastOne;\n    }\n\n    /**\n     * @returns {qq.Promise} The promise is a failure if we can't obtain the width & height.\n     * Otherwise, `success` is called on the returned promise with an object containing\n     * `width` and `height` properties.\n     */\n    function getWidthHeight() {\n        var sizeDetermination = new qq.Promise();\n\n        new qq.Identify(blob, log).isPreviewable().then(function() {\n            var image = new Image(),\n                url = window.URL && window.URL.createObjectURL ? window.URL :\n                      window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL :\n                      null;\n\n            if (url) {\n                image.onerror = function() {\n                    log(\"Cannot determine dimensions for image.  May be too large.\", \"error\");\n                    sizeDetermination.failure();\n                };\n\n                image.onload = function() {\n                    sizeDetermination.success({\n                        width: this.width,\n                        height: this.height\n                    });\n                };\n\n                image.src = url.createObjectURL(blob);\n            }\n            else {\n                log(\"No createObjectURL function available to generate image URL!\", \"error\");\n                sizeDetermination.failure();\n            }\n        }, sizeDetermination.failure);\n\n        return sizeDetermination;\n    }\n\n    /**\n     *\n     * @param limits Object with possible image-related limits to enforce.\n     * @param dimensions Object containing `width` & `height` properties for the image to test.\n     * @returns {String || undefined} The name of the failing limit.  Undefined if no failing limits.\n     */\n    function getFailingLimit(limits, dimensions) {\n        var failingLimit;\n\n        qq.each(limits, function(limitName, limitValue) {\n            if (limitValue > 0) {\n                var limitMatcher = /(max|min)(Width|Height)/.exec(limitName),\n                    dimensionPropName = limitMatcher[2].charAt(0).toLowerCase() + limitMatcher[2].slice(1),\n                    actualValue = dimensions[dimensionPropName];\n\n                /*jshint -W015*/\n                switch (limitMatcher[1]) {\n                    case \"min\":\n                        if (actualValue < limitValue) {\n                            failingLimit = limitName;\n                            return false;\n                        }\n                        break;\n                    case \"max\":\n                        if (actualValue > limitValue) {\n                            failingLimit = limitName;\n                            return false;\n                        }\n                        break;\n                }\n            }\n        });\n\n        return failingLimit;\n    }\n\n    /**\n     * Validate the associated blob.\n     *\n     * @param limits\n     * @returns {qq.Promise} `success` is called on the promise is the image is valid or\n     * if the blob is not an image, or if the image is not verifiable.\n     * Otherwise, `failure` with the name of the failing limit.\n     */\n    this.validate = function(limits) {\n        var validationEffort = new qq.Promise();\n\n        log(\"Attempting to validate image.\");\n\n        if (hasNonZeroLimits(limits)) {\n            getWidthHeight().then(function(dimensions) {\n                var failingLimit = getFailingLimit(limits, dimensions);\n\n                if (failingLimit) {\n                    validationEffort.failure(failingLimit);\n                }\n                else {\n                    validationEffort.success();\n                }\n            }, validationEffort.success);\n        }\n        else {\n            validationEffort.success();\n        }\n\n        return validationEffort;\n    };\n};\n","/* globals qq */\n/**\n * Module used to control populating the initial list of files.\n *\n * @constructor\n */\nqq.Session = function(spec) {\n    \"use strict\";\n\n    var options = {\n        endpoint: null,\n        params: {},\n        customHeaders: {},\n        cors: {},\n        addFileRecord: function(sessionData) {},\n        log: function(message, level) {}\n    };\n\n    qq.extend(options, spec, true);\n\n    function isJsonResponseValid(response) {\n        if (qq.isArray(response)) {\n            return true;\n        }\n\n        options.log(\"Session response is not an array.\", \"error\");\n    }\n\n    function handleFileItems(fileItems, success, xhrOrXdr, promise) {\n        var someItemsIgnored = false;\n\n        success = success && isJsonResponseValid(fileItems);\n\n        if (success) {\n            qq.each(fileItems, function(idx, fileItem) {\n                /* jshint eqnull:true */\n                if (fileItem.uuid == null) {\n                    someItemsIgnored = true;\n                    options.log(qq.format(\"Session response item {} did not include a valid UUID - ignoring.\", idx), \"error\");\n                }\n                else if (fileItem.name == null) {\n                    someItemsIgnored = true;\n                    options.log(qq.format(\"Session response item {} did not include a valid name - ignoring.\", idx), \"error\");\n                }\n                else {\n                    try {\n                        options.addFileRecord(fileItem);\n                        return true;\n                    }\n                    catch (err) {\n                        someItemsIgnored = true;\n                        options.log(err.message, \"error\");\n                    }\n                }\n\n                return false;\n            });\n        }\n\n        promise[success && !someItemsIgnored ? \"success\" : \"failure\"](fileItems, xhrOrXdr);\n    }\n\n    // Initiate a call to the server that will be used to populate the initial file list.\n    // Returns a `qq.Promise`.\n    this.refresh = function() {\n        /*jshint indent:false */\n        var refreshEffort = new qq.Promise(),\n            refreshCompleteCallback = function(response, success, xhrOrXdr) {\n                handleFileItems(response, success, xhrOrXdr, refreshEffort);\n            },\n            requesterOptions = qq.extend({}, options),\n            requester = new qq.SessionAjaxRequester(\n                qq.extend(requesterOptions, {onComplete: refreshCompleteCallback})\n            );\n\n        requester.queryServer();\n\n        return refreshEffort;\n    };\n};\n","/*globals qq, XMLHttpRequest*/\n/**\n * Thin module used to send GET requests to the server, expecting information about session\n * data used to initialize an uploader instance.\n *\n * @param spec Various options used to influence the associated request.\n * @constructor\n */\nqq.SessionAjaxRequester = function(spec) {\n    \"use strict\";\n\n    var requester,\n        options = {\n            endpoint: null,\n            customHeaders: {},\n            params: {},\n            cors: {\n                expected: false,\n                sendCredentials: false\n            },\n            onComplete: function(response, success, xhrOrXdr) {},\n            log: function(str, level) {}\n        };\n\n    qq.extend(options, spec);\n\n    function onComplete(id, xhrOrXdr, isError) {\n        var response = null;\n\n        /* jshint eqnull:true */\n        if (xhrOrXdr.responseText != null) {\n            try {\n                response = qq.parseJson(xhrOrXdr.responseText);\n            }\n            catch (err) {\n                options.log(\"Problem parsing session response: \" + err.message, \"error\");\n                isError = true;\n            }\n        }\n\n        options.onComplete(response, !isError, xhrOrXdr);\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        acceptHeader: \"application/json\",\n        validMethods: [\"GET\"],\n        method: \"GET\",\n        endpointStore: {\n            get: function() {\n                return options.endpoint;\n            }\n        },\n        customHeaders: options.customHeaders,\n        log: options.log,\n        onComplete: onComplete,\n        cors: options.cors\n    }));\n\n    qq.extend(this, {\n        queryServer: function() {\n            var params = qq.extend({}, options.params);\n\n            options.log(\"Session query request.\");\n\n            requester.initTransport(\"sessionRefresh\")\n                .withParams(params)\n                .withCacheBuster()\n                .send();\n        }\n    });\n};\n","/* globals qq, ExifRestorer */\n/**\n * Controls generation of scaled images based on a reference image encapsulated in a `File` or `Blob`.\n * Scaled images are generated and converted to blobs on-demand.\n * Multiple scaled images per reference image with varying sizes and other properties are supported.\n *\n * @param spec Information about the scaled images to generate.\n * @param log Logger instance\n * @constructor\n */\nqq.Scaler = function(spec, log) {\n    \"use strict\";\n\n    var self = this,\n        customResizeFunction = spec.customResizer,\n        includeOriginal = spec.sendOriginal,\n        orient = spec.orient,\n        defaultType = spec.defaultType,\n        defaultQuality = spec.defaultQuality / 100,\n        failedToScaleText = spec.failureText,\n        includeExif = spec.includeExif,\n        sizes = this._getSortedSizes(spec.sizes);\n\n    // Revealed API for instances of this module\n    qq.extend(this, {\n        // If no targeted sizes have been declared or if this browser doesn't support\n        // client-side image preview generation, there is no scaling to do.\n        enabled: qq.supportedFeatures.scaling && sizes.length > 0,\n\n        getFileRecords: function(originalFileUuid, originalFileName, originalBlobOrBlobData) {\n            var self = this,\n                records = [],\n                originalBlob = originalBlobOrBlobData.blob ? originalBlobOrBlobData.blob : originalBlobOrBlobData,\n                identifier = new qq.Identify(originalBlob, log);\n\n            // If the reference file cannot be rendered natively, we can't create scaled versions.\n            if (identifier.isPreviewableSync()) {\n                // Create records for each scaled version & add them to the records array, smallest first.\n                qq.each(sizes, function(idx, sizeRecord) {\n                    var outputType = self._determineOutputType({\n                        defaultType: defaultType,\n                        requestedType: sizeRecord.type,\n                        refType: originalBlob.type\n                    });\n\n                    records.push({\n                        uuid: qq.getUniqueId(),\n                        name: self._getName(originalFileName, {\n                            name: sizeRecord.name,\n                            type: outputType,\n                            refType: originalBlob.type\n                        }),\n                        blob: new qq.BlobProxy(originalBlob,\n                        qq.bind(self._generateScaledImage, self, {\n                            customResizeFunction: customResizeFunction,\n                            maxSize: sizeRecord.maxSize,\n                            orient: orient,\n                            type: outputType,\n                            quality: defaultQuality,\n                            failedText: failedToScaleText,\n                            includeExif: includeExif,\n                            log: log\n                        }))\n                    });\n                });\n\n                records.push({\n                    uuid: originalFileUuid,\n                    name: originalFileName,\n                    size: originalBlob.size,\n                    blob: includeOriginal ? originalBlob : null\n                });\n            }\n            else {\n                records.push({\n                    uuid: originalFileUuid,\n                    name: originalFileName,\n                    size: originalBlob.size,\n                    blob: originalBlob\n                });\n            }\n\n            return records;\n        },\n\n        handleNewFile: function(file, name, uuid, size, fileList, batchId, uuidParamName, api) {\n            var self = this,\n                buttonId = file.qqButtonId || (file.blob && file.blob.qqButtonId),\n                scaledIds = [],\n                originalId = null,\n                addFileToHandler = api.addFileToHandler,\n                uploadData = api.uploadData,\n                paramsStore = api.paramsStore,\n                proxyGroupId = qq.getUniqueId();\n\n            qq.each(self.getFileRecords(uuid, name, file), function(idx, record) {\n                var blobSize = record.size,\n                    id;\n\n                if (record.blob instanceof qq.BlobProxy) {\n                    blobSize = -1;\n                }\n\n                id = uploadData.addFile({\n                    uuid: record.uuid,\n                    name: record.name,\n                    size: blobSize,\n                    batchId: batchId,\n                    proxyGroupId: proxyGroupId\n                });\n\n                if (record.blob instanceof qq.BlobProxy) {\n                    scaledIds.push(id);\n                }\n                else {\n                    originalId = id;\n                }\n\n                if (record.blob) {\n                    addFileToHandler(id, record.blob);\n                    fileList.push({id: id, file: record.blob});\n                }\n                else {\n                    uploadData.setStatus(id, qq.status.REJECTED);\n                }\n            });\n\n            // If we are potentially uploading an original file and some scaled versions,\n            // ensure the scaled versions include reference's to the parent's UUID and size\n            // in their associated upload requests.\n            if (originalId !== null) {\n                qq.each(scaledIds, function(idx, scaledId) {\n                    var params = {\n                        qqparentuuid: uploadData.retrieve({id: originalId}).uuid,\n                        qqparentsize: uploadData.retrieve({id: originalId}).size\n                    };\n\n                    // Make sure the UUID for each scaled image is sent with the upload request,\n                    // to be consistent (since we may need to ensure it is sent for the original file as well).\n                    params[uuidParamName] = uploadData.retrieve({id: scaledId}).uuid;\n\n                    uploadData.setParentId(scaledId, originalId);\n                    paramsStore.addReadOnly(scaledId, params);\n                });\n\n                // If any scaled images are tied to this parent image, be SURE we send its UUID as an upload request\n                // parameter as well.\n                if (scaledIds.length) {\n                    (function() {\n                        var param = {};\n                        param[uuidParamName] = uploadData.retrieve({id: originalId}).uuid;\n                        paramsStore.addReadOnly(originalId, param);\n                    }());\n                }\n            }\n        }\n    });\n};\n\nqq.extend(qq.Scaler.prototype, {\n    scaleImage: function(id, specs, api) {\n        \"use strict\";\n\n        if (!qq.supportedFeatures.scaling) {\n            throw new qq.Error(\"Scaling is not supported in this browser!\");\n        }\n\n        var scalingEffort = new qq.Promise(),\n            log = api.log,\n            file = api.getFile(id),\n            uploadData = api.uploadData.retrieve({id: id}),\n            name = uploadData && uploadData.name,\n            uuid = uploadData && uploadData.uuid,\n            scalingOptions = {\n                customResizer: specs.customResizer,\n                sendOriginal: false,\n                orient: specs.orient,\n                defaultType: specs.type || null,\n                defaultQuality: specs.quality,\n                failedToScaleText: \"Unable to scale\",\n                sizes: [{name: \"\", maxSize: specs.maxSize}]\n            },\n            scaler = new qq.Scaler(scalingOptions, log);\n\n        if (!qq.Scaler || !qq.supportedFeatures.imagePreviews || !file) {\n            scalingEffort.failure();\n\n            log(\"Could not generate requested scaled image for \" + id + \".  \" +\n                \"Scaling is either not possible in this browser, or the file could not be located.\", \"error\");\n        }\n        else {\n            (qq.bind(function() {\n                // Assumption: There will never be more than one record\n                var record = scaler.getFileRecords(uuid, name, file)[0];\n\n                if (record && record.blob instanceof qq.BlobProxy) {\n                    record.blob.create().then(scalingEffort.success, scalingEffort.failure);\n                }\n                else {\n                    log(id + \" is not a scalable image!\", \"error\");\n                    scalingEffort.failure();\n                }\n            }, this)());\n        }\n\n        return scalingEffort;\n    },\n\n    // NOTE: We cannot reliably determine at this time if the UA supports a specific MIME type for the target format.\n    // image/jpeg and image/png are the only safe choices at this time.\n    _determineOutputType: function(spec) {\n        \"use strict\";\n\n        var requestedType = spec.requestedType,\n            defaultType = spec.defaultType,\n            referenceType = spec.refType;\n\n        // If a default type and requested type have not been specified, this should be a\n        // JPEG if the original type is a JPEG, otherwise, a PNG.\n        if (!defaultType && !requestedType) {\n            if (referenceType !== \"image/jpeg\") {\n                return \"image/png\";\n            }\n            return referenceType;\n        }\n\n        // A specified default type is used when a requested type is not specified.\n        if (!requestedType) {\n            return defaultType;\n        }\n\n        // If requested type is specified, use it, as long as this recognized type is supported by the current UA\n        if (qq.indexOf(Object.keys(qq.Identify.prototype.PREVIEWABLE_MIME_TYPES), requestedType) >= 0) {\n            if (requestedType === \"image/tiff\") {\n                return qq.supportedFeatures.tiffPreviews ? requestedType : defaultType;\n            }\n\n            return requestedType;\n        }\n\n        return defaultType;\n    },\n\n    // Get a file name for a generated scaled file record, based on the provided scaled image description\n    _getName: function(originalName, scaledVersionProperties) {\n        \"use strict\";\n\n        var startOfExt = originalName.lastIndexOf(\".\"),\n            versionType = scaledVersionProperties.type || \"image/png\",\n            referenceType = scaledVersionProperties.refType,\n            scaledName = \"\",\n            scaledExt = qq.getExtension(originalName),\n            nameAppendage = \"\";\n\n        if (scaledVersionProperties.name && scaledVersionProperties.name.trim().length) {\n            nameAppendage = \" (\" + scaledVersionProperties.name + \")\";\n        }\n\n        if (startOfExt >= 0) {\n            scaledName = originalName.substr(0, startOfExt);\n\n            if (referenceType !== versionType) {\n                scaledExt = versionType.split(\"/\")[1];\n            }\n\n            scaledName += nameAppendage + \".\" + scaledExt;\n        }\n        else {\n            scaledName = originalName + nameAppendage;\n        }\n\n        return scaledName;\n    },\n\n    // We want the smallest scaled file to be uploaded first\n    _getSortedSizes: function(sizes) {\n        \"use strict\";\n\n        sizes = qq.extend([], sizes);\n\n        return sizes.sort(function(a, b) {\n            if (a.maxSize > b.maxSize) {\n                return 1;\n            }\n            if (a.maxSize < b.maxSize) {\n                return -1;\n            }\n            return 0;\n        });\n    },\n\n    _generateScaledImage: function(spec, sourceFile) {\n        \"use strict\";\n\n        var self = this,\n            customResizeFunction = spec.customResizeFunction,\n            log = spec.log,\n            maxSize = spec.maxSize,\n            orient = spec.orient,\n            type = spec.type,\n            quality = spec.quality,\n            failedText = spec.failedText,\n            includeExif = spec.includeExif && sourceFile.type === \"image/jpeg\" && type === \"image/jpeg\",\n            scalingEffort = new qq.Promise(),\n            imageGenerator = new qq.ImageGenerator(log),\n            canvas = document.createElement(\"canvas\");\n\n        log(\"Attempting to generate scaled version for \" + sourceFile.name);\n\n        imageGenerator.generate(sourceFile, canvas, {maxSize: maxSize, orient: orient, customResizeFunction: customResizeFunction}).then(function() {\n            var scaledImageDataUri = canvas.toDataURL(type, quality),\n                signalSuccess = function() {\n                    log(\"Success generating scaled version for \" + sourceFile.name);\n                    var blob = qq.dataUriToBlob(scaledImageDataUri);\n                    scalingEffort.success(blob);\n                };\n\n            if (includeExif) {\n                self._insertExifHeader(sourceFile, scaledImageDataUri, log).then(function(scaledImageDataUriWithExif) {\n                    scaledImageDataUri = scaledImageDataUriWithExif;\n                    signalSuccess();\n                },\n                function() {\n                    log(\"Problem inserting EXIF header into scaled image.  Using scaled image w/out EXIF data.\", \"error\");\n                    signalSuccess();\n                });\n            }\n            else {\n                signalSuccess();\n            }\n        }, function() {\n            log(\"Failed attempt to generate scaled version for \" + sourceFile.name, \"error\");\n            scalingEffort.failure(failedText);\n        });\n\n        return scalingEffort;\n    },\n\n    // Attempt to insert the original image's EXIF header into a scaled version.\n    _insertExifHeader: function(originalImage, scaledImageDataUri, log) {\n        \"use strict\";\n\n        var reader = new FileReader(),\n            insertionEffort = new qq.Promise(),\n            originalImageDataUri = \"\";\n\n        reader.onload = function() {\n            originalImageDataUri = reader.result;\n            insertionEffort.success(qq.ExifRestorer.restore(originalImageDataUri, scaledImageDataUri));\n        };\n\n        reader.onerror = function() {\n            log(\"Problem reading \" + originalImage.name + \" during attempt to transfer EXIF data to scaled version.\", \"error\");\n            insertionEffort.failure();\n        };\n\n        reader.readAsDataURL(originalImage);\n\n        return insertionEffort;\n    },\n\n    _dataUriToBlob: function(dataUri) {\n        \"use strict\";\n\n        var byteString, mimeString, arrayBuffer, intArray;\n\n        // convert base64 to raw binary data held in a string\n        if (dataUri.split(\",\")[0].indexOf(\"base64\") >= 0) {\n            byteString = atob(dataUri.split(\",\")[1]);\n        }\n        else {\n            byteString = decodeURI(dataUri.split(\",\")[1]);\n        }\n\n        // extract the MIME\n        mimeString = dataUri.split(\",\")[0]\n            .split(\":\")[1]\n            .split(\";\")[0];\n\n        // write the bytes of the binary string to an ArrayBuffer\n        arrayBuffer = new ArrayBuffer(byteString.length);\n        intArray = new Uint8Array(arrayBuffer);\n        qq.each(byteString, function(idx, character) {\n            intArray[idx] = character.charCodeAt(0);\n        });\n\n        return this._createBlob(arrayBuffer, mimeString);\n    },\n\n    _createBlob: function(data, mime) {\n        \"use strict\";\n\n        var BlobBuilder = window.BlobBuilder ||\n                window.WebKitBlobBuilder ||\n                window.MozBlobBuilder ||\n                window.MSBlobBuilder,\n            blobBuilder = BlobBuilder && new BlobBuilder();\n\n        if (blobBuilder) {\n            blobBuilder.append(data);\n            return blobBuilder.getBlob(mime);\n        }\n        else {\n            return new Blob([data], {type: mime});\n        }\n    }\n});\n","//Based on MinifyJpeg\n//http://elicon.blog57.fc2.com/blog-entry-206.html\n\nqq.ExifRestorer = (function()\n{\n   \n\tvar ExifRestorer = {};\n\t \n    ExifRestorer.KEY_STR = \"ABCDEFGHIJKLMNOP\" +\n                         \"QRSTUVWXYZabcdef\" +\n                         \"ghijklmnopqrstuv\" +\n                         \"wxyz0123456789+/\" +\n                         \"=\";\n\n    ExifRestorer.encode64 = function(input)\n    {\n        var output = \"\",\n            chr1, chr2, chr3 = \"\",\n            enc1, enc2, enc3, enc4 = \"\",\n            i = 0;\n\n        do {\n            chr1 = input[i++];\n            chr2 = input[i++];\n            chr3 = input[i++];\n\n            enc1 = chr1 >> 2;\n            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);\n            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);\n            enc4 = chr3 & 63;\n\n            if (isNaN(chr2)) {\n               enc3 = enc4 = 64;\n            } else if (isNaN(chr3)) {\n               enc4 = 64;\n            }\n\n            output = output +\n               this.KEY_STR.charAt(enc1) +\n               this.KEY_STR.charAt(enc2) +\n               this.KEY_STR.charAt(enc3) +\n               this.KEY_STR.charAt(enc4);\n            chr1 = chr2 = chr3 = \"\";\n            enc1 = enc2 = enc3 = enc4 = \"\";\n        } while (i < input.length);\n\n        return output;\n    };\n    \n    ExifRestorer.restore = function(origFileBase64, resizedFileBase64)\n    {\n        var expectedBase64Header = \"data:image/jpeg;base64,\";\n\n        if (!origFileBase64.match(expectedBase64Header))\n        {\n        \treturn resizedFileBase64;\n        }       \n        \n        var rawImage = this.decode64(origFileBase64.replace(expectedBase64Header, \"\"));\n        var segments = this.slice2Segments(rawImage);\n                \n        var image = this.exifManipulation(resizedFileBase64, segments);\n        \n        return expectedBase64Header + this.encode64(image);\n        \n    };\n\n\n    ExifRestorer.exifManipulation = function(resizedFileBase64, segments)\n    {\n            var exifArray = this.getExifArray(segments),\n                newImageArray = this.insertExif(resizedFileBase64, exifArray),\n                aBuffer = new Uint8Array(newImageArray);\n\n            return aBuffer;\n    };\n\n\n    ExifRestorer.getExifArray = function(segments)\n    {\n            var seg;\n            for (var x = 0; x < segments.length; x++)\n            {\n                seg = segments[x];\n                if (seg[0] == 255 & seg[1] == 225) //(ff e1)\n                {\n                    return seg;\n                }\n            }\n            return [];\n    };\n\n\n    ExifRestorer.insertExif = function(resizedFileBase64, exifArray)\n    {\n            var imageData = resizedFileBase64.replace(\"data:image/jpeg;base64,\", \"\"),\n                buf = this.decode64(imageData),\n                separatePoint = buf.indexOf(255,3),\n                mae = buf.slice(0, separatePoint),\n                ato = buf.slice(separatePoint),\n                array = mae;\n\n            array = array.concat(exifArray);\n            array = array.concat(ato);\n           return array;\n    };\n\n\n    \n    ExifRestorer.slice2Segments = function(rawImageArray)\n    {\n        var head = 0,\n            segments = [];\n\n        while (1)\n        {\n            if (rawImageArray[head] == 255 & rawImageArray[head + 1] == 218){break;}\n            if (rawImageArray[head] == 255 & rawImageArray[head + 1] == 216)\n            {\n                head += 2;\n            }\n            else\n            {\n                var length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3],\n                    endPoint = head + length + 2,\n                    seg = rawImageArray.slice(head, endPoint);\n                segments.push(seg);\n                head = endPoint;\n            }\n            if (head > rawImageArray.length){break;}\n        }\n\n        return segments;\n    };\n\n\n    \n    ExifRestorer.decode64 = function(input) \n    {\n        var output = \"\",\n            chr1, chr2, chr3 = \"\",\n            enc1, enc2, enc3, enc4 = \"\",\n            i = 0,\n            buf = [];\n\n        // remove all characters that are not A-Z, a-z, 0-9, +, /, or =\n        var base64test = /[^A-Za-z0-9\\+\\/\\=]/g;\n        if (base64test.exec(input)) {\n            throw new Error(\"There were invalid base64 characters in the input text.  \" +\n                \"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\");\n        }\n        input = input.replace(/[^A-Za-z0-9\\+\\/\\=]/g, \"\");\n\n        do {\n            enc1 = this.KEY_STR.indexOf(input.charAt(i++));\n            enc2 = this.KEY_STR.indexOf(input.charAt(i++));\n            enc3 = this.KEY_STR.indexOf(input.charAt(i++));\n            enc4 = this.KEY_STR.indexOf(input.charAt(i++));\n\n            chr1 = (enc1 << 2) | (enc2 >> 4);\n            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);\n            chr3 = ((enc3 & 3) << 6) | enc4;\n\n            buf.push(chr1);\n\n            if (enc3 != 64) {\n               buf.push(chr2);\n            }\n            if (enc4 != 64) {\n               buf.push(chr3);\n            }\n\n            chr1 = chr2 = chr3 = \"\";\n            enc1 = enc2 = enc3 = enc4 = \"\";\n\n        } while (i < input.length);\n\n        return buf;\n    };\n\n    \n    return ExifRestorer;\n})();\n","/* globals qq */\n/**\n * Keeps a running tally of total upload progress for a batch of files.\n *\n * @param callback Invoked when total progress changes, passing calculated total loaded & total size values.\n * @param getSize Function that returns the size of a file given its ID\n * @constructor\n */\nqq.TotalProgress = function(callback, getSize) {\n    \"use strict\";\n\n    var perFileProgress = {},\n        totalLoaded = 0,\n        totalSize = 0,\n\n        lastLoadedSent = -1,\n        lastTotalSent = -1,\n        callbackProxy = function(loaded, total) {\n            if (loaded !== lastLoadedSent || total !== lastTotalSent) {\n                callback(loaded, total);\n            }\n\n            lastLoadedSent = loaded;\n            lastTotalSent = total;\n        },\n\n        /**\n         * @param failed Array of file IDs that have failed\n         * @param retryable Array of file IDs that are retryable\n         * @returns true if none of the failed files are eligible for retry\n         */\n        noRetryableFiles = function(failed, retryable) {\n            var none = true;\n\n            qq.each(failed, function(idx, failedId) {\n                if (qq.indexOf(retryable, failedId) >= 0) {\n                    none = false;\n                    return false;\n                }\n            });\n\n            return none;\n        },\n\n        onCancel = function(id) {\n            updateTotalProgress(id, -1, -1);\n            delete perFileProgress[id];\n        },\n\n        onAllComplete = function(successful, failed, retryable) {\n            if (failed.length === 0 || noRetryableFiles(failed, retryable)) {\n                callbackProxy(totalSize, totalSize);\n                this.reset();\n            }\n        },\n\n        onNew = function(id) {\n            var size = getSize(id);\n\n            // We might not know the size yet, such as for blob proxies\n            if (size > 0) {\n                updateTotalProgress(id, 0, size);\n                perFileProgress[id] = {loaded: 0, total: size};\n            }\n        },\n\n        /**\n         * Invokes the callback with the current total progress of all files in the batch.  Called whenever it may\n         * be appropriate to re-calculate and disseminate this data.\n         *\n         * @param id ID of a file that has changed in some important way\n         * @param newLoaded New loaded value for this file.  -1 if this value should no longer be part of calculations\n         * @param newTotal New total size of the file.  -1 if this value should no longer be part of calculations\n         */\n        updateTotalProgress = function(id, newLoaded, newTotal) {\n            var oldLoaded = perFileProgress[id] ? perFileProgress[id].loaded : 0,\n                oldTotal = perFileProgress[id] ? perFileProgress[id].total : 0;\n\n            if (newLoaded === -1 && newTotal === -1) {\n                totalLoaded -= oldLoaded;\n                totalSize -= oldTotal;\n            }\n            else {\n                if (newLoaded) {\n                    totalLoaded += newLoaded - oldLoaded;\n                }\n                if (newTotal) {\n                    totalSize += newTotal - oldTotal;\n                }\n            }\n\n            callbackProxy(totalLoaded, totalSize);\n        };\n\n    qq.extend(this, {\n        // Called when a batch of files has completed uploading.\n        onAllComplete: onAllComplete,\n\n        // Called when the status of a file has changed.\n        onStatusChange: function(id, oldStatus, newStatus) {\n            if (newStatus === qq.status.CANCELED || newStatus === qq.status.REJECTED) {\n                onCancel(id);\n            }\n            else if (newStatus === qq.status.SUBMITTING) {\n                onNew(id);\n            }\n        },\n\n        // Called whenever the upload progress of an individual file has changed.\n        onIndividualProgress: function(id, loaded, total) {\n            updateTotalProgress(id, loaded, total);\n            perFileProgress[id] = {loaded: loaded, total: total};\n        },\n\n        // Called whenever the total size of a file has changed, such as when the size of a generated blob is known.\n        onNewSize: function(id) {\n            onNew(id);\n        },\n\n        reset: function() {\n            perFileProgress = {};\n            totalLoaded = 0;\n            totalSize = 0;\n        }\n    });\n};\n","/*globals qq*/\nqq.PasteSupport = function(o) {\n    \"use strict\";\n\n    var options, detachPasteHandler;\n\n    options = {\n        targetElement: null,\n        callbacks: {\n            log: function(message, level) {},\n            pasteReceived: function(blob) {}\n        }\n    };\n\n    function isImage(item) {\n        return item.type &&\n            item.type.indexOf(\"image/\") === 0;\n    }\n\n    function registerPasteHandler() {\n        detachPasteHandler = qq(options.targetElement).attach(\"paste\", function(event) {\n            var clipboardData = event.clipboardData;\n\n            if (clipboardData) {\n                qq.each(clipboardData.items, function(idx, item) {\n                    if (isImage(item)) {\n                        var blob = item.getAsFile();\n                        options.callbacks.pasteReceived(blob);\n                    }\n                });\n            }\n        });\n    }\n\n    function unregisterPasteHandler() {\n        if (detachPasteHandler) {\n            detachPasteHandler();\n        }\n    }\n\n    qq.extend(options, o);\n    registerPasteHandler();\n\n    qq.extend(this, {\n        reset: function() {\n            unregisterPasteHandler();\n        }\n    });\n};\n","/* globals qq */\n/**\n * Module that handles support for existing forms.\n *\n * @param options Options passed from the integrator-supplied options related to form support.\n * @param startUpload Callback to invoke when files \"stored\" should be uploaded.\n * @param log Proxy for the logger\n * @constructor\n */\nqq.FormSupport = function(options, startUpload, log) {\n    \"use strict\";\n    var self  = this,\n        interceptSubmit = options.interceptSubmit,\n        formEl = options.element,\n        autoUpload = options.autoUpload;\n\n    // Available on the public API associated with this module.\n    qq.extend(this, {\n        // To be used by the caller to determine if the endpoint will be determined by some processing\n        // that occurs in this module, such as if the form has an action attribute.\n        // Ignore if `attachToForm === false`.\n        newEndpoint: null,\n\n        // To be used by the caller to determine if auto uploading should be allowed.\n        // Ignore if `attachToForm === false`.\n        newAutoUpload: autoUpload,\n\n        // true if a form was detected and is being tracked by this module\n        attachedToForm: false,\n\n        // Returns an object with names and values for all valid form elements associated with the attached form.\n        getFormInputsAsObject: function() {\n            /* jshint eqnull:true */\n            if (formEl == null) {\n                return null;\n            }\n\n            return self._form2Obj(formEl);\n        }\n    });\n\n    // If the form contains an action attribute, this should be the new upload endpoint.\n    function determineNewEndpoint(formEl) {\n        if (formEl.getAttribute(\"action\")) {\n            self.newEndpoint = formEl.getAttribute(\"action\");\n        }\n    }\n\n    // Return true only if the form is valid, or if we cannot make this determination.\n    // If the form is invalid, ensure invalid field(s) are highlighted in the UI.\n    function validateForm(formEl, nativeSubmit) {\n        if (formEl.checkValidity && !formEl.checkValidity()) {\n            log(\"Form did not pass validation checks - will not upload.\", \"error\");\n            nativeSubmit();\n        }\n        else {\n            return true;\n        }\n    }\n\n    // Intercept form submit attempts, unless the integrator has told us not to do this.\n    function maybeUploadOnSubmit(formEl) {\n        var nativeSubmit = formEl.submit;\n\n        // Intercept and squelch submit events.\n        qq(formEl).attach(\"submit\", function(event) {\n            event = event || window.event;\n\n            if (event.preventDefault) {\n                event.preventDefault();\n            }\n            else {\n                event.returnValue = false;\n            }\n\n            validateForm(formEl, nativeSubmit) && startUpload();\n        });\n\n        // The form's `submit()` function may be called instead (i.e. via jQuery.submit()).\n        // Intercept that too.\n        formEl.submit = function() {\n            validateForm(formEl, nativeSubmit) && startUpload();\n        };\n    }\n\n    // If the element value passed from the uploader is a string, assume it is an element ID - select it.\n    // The rest of the code in this module depends on this being an HTMLElement.\n    function determineFormEl(formEl) {\n        if (formEl) {\n            if (qq.isString(formEl)) {\n                formEl = document.getElementById(formEl);\n            }\n\n            if (formEl) {\n                log(\"Attaching to form element.\");\n                determineNewEndpoint(formEl);\n                interceptSubmit && maybeUploadOnSubmit(formEl);\n            }\n        }\n\n        return formEl;\n    }\n\n    formEl = determineFormEl(formEl);\n    this.attachedToForm = !!formEl;\n};\n\nqq.extend(qq.FormSupport.prototype, {\n    // Converts all relevant form fields to key/value pairs.  This is meant to mimic the data a browser will\n    // construct from a given form when the form is submitted.\n    _form2Obj: function(form) {\n        \"use strict\";\n        var obj = {},\n            notIrrelevantType = function(type) {\n                var irrelevantTypes = [\n                    \"button\",\n                    \"image\",\n                    \"reset\",\n                    \"submit\"\n                ];\n\n                return qq.indexOf(irrelevantTypes, type.toLowerCase()) < 0;\n            },\n            radioOrCheckbox = function(type) {\n                return qq.indexOf([\"checkbox\", \"radio\"], type.toLowerCase()) >= 0;\n            },\n            ignoreValue = function(el) {\n                if (radioOrCheckbox(el.type) && !el.checked) {\n                    return true;\n                }\n\n                return el.disabled && el.type.toLowerCase() !== \"hidden\";\n            },\n            selectValue = function(select) {\n                var value = null;\n\n                qq.each(qq(select).children(), function(idx, child) {\n                    if (child.tagName.toLowerCase() === \"option\" && child.selected) {\n                        value = child.value;\n                        return false;\n                    }\n                });\n\n                return value;\n            };\n\n        qq.each(form.elements, function(idx, el) {\n            if ((qq.isInput(el, true) || el.tagName.toLowerCase() === \"textarea\") &&\n                notIrrelevantType(el.type) &&\n                !ignoreValue(el)) {\n\n                obj[el.name] = el.value;\n            }\n            else if (el.tagName.toLowerCase() === \"select\" && !ignoreValue(el)) {\n                var value = selectValue(el);\n\n                if (value !== null) {\n                    obj[el.name] = value;\n                }\n            }\n        });\n\n        return obj;\n    }\n});\n","/*\nCryptoJS v3.1.2\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n/**\n * CryptoJS core components.\n */\nqq.CryptoJS = (function (Math, undefined) {\n    /**\n     * CryptoJS namespace.\n     */\n    var C = {};\n\n    /**\n     * Library namespace.\n     */\n    var C_lib = C.lib = {};\n\n    /**\n     * Base object for prototypal inheritance.\n     */\n    var Base = C_lib.Base = (function () {\n        function F() {}\n\n        return {\n            /**\n             * Creates a new object that inherits from this object.\n             *\n             * @param {Object} overrides Properties to copy into the new object.\n             *\n             * @return {Object} The new object.\n             *\n             * @static\n             *\n             * @example\n             *\n             *     var MyType = CryptoJS.lib.Base.extend({\n             *         field: 'value',\n             *\n             *         method: function () {\n             *         }\n             *     });\n             */\n            extend: function (overrides) {\n                // Spawn\n                F.prototype = this;\n                var subtype = new F();\n\n                // Augment\n                if (overrides) {\n                    subtype.mixIn(overrides);\n                }\n\n                // Create default initializer\n                if (!subtype.hasOwnProperty('init')) {\n                    subtype.init = function () {\n                        subtype.$super.init.apply(this, arguments);\n                    };\n                }\n\n                // Initializer's prototype is the subtype object\n                subtype.init.prototype = subtype;\n\n                // Reference supertype\n                subtype.$super = this;\n\n                return subtype;\n            },\n\n            /**\n             * Extends this object and runs the init method.\n             * Arguments to create() will be passed to init().\n             *\n             * @return {Object} The new object.\n             *\n             * @static\n             *\n             * @example\n             *\n             *     var instance = MyType.create();\n             */\n            create: function () {\n                var instance = this.extend();\n                instance.init.apply(instance, arguments);\n\n                return instance;\n            },\n\n            /**\n             * Initializes a newly created object.\n             * Override this method to add some logic when your objects are created.\n             *\n             * @example\n             *\n             *     var MyType = CryptoJS.lib.Base.extend({\n             *         init: function () {\n             *             // ...\n             *         }\n             *     });\n             */\n            init: function () {\n            },\n\n            /**\n             * Copies properties into this object.\n             *\n             * @param {Object} properties The properties to mix in.\n             *\n             * @example\n             *\n             *     MyType.mixIn({\n             *         field: 'value'\n             *     });\n             */\n            mixIn: function (properties) {\n                for (var propertyName in properties) {\n                    if (properties.hasOwnProperty(propertyName)) {\n                        this[propertyName] = properties[propertyName];\n                    }\n                }\n\n                // IE won't copy toString using the loop above\n                if (properties.hasOwnProperty('toString')) {\n                    this.toString = properties.toString;\n                }\n            },\n\n            /**\n             * Creates a copy of this object.\n             *\n             * @return {Object} The clone.\n             *\n             * @example\n             *\n             *     var clone = instance.clone();\n             */\n            clone: function () {\n                return this.init.prototype.extend(this);\n            }\n        };\n    }());\n\n    /**\n     * An array of 32-bit words.\n     *\n     * @property {Array} words The array of 32-bit words.\n     * @property {number} sigBytes The number of significant bytes in this word array.\n     */\n    var WordArray = C_lib.WordArray = Base.extend({\n        /**\n         * Initializes a newly created word array.\n         *\n         * @param {Array} words (Optional) An array of 32-bit words.\n         * @param {number} sigBytes (Optional) The number of significant bytes in the words.\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.lib.WordArray.create();\n         *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);\n         *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);\n         */\n        init: function (words, sigBytes) {\n            words = this.words = words || [];\n\n            if (sigBytes != undefined) {\n                this.sigBytes = sigBytes;\n            } else {\n                this.sigBytes = words.length * 4;\n            }\n        },\n\n        /**\n         * Converts this word array to a string.\n         *\n         * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex\n         *\n         * @return {string} The stringified word array.\n         *\n         * @example\n         *\n         *     var string = wordArray + '';\n         *     var string = wordArray.toString();\n         *     var string = wordArray.toString(CryptoJS.enc.Utf8);\n         */\n        toString: function (encoder) {\n            return (encoder || Hex).stringify(this);\n        },\n\n        /**\n         * Concatenates a word array to this word array.\n         *\n         * @param {WordArray} wordArray The word array to append.\n         *\n         * @return {WordArray} This word array.\n         *\n         * @example\n         *\n         *     wordArray1.concat(wordArray2);\n         */\n        concat: function (wordArray) {\n            // Shortcuts\n            var thisWords = this.words;\n            var thatWords = wordArray.words;\n            var thisSigBytes = this.sigBytes;\n            var thatSigBytes = wordArray.sigBytes;\n\n            // Clamp excess bits\n            this.clamp();\n\n            // Concat\n            if (thisSigBytes % 4) {\n                // Copy one byte at a time\n                for (var i = 0; i < thatSigBytes; i++) {\n                    var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\n                    thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);\n                }\n            } else if (thatWords.length > 0xffff) {\n                // Copy one word at a time\n                for (var i = 0; i < thatSigBytes; i += 4) {\n                    thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];\n                }\n            } else {\n                // Copy all words at once\n                thisWords.push.apply(thisWords, thatWords);\n            }\n            this.sigBytes += thatSigBytes;\n\n            // Chainable\n            return this;\n        },\n\n        /**\n         * Removes insignificant bits.\n         *\n         * @example\n         *\n         *     wordArray.clamp();\n         */\n        clamp: function () {\n            // Shortcuts\n            var words = this.words;\n            var sigBytes = this.sigBytes;\n\n            // Clamp\n            words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);\n            words.length = Math.ceil(sigBytes / 4);\n        },\n\n        /**\n         * Creates a copy of this word array.\n         *\n         * @return {WordArray} The clone.\n         *\n         * @example\n         *\n         *     var clone = wordArray.clone();\n         */\n        clone: function () {\n            var clone = Base.clone.call(this);\n            clone.words = this.words.slice(0);\n\n            return clone;\n        },\n\n        /**\n         * Creates a word array filled with random bytes.\n         *\n         * @param {number} nBytes The number of random bytes to generate.\n         *\n         * @return {WordArray} The random word array.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.lib.WordArray.random(16);\n         */\n        random: function (nBytes) {\n            var words = [];\n            for (var i = 0; i < nBytes; i += 4) {\n                words.push((Math.random() * 0x100000000) | 0);\n            }\n\n            return new WordArray.init(words, nBytes);\n        }\n    });\n\n    /**\n     * Encoder namespace.\n     */\n    var C_enc = C.enc = {};\n\n    /**\n     * Hex encoding strategy.\n     */\n    var Hex = C_enc.Hex = {\n        /**\n         * Converts a word array to a hex string.\n         *\n         * @param {WordArray} wordArray The word array.\n         *\n         * @return {string} The hex string.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var hexString = CryptoJS.enc.Hex.stringify(wordArray);\n         */\n        stringify: function (wordArray) {\n            // Shortcuts\n            var words = wordArray.words;\n            var sigBytes = wordArray.sigBytes;\n\n            // Convert\n            var hexChars = [];\n            for (var i = 0; i < sigBytes; i++) {\n                var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\n                hexChars.push((bite >>> 4).toString(16));\n                hexChars.push((bite & 0x0f).toString(16));\n            }\n\n            return hexChars.join('');\n        },\n\n        /**\n         * Converts a hex string to a word array.\n         *\n         * @param {string} hexStr The hex string.\n         *\n         * @return {WordArray} The word array.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.enc.Hex.parse(hexString);\n         */\n        parse: function (hexStr) {\n            // Shortcut\n            var hexStrLength = hexStr.length;\n\n            // Convert\n            var words = [];\n            for (var i = 0; i < hexStrLength; i += 2) {\n                words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);\n            }\n\n            return new WordArray.init(words, hexStrLength / 2);\n        }\n    };\n\n    /**\n     * Latin1 encoding strategy.\n     */\n    var Latin1 = C_enc.Latin1 = {\n        /**\n         * Converts a word array to a Latin1 string.\n         *\n         * @param {WordArray} wordArray The word array.\n         *\n         * @return {string} The Latin1 string.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);\n         */\n        stringify: function (wordArray) {\n            // Shortcuts\n            var words = wordArray.words;\n            var sigBytes = wordArray.sigBytes;\n\n            // Convert\n            var latin1Chars = [];\n            for (var i = 0; i < sigBytes; i++) {\n                var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;\n                latin1Chars.push(String.fromCharCode(bite));\n            }\n\n            return latin1Chars.join('');\n        },\n\n        /**\n         * Converts a Latin1 string to a word array.\n         *\n         * @param {string} latin1Str The Latin1 string.\n         *\n         * @return {WordArray} The word array.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.enc.Latin1.parse(latin1String);\n         */\n        parse: function (latin1Str) {\n            // Shortcut\n            var latin1StrLength = latin1Str.length;\n\n            // Convert\n            var words = [];\n            for (var i = 0; i < latin1StrLength; i++) {\n                words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);\n            }\n\n            return new WordArray.init(words, latin1StrLength);\n        }\n    };\n\n    /**\n     * UTF-8 encoding strategy.\n     */\n    var Utf8 = C_enc.Utf8 = {\n        /**\n         * Converts a word array to a UTF-8 string.\n         *\n         * @param {WordArray} wordArray The word array.\n         *\n         * @return {string} The UTF-8 string.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);\n         */\n        stringify: function (wordArray) {\n            try {\n                return decodeURIComponent(escape(Latin1.stringify(wordArray)));\n            } catch (e) {\n                throw new Error('Malformed UTF-8 data');\n            }\n        },\n\n        /**\n         * Converts a UTF-8 string to a word array.\n         *\n         * @param {string} utf8Str The UTF-8 string.\n         *\n         * @return {WordArray} The word array.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.enc.Utf8.parse(utf8String);\n         */\n        parse: function (utf8Str) {\n            return Latin1.parse(unescape(encodeURIComponent(utf8Str)));\n        }\n    };\n\n    /**\n     * Abstract buffered block algorithm template.\n     *\n     * The property blockSize must be implemented in a concrete subtype.\n     *\n     * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0\n     */\n    var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({\n        /**\n         * Resets this block algorithm's data buffer to its initial state.\n         *\n         * @example\n         *\n         *     bufferedBlockAlgorithm.reset();\n         */\n        reset: function () {\n            // Initial values\n            this._data = new WordArray.init();\n            this._nDataBytes = 0;\n        },\n\n        /**\n         * Adds new data to this block algorithm's buffer.\n         *\n         * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.\n         *\n         * @example\n         *\n         *     bufferedBlockAlgorithm._append('data');\n         *     bufferedBlockAlgorithm._append(wordArray);\n         */\n        _append: function (data) {\n            // Convert string to WordArray, else assume WordArray already\n            if (typeof data == 'string') {\n                data = Utf8.parse(data);\n            }\n\n            // Append\n            this._data.concat(data);\n            this._nDataBytes += data.sigBytes;\n        },\n\n        /**\n         * Processes available data blocks.\n         *\n         * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.\n         *\n         * @param {boolean} doFlush Whether all blocks and partial blocks should be processed.\n         *\n         * @return {WordArray} The processed data.\n         *\n         * @example\n         *\n         *     var processedData = bufferedBlockAlgorithm._process();\n         *     var processedData = bufferedBlockAlgorithm._process(!!'flush');\n         */\n        _process: function (doFlush) {\n            // Shortcuts\n            var data = this._data;\n            var dataWords = data.words;\n            var dataSigBytes = data.sigBytes;\n            var blockSize = this.blockSize;\n            var blockSizeBytes = blockSize * 4;\n\n            // Count blocks ready\n            var nBlocksReady = dataSigBytes / blockSizeBytes;\n            if (doFlush) {\n                // Round up to include partial blocks\n                nBlocksReady = Math.ceil(nBlocksReady);\n            } else {\n                // Round down to include only full blocks,\n                // less the number of blocks that must remain in the buffer\n                nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);\n            }\n\n            // Count words ready\n            var nWordsReady = nBlocksReady * blockSize;\n\n            // Count bytes ready\n            var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);\n\n            // Process blocks\n            if (nWordsReady) {\n                for (var offset = 0; offset < nWordsReady; offset += blockSize) {\n                    // Perform concrete-algorithm logic\n                    this._doProcessBlock(dataWords, offset);\n                }\n\n                // Remove processed words\n                var processedWords = dataWords.splice(0, nWordsReady);\n                data.sigBytes -= nBytesReady;\n            }\n\n            // Return processed words\n            return new WordArray.init(processedWords, nBytesReady);\n        },\n\n        /**\n         * Creates a copy of this object.\n         *\n         * @return {Object} The clone.\n         *\n         * @example\n         *\n         *     var clone = bufferedBlockAlgorithm.clone();\n         */\n        clone: function () {\n            var clone = Base.clone.call(this);\n            clone._data = this._data.clone();\n\n            return clone;\n        },\n\n        _minBufferSize: 0\n    });\n\n    /**\n     * Abstract hasher template.\n     *\n     * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)\n     */\n    var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({\n        /**\n         * Configuration options.\n         */\n        cfg: Base.extend(),\n\n        /**\n         * Initializes a newly created hasher.\n         *\n         * @param {Object} cfg (Optional) The configuration options to use for this hash computation.\n         *\n         * @example\n         *\n         *     var hasher = CryptoJS.algo.SHA256.create();\n         */\n        init: function (cfg) {\n            // Apply config defaults\n            this.cfg = this.cfg.extend(cfg);\n\n            // Set initial values\n            this.reset();\n        },\n\n        /**\n         * Resets this hasher to its initial state.\n         *\n         * @example\n         *\n         *     hasher.reset();\n         */\n        reset: function () {\n            // Reset data buffer\n            BufferedBlockAlgorithm.reset.call(this);\n\n            // Perform concrete-hasher logic\n            this._doReset();\n        },\n\n        /**\n         * Updates this hasher with a message.\n         *\n         * @param {WordArray|string} messageUpdate The message to append.\n         *\n         * @return {Hasher} This hasher.\n         *\n         * @example\n         *\n         *     hasher.update('message');\n         *     hasher.update(wordArray);\n         */\n        update: function (messageUpdate) {\n            // Append\n            this._append(messageUpdate);\n\n            // Update the hash\n            this._process();\n\n            // Chainable\n            return this;\n        },\n\n        /**\n         * Finalizes the hash computation.\n         * Note that the finalize operation is effectively a destructive, read-once operation.\n         *\n         * @param {WordArray|string} messageUpdate (Optional) A final message update.\n         *\n         * @return {WordArray} The hash.\n         *\n         * @example\n         *\n         *     var hash = hasher.finalize();\n         *     var hash = hasher.finalize('message');\n         *     var hash = hasher.finalize(wordArray);\n         */\n        finalize: function (messageUpdate) {\n            // Final message update\n            if (messageUpdate) {\n                this._append(messageUpdate);\n            }\n\n            // Perform concrete-hasher logic\n            var hash = this._doFinalize();\n\n            return hash;\n        },\n\n        blockSize: 512/32,\n\n        /**\n         * Creates a shortcut function to a hasher's object interface.\n         *\n         * @param {Hasher} hasher The hasher to create a helper for.\n         *\n         * @return {Function} The shortcut function.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);\n         */\n        _createHelper: function (hasher) {\n            return function (message, cfg) {\n                return new hasher.init(cfg).finalize(message);\n            };\n        },\n\n        /**\n         * Creates a shortcut function to the HMAC's object interface.\n         *\n         * @param {Hasher} hasher The hasher to use in this HMAC helper.\n         *\n         * @return {Function} The shortcut function.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);\n         */\n        _createHmacHelper: function (hasher) {\n            return function (message, key) {\n                return new C_algo.HMAC.init(hasher, key).finalize(message);\n            };\n        }\n    });\n\n    /**\n     * Algorithm namespace.\n     */\n    var C_algo = C.algo = {};\n\n    return C;\n}(Math));\n","/*\nCryptoJS v3.1.2\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    // Shortcuts\n    var C = qq.CryptoJS;\n    var C_lib = C.lib;\n    var WordArray = C_lib.WordArray;\n    var C_enc = C.enc;\n\n    /**\n     * Base64 encoding strategy.\n     */\n    var Base64 = C_enc.Base64 = {\n        /**\n         * Converts a word array to a Base64 string.\n         *\n         * @param {WordArray} wordArray The word array.\n         *\n         * @return {string} The Base64 string.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var base64String = CryptoJS.enc.Base64.stringify(wordArray);\n         */\n        stringify: function (wordArray) {\n            // Shortcuts\n            var words = wordArray.words;\n            var sigBytes = wordArray.sigBytes;\n            var map = this._map;\n\n            // Clamp excess bits\n            wordArray.clamp();\n\n            // Convert\n            var base64Chars = [];\n            for (var i = 0; i < sigBytes; i += 3) {\n                var byte1 = (words[i >>> 2]       >>> (24 - (i % 4) * 8))       & 0xff;\n                var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;\n                var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;\n\n                var triplet = (byte1 << 16) | (byte2 << 8) | byte3;\n\n                for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {\n                    base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));\n                }\n            }\n\n            // Add padding\n            var paddingChar = map.charAt(64);\n            if (paddingChar) {\n                while (base64Chars.length % 4) {\n                    base64Chars.push(paddingChar);\n                }\n            }\n\n            return base64Chars.join('');\n        },\n\n        /**\n         * Converts a Base64 string to a word array.\n         *\n         * @param {string} base64Str The Base64 string.\n         *\n         * @return {WordArray} The word array.\n         *\n         * @static\n         *\n         * @example\n         *\n         *     var wordArray = CryptoJS.enc.Base64.parse(base64String);\n         */\n        parse: function (base64Str) {\n            // Shortcuts\n            var base64StrLength = base64Str.length;\n            var map = this._map;\n\n            // Ignore padding\n            var paddingChar = map.charAt(64);\n            if (paddingChar) {\n                var paddingIndex = base64Str.indexOf(paddingChar);\n                if (paddingIndex != -1) {\n                    base64StrLength = paddingIndex;\n                }\n            }\n\n            // Convert\n            var words = [];\n            var nBytes = 0;\n            for (var i = 0; i < base64StrLength; i++) {\n                if (i % 4) {\n                    var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2);\n                    var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2);\n                    words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);\n                    nBytes++;\n                }\n            }\n\n            return WordArray.create(words, nBytes);\n        },\n\n        _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='\n    };\n}());\n","/*\nCryptoJS v3.1.2\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    // Shortcuts\n    var C = qq.CryptoJS;\n    var C_lib = C.lib;\n    var Base = C_lib.Base;\n    var C_enc = C.enc;\n    var Utf8 = C_enc.Utf8;\n    var C_algo = C.algo;\n\n    /**\n     * HMAC algorithm.\n     */\n    var HMAC = C_algo.HMAC = Base.extend({\n        /**\n         * Initializes a newly created HMAC.\n         *\n         * @param {Hasher} hasher The hash algorithm to use.\n         * @param {WordArray|string} key The secret key.\n         *\n         * @example\n         *\n         *     var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);\n         */\n        init: function (hasher, key) {\n            // Init hasher\n            hasher = this._hasher = new hasher.init();\n\n            // Convert string to WordArray, else assume WordArray already\n            if (typeof key == 'string') {\n                key = Utf8.parse(key);\n            }\n\n            // Shortcuts\n            var hasherBlockSize = hasher.blockSize;\n            var hasherBlockSizeBytes = hasherBlockSize * 4;\n\n            // Allow arbitrary length keys\n            if (key.sigBytes > hasherBlockSizeBytes) {\n                key = hasher.finalize(key);\n            }\n\n            // Clamp excess bits\n            key.clamp();\n\n            // Clone key for inner and outer pads\n            var oKey = this._oKey = key.clone();\n            var iKey = this._iKey = key.clone();\n\n            // Shortcuts\n            var oKeyWords = oKey.words;\n            var iKeyWords = iKey.words;\n\n            // XOR keys with pad constants\n            for (var i = 0; i < hasherBlockSize; i++) {\n                oKeyWords[i] ^= 0x5c5c5c5c;\n                iKeyWords[i] ^= 0x36363636;\n            }\n            oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;\n\n            // Set initial values\n            this.reset();\n        },\n\n        /**\n         * Resets this HMAC to its initial state.\n         *\n         * @example\n         *\n         *     hmacHasher.reset();\n         */\n        reset: function () {\n            // Shortcut\n            var hasher = this._hasher;\n\n            // Reset\n            hasher.reset();\n            hasher.update(this._iKey);\n        },\n\n        /**\n         * Updates this HMAC with a message.\n         *\n         * @param {WordArray|string} messageUpdate The message to append.\n         *\n         * @return {HMAC} This HMAC instance.\n         *\n         * @example\n         *\n         *     hmacHasher.update('message');\n         *     hmacHasher.update(wordArray);\n         */\n        update: function (messageUpdate) {\n            this._hasher.update(messageUpdate);\n\n            // Chainable\n            return this;\n        },\n\n        /**\n         * Finalizes the HMAC computation.\n         * Note that the finalize operation is effectively a destructive, read-once operation.\n         *\n         * @param {WordArray|string} messageUpdate (Optional) A final message update.\n         *\n         * @return {WordArray} The HMAC.\n         *\n         * @example\n         *\n         *     var hmac = hmacHasher.finalize();\n         *     var hmac = hmacHasher.finalize('message');\n         *     var hmac = hmacHasher.finalize(wordArray);\n         */\n        finalize: function (messageUpdate) {\n            // Shortcut\n            var hasher = this._hasher;\n\n            // Compute HMAC\n            var innerHash = hasher.finalize(messageUpdate);\n            hasher.reset();\n            var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));\n\n            return hmac;\n        }\n    });\n}());\n","/*\nCryptoJS v3.1.2\ncode.google.com/p/crypto-js\n(c) 2009-2013 by Jeff Mott. All rights reserved.\ncode.google.com/p/crypto-js/wiki/License\n*/\n(function () {\n    // Shortcuts\n    var C = qq.CryptoJS;\n    var C_lib = C.lib;\n    var WordArray = C_lib.WordArray;\n    var Hasher = C_lib.Hasher;\n    var C_algo = C.algo;\n\n    // Reusable object\n    var W = [];\n\n    /**\n     * SHA-1 hash algorithm.\n     */\n    var SHA1 = C_algo.SHA1 = Hasher.extend({\n        _doReset: function () {\n            this._hash = new WordArray.init([\n                0x67452301, 0xefcdab89,\n                0x98badcfe, 0x10325476,\n                0xc3d2e1f0\n            ]);\n        },\n\n        _doProcessBlock: function (M, offset) {\n            // Shortcut\n            var H = this._hash.words;\n\n            // Working variables\n            var a = H[0];\n            var b = H[1];\n            var c = H[2];\n            var d = H[3];\n            var e = H[4];\n\n            // Computation\n            for (var i = 0; i < 80; i++) {\n                if (i < 16) {\n                    W[i] = M[offset + i] | 0;\n                } else {\n                    var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];\n                    W[i] = (n << 1) | (n >>> 31);\n                }\n\n                var t = ((a << 5) | (a >>> 27)) + e + W[i];\n                if (i < 20) {\n                    t += ((b & c) | (~b & d)) + 0x5a827999;\n                } else if (i < 40) {\n                    t += (b ^ c ^ d) + 0x6ed9eba1;\n                } else if (i < 60) {\n                    t += ((b & c) | (b & d) | (c & d)) - 0x70e44324;\n                } else /* if (i < 80) */ {\n                    t += (b ^ c ^ d) - 0x359d3e2a;\n                }\n\n                e = d;\n                d = c;\n                c = (b << 30) | (b >>> 2);\n                b = a;\n                a = t;\n            }\n\n            // Intermediate hash value\n            H[0] = (H[0] + a) | 0;\n            H[1] = (H[1] + b) | 0;\n            H[2] = (H[2] + c) | 0;\n            H[3] = (H[3] + d) | 0;\n            H[4] = (H[4] + e) | 0;\n        },\n\n        _doFinalize: function () {\n            // Shortcuts\n            var data = this._data;\n            var dataWords = data.words;\n\n            var nBitsTotal = this._nDataBytes * 8;\n            var nBitsLeft = data.sigBytes * 8;\n\n            // Add padding\n            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);\n            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);\n            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;\n            data.sigBytes = dataWords.length * 4;\n\n            // Hash final blocks\n            this._process();\n\n            // Return final computed hash\n            return this._hash;\n        },\n\n        clone: function () {\n            var clone = Hasher.clone.call(this);\n            clone._hash = this._hash.clone();\n\n            return clone;\n        }\n    });\n\n    /**\n     * Shortcut function to the hasher's object interface.\n     *\n     * @param {WordArray|string} message The message to hash.\n     *\n     * @return {WordArray} The hash.\n     *\n     * @static\n     *\n     * @example\n     *\n     *     var hash = CryptoJS.SHA1('message');\n     *     var hash = CryptoJS.SHA1(wordArray);\n     */\n    C.SHA1 = Hasher._createHelper(SHA1);\n\n    /**\n     * Shortcut function to the HMAC's object interface.\n     *\n     * @param {WordArray|string} message The message to hash.\n     * @param {WordArray|string} key The secret key.\n     *\n     * @return {WordArray} The HMAC.\n     *\n     * @static\n     *\n     * @example\n     *\n     *     var hmac = CryptoJS.HmacSHA1(message, key);\n     */\n    C.HmacSHA1 = Hasher._createHmacHelper(SHA1);\n}());\n","/*\r\nCryptoJS v3.1.2\r\ncode.google.com/p/crypto-js\r\n(c) 2009-2013 by Jeff Mott. All rights reserved.\r\ncode.google.com/p/crypto-js/wiki/License\r\n*/\r\n(function (Math) {\r\n    // Shortcuts\r\n    var C = qq.CryptoJS;\r\n    var C_lib = C.lib;\r\n    var WordArray = C_lib.WordArray;\r\n    var Hasher = C_lib.Hasher;\r\n    var C_algo = C.algo;\r\n\r\n    // Initialization and round constants tables\r\n    var H = [];\r\n    var K = [];\r\n\r\n    // Compute constants\r\n    (function () {\r\n        function isPrime(n) {\r\n            var sqrtN = Math.sqrt(n);\r\n            for (var factor = 2; factor <= sqrtN; factor++) {\r\n                if (!(n % factor)) {\r\n                    return false;\r\n                }\r\n            }\r\n\r\n            return true;\r\n        }\r\n\r\n        function getFractionalBits(n) {\r\n            return ((n - (n | 0)) * 0x100000000) | 0;\r\n        }\r\n\r\n        var n = 2;\r\n        var nPrime = 0;\r\n        while (nPrime < 64) {\r\n            if (isPrime(n)) {\r\n                if (nPrime < 8) {\r\n                    H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));\r\n                }\r\n                K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));\r\n\r\n                nPrime++;\r\n            }\r\n\r\n            n++;\r\n        }\r\n    }());\r\n\r\n    // Reusable object\r\n    var W = [];\r\n\r\n    /**\r\n     * SHA-256 hash algorithm.\r\n     */\r\n    var SHA256 = C_algo.SHA256 = Hasher.extend({\r\n        _doReset: function () {\r\n            this._hash = new WordArray.init(H.slice(0));\r\n        },\r\n\r\n        _doProcessBlock: function (M, offset) {\r\n            // Shortcut\r\n            var H = this._hash.words;\r\n\r\n            // Working variables\r\n            var a = H[0];\r\n            var b = H[1];\r\n            var c = H[2];\r\n            var d = H[3];\r\n            var e = H[4];\r\n            var f = H[5];\r\n            var g = H[6];\r\n            var h = H[7];\r\n\r\n            // Computation\r\n            for (var i = 0; i < 64; i++) {\r\n                if (i < 16) {\r\n                    W[i] = M[offset + i] | 0;\r\n                } else {\r\n                    var gamma0x = W[i - 15];\r\n                    var gamma0  = ((gamma0x << 25) | (gamma0x >>> 7))  ^\r\n                                  ((gamma0x << 14) | (gamma0x >>> 18)) ^\r\n                                   (gamma0x >>> 3);\r\n\r\n                    var gamma1x = W[i - 2];\r\n                    var gamma1  = ((gamma1x << 15) | (gamma1x >>> 17)) ^\r\n                                  ((gamma1x << 13) | (gamma1x >>> 19)) ^\r\n                                   (gamma1x >>> 10);\r\n\r\n                    W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];\r\n                }\r\n\r\n                var ch  = (e & f) ^ (~e & g);\r\n                var maj = (a & b) ^ (a & c) ^ (b & c);\r\n\r\n                var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));\r\n                var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7)  | (e >>> 25));\r\n\r\n                var t1 = h + sigma1 + ch + K[i] + W[i];\r\n                var t2 = sigma0 + maj;\r\n\r\n                h = g;\r\n                g = f;\r\n                f = e;\r\n                e = (d + t1) | 0;\r\n                d = c;\r\n                c = b;\r\n                b = a;\r\n                a = (t1 + t2) | 0;\r\n            }\r\n\r\n            // Intermediate hash value\r\n            H[0] = (H[0] + a) | 0;\r\n            H[1] = (H[1] + b) | 0;\r\n            H[2] = (H[2] + c) | 0;\r\n            H[3] = (H[3] + d) | 0;\r\n            H[4] = (H[4] + e) | 0;\r\n            H[5] = (H[5] + f) | 0;\r\n            H[6] = (H[6] + g) | 0;\r\n            H[7] = (H[7] + h) | 0;\r\n        },\r\n\r\n        _doFinalize: function () {\r\n            // Shortcuts\r\n            var data = this._data;\r\n            var dataWords = data.words;\r\n\r\n            var nBitsTotal = this._nDataBytes * 8;\r\n            var nBitsLeft = data.sigBytes * 8;\r\n\r\n            // Add padding\r\n            dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);\r\n            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);\r\n            dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;\r\n            data.sigBytes = dataWords.length * 4;\r\n\r\n            // Hash final blocks\r\n            this._process();\r\n\r\n            // Return final computed hash\r\n            return this._hash;\r\n        },\r\n\r\n        clone: function () {\r\n            var clone = Hasher.clone.call(this);\r\n            clone._hash = this._hash.clone();\r\n\r\n            return clone;\r\n        }\r\n    });\r\n\r\n    /**\r\n     * Shortcut function to the hasher's object interface.\r\n     *\r\n     * @param {WordArray|string} message The message to hash.\r\n     *\r\n     * @return {WordArray} The hash.\r\n     *\r\n     * @static\r\n     *\r\n     * @example\r\n     *\r\n     *     var hash = CryptoJS.SHA256('message');\r\n     *     var hash = CryptoJS.SHA256(wordArray);\r\n     */\r\n    C.SHA256 = Hasher._createHelper(SHA256);\r\n\r\n    /**\r\n     * Shortcut function to the HMAC's object interface.\r\n     *\r\n     * @param {WordArray|string} message The message to hash.\r\n     * @param {WordArray|string} key The secret key.\r\n     *\r\n     * @return {WordArray} The HMAC.\r\n     *\r\n     * @static\r\n     *\r\n     * @example\r\n     *\r\n     *     var hmac = CryptoJS.HmacSHA256(message, key);\r\n     */\r\n    C.HmacSHA256 = Hasher._createHmacHelper(SHA256);\r\n}(Math));\r\n","/*\r\nCryptoJS v3.1.2\r\ncode.google.com/p/crypto-js\r\n(c) 2009-2013 by Jeff Mott. All rights reserved.\r\ncode.google.com/p/crypto-js/wiki/License\r\n*/\r\n(function () {\r\n    // Check if typed arrays are supported\r\n    if (typeof ArrayBuffer != 'function') {\r\n        return;\r\n    }\r\n\r\n    // Shortcuts\r\n    var C = qq.CryptoJS;\r\n    var C_lib = C.lib;\r\n    var WordArray = C_lib.WordArray;\r\n\r\n    // Reference original init\r\n    var superInit = WordArray.init;\r\n\r\n    // Augment WordArray.init to handle typed arrays\r\n    var subInit = WordArray.init = function (typedArray) {\r\n        // Convert buffers to uint8\r\n        if (typedArray instanceof ArrayBuffer) {\r\n            typedArray = new Uint8Array(typedArray);\r\n        }\r\n\r\n        // Convert other array views to uint8\r\n        if (\r\n            typedArray instanceof Int8Array ||\r\n            typedArray instanceof Uint8ClampedArray ||\r\n            typedArray instanceof Int16Array ||\r\n            typedArray instanceof Uint16Array ||\r\n            typedArray instanceof Int32Array ||\r\n            typedArray instanceof Uint32Array ||\r\n            typedArray instanceof Float32Array ||\r\n            typedArray instanceof Float64Array\r\n        ) {\r\n            typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);\r\n        }\r\n\r\n        // Handle Uint8Array\r\n        if (typedArray instanceof Uint8Array) {\r\n            // Shortcut\r\n            var typedArrayByteLength = typedArray.byteLength;\r\n\r\n            // Extract bytes\r\n            var words = [];\r\n            for (var i = 0; i < typedArrayByteLength; i++) {\r\n                words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8);\r\n            }\r\n\r\n            // Initialize this word array\r\n            superInit.call(this, words, typedArrayByteLength);\r\n        } else {\r\n            // Else call normal init\r\n            superInit.apply(this, arguments);\r\n        }\r\n    };\r\n\r\n    subInit.prototype = WordArray;\r\n}());\r\n","/*globals qq */\nqq.s3 = qq.s3 || {};\n\nqq.s3.util = qq.s3.util || (function() {\n    \"use strict\";\n\n    return {\n        ALGORITHM_PARAM_NAME: \"x-amz-algorithm\",\n\n        AWS_PARAM_PREFIX: \"x-amz-meta-\",\n\n        CREDENTIAL_PARAM_NAME: \"x-amz-credential\",\n\n        DATE_PARAM_NAME: \"x-amz-date\",\n\n        REDUCED_REDUNDANCY_PARAM_NAME: \"x-amz-storage-class\",\n        REDUCED_REDUNDANCY_PARAM_VALUE: \"REDUCED_REDUNDANCY\",\n\n        SERVER_SIDE_ENCRYPTION_PARAM_NAME: \"x-amz-server-side-encryption\",\n        SERVER_SIDE_ENCRYPTION_PARAM_VALUE: \"AES256\",\n\n        SESSION_TOKEN_PARAM_NAME: \"x-amz-security-token\",\n\n        V4_ALGORITHM_PARAM_VALUE: \"AWS4-HMAC-SHA256\",\n\n        V4_SIGNATURE_PARAM_NAME: \"x-amz-signature\",\n\n        CASE_SENSITIVE_PARAM_NAMES: [\n            \"Cache-Control\",\n            \"Content-Disposition\",\n            \"Content-Encoding\",\n            \"Content-MD5\"\n        ],\n\n        UNSIGNABLE_REST_HEADER_NAMES: [\n            \"Cache-Control\",\n            \"Content-Disposition\",\n            \"Content-Encoding\",\n            \"Content-MD5\"\n        ],\n\n        UNPREFIXED_PARAM_NAMES: [\n            \"Cache-Control\",\n            \"Content-Disposition\",\n            \"Content-Encoding\",\n            \"Content-MD5\",\n            \"x-amz-server-side-encryption-customer-algorithm\",\n            \"x-amz-server-side-encryption-customer-key\",\n            \"x-amz-server-side-encryption-customer-key-MD5\"\n        ],\n\n        /**\n         * This allows for the region to be specified in the bucket's endpoint URL, or not.\n         *\n         * Examples of some valid endpoints are:\n         *     http://foo.s3.amazonaws.com\n         *     https://foo.s3.amazonaws.com\n         *     http://foo.s3-ap-northeast-1.amazonaws.com\n         *     foo.s3.amazonaws.com\n         *     http://foo.bar.com\n         *     http://s3.amazonaws.com/foo.bar.com\n         * ...etc\n         *\n         * @param endpoint The bucket's URL.\n         * @returns {String || undefined} The bucket name, or undefined if the URL cannot be parsed.\n         */\n        getBucket: function(endpoint) {\n            var patterns = [\n                    //bucket in domain\n                    /^(?:https?:\\/\\/)?([a-z0-9.\\-_]+)\\.s3(?:-[a-z0-9\\-]+)?\\.amazonaws\\.com/i,\n                    //bucket in path\n                    /^(?:https?:\\/\\/)?s3(?:-[a-z0-9\\-]+)?\\.amazonaws\\.com\\/([a-z0-9.\\-_]+)/i,\n                    //custom domain\n                    /^(?:https?:\\/\\/)?([a-z0-9.\\-_]+)/i\n                ],\n                bucket;\n\n            qq.each(patterns, function(idx, pattern) {\n                var match = pattern.exec(endpoint);\n\n                if (match) {\n                    bucket = match[1];\n                    return false;\n                }\n            });\n\n            return bucket;\n        },\n\n        /** Create Prefixed request headers which are appropriate for S3.\n         *\n         * If the request header is appropriate for S3 (e.g. Cache-Control) then pass\n         * it along without a metadata prefix. For all other request header parameter names,\n         * apply qq.s3.util.AWS_PARAM_PREFIX before the name.\n         * See: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html\n         */\n        _getPrefixedParamName: function(name) {\n            if (qq.indexOf(qq.s3.util.UNPREFIXED_PARAM_NAMES, name) >= 0) {\n                return name;\n            }\n            return qq.s3.util.AWS_PARAM_PREFIX + name;\n        },\n\n        /**\n         * Create a policy document to be signed and sent along with the S3 upload request.\n         *\n         * @param spec Object with properties use to construct the policy document.\n         * @returns {Object} Policy doc.\n         */\n        getPolicy: function(spec) {\n            var policy = {},\n                conditions = [],\n                bucket = spec.bucket,\n                date = spec.date,\n                drift = spec.clockDrift,\n                key = spec.key,\n                accessKey = spec.accessKey,\n                acl = spec.acl,\n                type = spec.type,\n                expectedStatus = spec.expectedStatus,\n                sessionToken = spec.sessionToken,\n                params = spec.params,\n                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),\n                minFileSize = spec.minFileSize,\n                maxFileSize = spec.maxFileSize,\n                reducedRedundancy = spec.reducedRedundancy,\n                region = spec.region,\n                serverSideEncryption = spec.serverSideEncryption,\n                signatureVersion = spec.signatureVersion;\n\n            policy.expiration = qq.s3.util.getPolicyExpirationDate(date, drift);\n\n            conditions.push({acl: acl});\n            conditions.push({bucket: bucket});\n\n            if (type) {\n                conditions.push({\"Content-Type\": type});\n            }\n\n            // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n            if (expectedStatus) {\n                conditions.push({success_action_status: expectedStatus.toString()});\n            }\n\n            if (successRedirectUrl) {\n                conditions.push({success_action_redirect: successRedirectUrl});\n            }\n            // jscs:enable\n            if (reducedRedundancy) {\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;\n            }\n\n            if (sessionToken) {\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;\n            }\n\n            if (serverSideEncryption) {\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;\n            }\n\n            if (signatureVersion === 2) {\n                conditions.push({key: key});\n            }\n            else if (signatureVersion === 4) {\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.ALGORITHM_PARAM_NAME] = qq.s3.util.V4_ALGORITHM_PARAM_VALUE;\n\n                conditions.push({});\n                conditions[conditions.length - 1].key = key;\n\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.CREDENTIAL_PARAM_NAME] =\n                    qq.s3.util.getV4CredentialsString({date: date, key: accessKey, region: region});\n\n                conditions.push({});\n                conditions[conditions.length - 1][qq.s3.util.DATE_PARAM_NAME] =\n                    qq.s3.util.getV4PolicyDate(date, drift);\n            }\n\n            // user metadata\n            qq.each(params, function(name, val) {\n                var awsParamName = qq.s3.util._getPrefixedParamName(name),\n                    param = {};\n\n                if (qq.indexOf(qq.s3.util.UNPREFIXED_PARAM_NAMES, awsParamName) >= 0) {\n                    param[awsParamName] = val;\n                }\n                else {\n                    param[awsParamName] = encodeURIComponent(val);\n                }\n\n                conditions.push(param);\n            });\n\n            policy.conditions = conditions;\n\n            qq.s3.util.enforceSizeLimits(policy, minFileSize, maxFileSize);\n\n            return policy;\n        },\n\n        /**\n         * Update a previously constructed policy document with updated credentials.  Currently, this only requires we\n         * update the session token.  This is only relevant if requests are being signed client-side.\n         *\n         * @param policy Live policy document\n         * @param newSessionToken Updated session token.\n         */\n        refreshPolicyCredentials: function(policy, newSessionToken) {\n            var sessionTokenFound = false;\n\n            qq.each(policy.conditions, function(oldCondIdx, oldCondObj) {\n                qq.each(oldCondObj, function(oldCondName, oldCondVal) {\n                    if (oldCondName === qq.s3.util.SESSION_TOKEN_PARAM_NAME) {\n                        oldCondObj[oldCondName] = newSessionToken;\n                        sessionTokenFound = true;\n                    }\n                });\n            });\n\n            if (!sessionTokenFound) {\n                policy.conditions.push({});\n                policy.conditions[policy.conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = newSessionToken;\n            }\n        },\n\n        /**\n         * Generates all parameters to be passed along with the S3 upload request.  This includes invoking a callback\n         * that is expected to asynchronously retrieve a signature for the policy document.  Note that the server\n         * signing the request should reject a \"tainted\" policy document that includes unexpected values, since it is\n         * still possible for a malicious user to tamper with these values during policy document generation,\n         * before it is sent to the server for signing.\n         *\n         * @param spec Object with properties: `params`, `type`, `key`, `accessKey`, `acl`, `expectedStatus`, `successRedirectUrl`,\n         * `reducedRedundancy`, `region`, `serverSideEncryption`, `version`, and `log()`, along with any options associated with `qq.s3.util.getPolicy()`.\n         * @returns {qq.Promise} Promise that will be fulfilled once all parameters have been determined.\n         */\n        generateAwsParams: function(spec, signPolicyCallback) {\n            var awsParams = {},\n                customParams = spec.params,\n                promise = new qq.Promise(),\n                sessionToken = spec.sessionToken,\n                drift = spec.clockDrift,\n                type = spec.type,\n                key = spec.key,\n                accessKey = spec.accessKey,\n                acl = spec.acl,\n                expectedStatus = spec.expectedStatus,\n                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),\n                reducedRedundancy = spec.reducedRedundancy,\n                region = spec.region,\n                serverSideEncryption = spec.serverSideEncryption,\n                signatureVersion = spec.signatureVersion,\n                now = new Date(),\n                log = spec.log,\n                policyJson;\n\n            spec.date = now;\n            policyJson = qq.s3.util.getPolicy(spec);\n\n            awsParams.key = key;\n\n            if (type) {\n                awsParams[\"Content-Type\"] = type;\n            }\n            // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n            if (expectedStatus) {\n                awsParams.success_action_status = expectedStatus;\n            }\n\n            if (successRedirectUrl) {\n                awsParams.success_action_redirect = successRedirectUrl;\n            }\n            // jscs:enable\n            if (reducedRedundancy) {\n                awsParams[qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;\n            }\n\n            if (serverSideEncryption) {\n                awsParams[qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;\n            }\n\n            if (sessionToken) {\n                awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;\n            }\n\n            awsParams.acl = acl;\n\n            // Custom (user-supplied) params must be prefixed with the value of `qq.s3.util.AWS_PARAM_PREFIX`.\n            // Params such as Cache-Control or Content-Disposition will not be prefixed.\n            // Prefixed param values will be URI encoded as well.\n            qq.each(customParams, function(name, val) {\n                var awsParamName = qq.s3.util._getPrefixedParamName(name);\n\n                if (qq.indexOf(qq.s3.util.UNPREFIXED_PARAM_NAMES, awsParamName) >= 0) {\n                    awsParams[awsParamName] = val;\n                }\n                else {\n                    awsParams[awsParamName] = encodeURIComponent(val);\n                }\n            });\n\n            if (signatureVersion === 2) {\n                awsParams.AWSAccessKeyId = accessKey;\n            }\n            else if (signatureVersion === 4) {\n                awsParams[qq.s3.util.ALGORITHM_PARAM_NAME] = qq.s3.util.V4_ALGORITHM_PARAM_VALUE;\n                awsParams[qq.s3.util.CREDENTIAL_PARAM_NAME] = qq.s3.util.getV4CredentialsString({date: now, key: accessKey, region: region});\n                awsParams[qq.s3.util.DATE_PARAM_NAME] = qq.s3.util.getV4PolicyDate(now, drift);\n            }\n\n            // Invoke a promissory callback that should provide us with a base64-encoded policy doc and an\n            // HMAC signature for the policy doc.\n            signPolicyCallback(policyJson).then(\n                function(policyAndSignature, updatedAccessKey, updatedSessionToken) {\n                    awsParams.policy = policyAndSignature.policy;\n\n                    if (spec.signatureVersion === 2) {\n                        awsParams.signature = policyAndSignature.signature;\n\n                        if (updatedAccessKey) {\n                            awsParams.AWSAccessKeyId = updatedAccessKey;\n                        }\n                    }\n                    else if (spec.signatureVersion === 4) {\n                        awsParams[qq.s3.util.V4_SIGNATURE_PARAM_NAME] = policyAndSignature.signature;\n                    }\n\n                    if (updatedSessionToken) {\n                        awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = updatedSessionToken;\n                    }\n\n                    promise.success(awsParams);\n                },\n                function(errorMessage) {\n                    errorMessage = errorMessage || \"Can't continue further with request to S3 as we did not receive \" +\n                                                   \"a valid signature and policy from the server.\";\n\n                    log(\"Policy signing failed.  \" + errorMessage, \"error\");\n                    promise.failure(errorMessage);\n                }\n            );\n\n            return promise;\n        },\n\n        /**\n         * Add a condition to an existing S3 upload request policy document used to ensure AWS enforces any size\n         * restrictions placed on files server-side.  This is important to do, in case users mess with the client-side\n         * checks already in place.\n         *\n         * @param policy Policy document as an `Object`, with a `conditions` property already attached\n         * @param minSize Minimum acceptable size, in bytes\n         * @param maxSize Maximum acceptable size, in bytes (0 = unlimited)\n         */\n        enforceSizeLimits: function(policy, minSize, maxSize) {\n            var adjustedMinSize = minSize < 0 ? 0 : minSize,\n                // Adjust a maxSize of 0 to the largest possible integer, since we must specify a high and a low in the request\n                adjustedMaxSize = maxSize <= 0 ? 9007199254740992 : maxSize;\n\n            if (minSize > 0 || maxSize > 0) {\n                policy.conditions.push([\"content-length-range\", adjustedMinSize.toString(), adjustedMaxSize.toString()]);\n            }\n        },\n\n        getPolicyExpirationDate: function(date, drift) {\n            var adjustedDate = new Date(date.getTime() + drift);\n            return qq.s3.util.getPolicyDate(adjustedDate, 5);\n        },\n\n        getCredentialsDate: function(date) {\n            return date.getUTCFullYear() + \"\" +\n                (\"0\" + (date.getUTCMonth() + 1)).slice(-2) +\n                (\"0\" + date.getUTCDate()).slice(-2);\n        },\n\n        getPolicyDate: function(date, _minutesToAdd_) {\n            var minutesToAdd = _minutesToAdd_ || 0,\n                pad, r;\n\n            /*jshint -W014 */\n            // Is this going to be a problem if we encounter this moments before 2 AM just before daylight savings time ends?\n            date.setMinutes(date.getMinutes() + (minutesToAdd || 0));\n\n            if (Date.prototype.toISOString) {\n                return date.toISOString();\n            }\n            else {\n                pad = function(number) {\n                    r = String(number);\n\n                    if (r.length === 1) {\n                        r = \"0\" + r;\n                    }\n\n                    return r;\n                };\n\n                return date.getUTCFullYear()\n                    + \"-\" + pad(date.getUTCMonth() + 1)\n                    + \"-\" + pad(date.getUTCDate())\n                    + \"T\" + pad(date.getUTCHours())\n                    + \":\" + pad(date.getUTCMinutes())\n                    + \":\" + pad(date.getUTCSeconds())\n                    + \".\" + String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)\n                    + \"Z\";\n            }\n        },\n\n        /**\n         * Looks at a response from S3 contained in an iframe and parses the query string in an attempt to identify\n         * the associated resource.\n         *\n         * @param iframe Iframe containing response\n         * @returns {{bucket: *, key: *, etag: *}}\n         */\n        parseIframeResponse: function(iframe) {\n            var doc = iframe.contentDocument || iframe.contentWindow.document,\n                queryString = doc.location.search,\n                match = /bucket=(.+)&key=(.+)&etag=(.+)/.exec(queryString);\n\n            if (match) {\n                return {\n                    bucket: match[1],\n                    key: match[2],\n                    etag: match[3].replace(/%22/g, \"\")\n                };\n            }\n        },\n\n        /**\n         * @param successRedirectUrl Relative or absolute location of success redirect page\n         * @returns {*|string} undefined if the parameter is undefined, otherwise the absolute location of the success redirect page\n         */\n        getSuccessRedirectAbsoluteUrl: function(successRedirectUrl) {\n            if (successRedirectUrl) {\n                var targetAnchorContainer = document.createElement(\"div\"),\n                    targetAnchor;\n\n                if (qq.ie7()) {\n                    // Note that we must make use of `innerHTML` for IE7 only instead of simply creating an anchor via\n                    // `document.createElement('a')` and setting the `href` attribute.  The latter approach does not allow us to\n                    // obtain an absolute URL in IE7 if the `endpoint` is a relative URL.\n                    targetAnchorContainer.innerHTML = \"<a href='\" + successRedirectUrl + \"'></a>\";\n                    targetAnchor = targetAnchorContainer.firstChild;\n                    return targetAnchor.href;\n                }\n                else {\n                    // IE8 and IE9 do not seem to derive an absolute URL from a relative URL using the `innerHTML`\n                    // approach above, so we'll just create an anchor this way and set it's `href` attribute.\n                    // Due to yet another quirk in IE8 and IE9, we have to set the `href` equal to itself\n                    // in order to ensure relative URLs will be properly parsed.\n                    targetAnchor = document.createElement(\"a\");\n                    targetAnchor.href = successRedirectUrl;\n                    targetAnchor.href = targetAnchor.href;\n                    return targetAnchor.href;\n                }\n            }\n        },\n\n        getV4CredentialsString: function(spec) {\n            return spec.key + \"/\" +\n                qq.s3.util.getCredentialsDate(spec.date) + \"/\" +\n                spec.region + \"/s3/aws4_request\";\n        },\n\n        getV4PolicyDate: function(date, drift) {\n            var adjustedDate = new Date(date.getTime() + drift);\n\n            return qq.s3.util.getCredentialsDate(adjustedDate) + \"T\" +\n                    (\"0\" + adjustedDate.getUTCHours()).slice(-2) +\n                    (\"0\" + adjustedDate.getUTCMinutes()).slice(-2) +\n                    (\"0\" + adjustedDate.getUTCSeconds()).slice(-2) +\n                    \"Z\";\n        },\n\n        // AWS employs a strict interpretation of [RFC 3986](http://tools.ietf.org/html/rfc3986#page-12).\n        // So, we must ensure all reserved characters listed in the spec are percent-encoded,\n        // and spaces are replaced with \"+\".\n        encodeQueryStringParam: function(param) {\n            var percentEncoded = encodeURIComponent(param);\n\n            // %-encode characters not handled by `encodeURIComponent` (to follow RFC 3986)\n            percentEncoded = percentEncoded.replace(/[!'()]/g, escape);\n\n            // %-encode characters not handled by `escape` (to follow RFC 3986)\n            percentEncoded = percentEncoded.replace(/\\*/g, \"%2A\");\n\n            // replace percent-encoded spaces with a \"+\"\n            return percentEncoded.replace(/%20/g, \"+\");\n        },\n        /**\n         * Escapes url part as for AWS requirements\n         * AWS uriEscapePath function pulled from aws-sdk-js licensed under Apache 2.0 - http://github.com/aws/aws-sdk-js\n         */\n        uriEscape: function(string) {\n            var output = encodeURIComponent(string);\n            output = output.replace(/[^A-Za-z0-9_.~\\-%]+/g, escape);\n            output = output.replace(/[*]/g, function(ch) {\n                return \"%\" + ch.charCodeAt(0).toString(16).toUpperCase();\n            });\n            return output;\n        },\n        /**\n         * Escapes a path as for AWS requirement\n         * AWS uriEscapePath function pulled from aws-sdk-js licensed under Apache 2.0 - http://github.com/aws/aws-sdk-js\n         */\n        uriEscapePath: function(path) {\n            var parts = [];\n            qq.each(path.split(\"/\"), function(idx, item) {\n                parts.push(qq.s3.util.uriEscape(item));\n            });\n            return parts.join(\"/\");\n        }\n    };\n}());\n","/*globals qq*/\n/**\n * Defines the public API for non-traditional FineUploaderBasic mode.\n */\n(function() {\n    \"use strict\";\n\n    qq.nonTraditionalBasePublicApi = {\n        setUploadSuccessParams: function(params, id) {\n            this._uploadSuccessParamsStore.set(params, id);\n        },\n        setUploadSuccessEndpoint: function(endpoint, id) {\n            this._uploadSuccessEndpointStore.set(endpoint, id);\n        }\n    };\n\n    qq.nonTraditionalBasePrivateApi = {\n        /**\n         * When the upload has completed, if it is successful, send a request to the `successEndpoint` (if defined).\n         * This will hold up the call to the `onComplete` callback until we have determined success of the upload\n         * according to the local server, if a `successEndpoint` has been defined by the integrator.\n         *\n         * @param id ID of the completed upload\n         * @param name Name of the associated item\n         * @param result Object created from the server's parsed JSON response.\n         * @param xhr Associated XmlHttpRequest, if this was used to send the request.\n         * @returns {boolean || qq.Promise} true/false if success can be determined immediately, otherwise a `qq.Promise`\n         * if we need to ask the server.\n         * @private\n         */\n        _onComplete: function(id, name, result, xhr) {\n            var success = result.success ? true : false,\n                self = this,\n                onCompleteArgs = arguments,\n                successEndpoint = this._uploadSuccessEndpointStore.get(id),\n                successCustomHeaders = this._options.uploadSuccess.customHeaders,\n                successMethod = this._options.uploadSuccess.method,\n                cors = this._options.cors,\n                promise = new qq.Promise(),\n                uploadSuccessParams = this._uploadSuccessParamsStore.get(id),\n                fileParams = this._paramsStore.get(id),\n\n                // If we are waiting for confirmation from the local server, and have received it,\n                // include properties from the local server response in the `response` parameter\n                // sent to the `onComplete` callback, delegate to the parent `_onComplete`, and\n                // fulfill the associated promise.\n                onSuccessFromServer = function(successRequestResult) {\n                    delete self._failedSuccessRequestCallbacks[id];\n                    qq.extend(result, successRequestResult);\n                    qq.FineUploaderBasic.prototype._onComplete.apply(self, onCompleteArgs);\n                    promise.success(successRequestResult);\n                },\n\n                // If the upload success request fails, attempt to re-send the success request (via the core retry code).\n                // The entire upload may be restarted if the server returns a \"reset\" property with a value of true as well.\n                onFailureFromServer = function(successRequestResult) {\n                    var callback = submitSuccessRequest;\n\n                    qq.extend(result, successRequestResult);\n\n                    if (result && result.reset) {\n                        callback = null;\n                    }\n\n                    if (!callback) {\n                        delete self._failedSuccessRequestCallbacks[id];\n                    }\n                    else {\n                        self._failedSuccessRequestCallbacks[id] = callback;\n                    }\n\n                    if (!self._onAutoRetry(id, name, result, xhr, callback)) {\n                        qq.FineUploaderBasic.prototype._onComplete.apply(self, onCompleteArgs);\n                        promise.failure(successRequestResult);\n                    }\n                },\n                submitSuccessRequest,\n                successAjaxRequester;\n\n            // Ask the local server if the file sent is ok.\n            if (success && successEndpoint) {\n                successAjaxRequester = new qq.UploadSuccessAjaxRequester({\n                    endpoint: successEndpoint,\n                    method: successMethod,\n                    customHeaders: successCustomHeaders,\n                    cors: cors,\n                    log: qq.bind(this.log, this)\n                });\n\n                // combine custom params and default params\n                qq.extend(uploadSuccessParams, self._getEndpointSpecificParams(id, result, xhr), true);\n\n                // include any params associated with the file\n                fileParams && qq.extend(uploadSuccessParams, fileParams, true);\n\n                submitSuccessRequest = qq.bind(function() {\n                    successAjaxRequester.sendSuccessRequest(id, uploadSuccessParams)\n                        .then(onSuccessFromServer, onFailureFromServer);\n                }, self);\n\n                submitSuccessRequest();\n\n                return promise;\n            }\n\n            // If we are not asking the local server about the file, just delegate to the parent `_onComplete`.\n            return qq.FineUploaderBasic.prototype._onComplete.apply(this, arguments);\n        },\n\n        // If the failure occurred on an upload success request (and a reset was not ordered), try to resend that instead.\n        _manualRetry: function(id) {\n            var successRequestCallback = this._failedSuccessRequestCallbacks[id];\n\n            return qq.FineUploaderBasic.prototype._manualRetry.call(this, id, successRequestCallback);\n        }\n    };\n}());\n","/*globals qq */\n/**\n * This defines FineUploaderBasic mode w/ support for uploading to S3, which provides all the basic\n * functionality of Fine Uploader Basic as well as code to handle uploads directly to S3.\n * Some inherited options and API methods have a special meaning in the context of the S3 uploader.\n */\n(function() {\n    \"use strict\";\n\n    qq.s3.FineUploaderBasic = function(o) {\n        var options = {\n            request: {\n                // public key (required for server-side signing, ignored if `credentials` have been provided)\n                accessKey: null,\n\n                // padding, in milliseconds, to add to the x-amz-date header & the policy expiration date\n                clockDrift: 0\n            },\n\n            objectProperties: {\n                acl: \"private\",\n\n                // string or a function which may be promissory\n                bucket: qq.bind(function(id) {\n                    return qq.s3.util.getBucket(this.getEndpoint(id));\n                }, this),\n\n                // string or a function which may be promissory - only used for V4 multipart uploads\n                host: qq.bind(function(id) {\n                    return (/(?:http|https):\\/\\/(.+)(?:\\/.+)?/).exec(this._endpointStore.get(id))[1];\n                }, this),\n\n                // 'uuid', 'filename', or a function which may be promissory\n                key: \"uuid\",\n\n                reducedRedundancy: false,\n\n                // Defined at http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region\n                region: \"us-east-1\",\n\n                serverSideEncryption: false\n            },\n\n            credentials: {\n                // Public key (required).\n                accessKey: null,\n                // Private key (required).\n                secretKey: null,\n                // Expiration date for the credentials (required).  May be an ISO string or a `Date`.\n                expiration: null,\n                // Temporary credentials session token.\n                // Only required for temporary credentials obtained via AssumeRoleWithWebIdentity.\n                sessionToken: null\n            },\n\n            // All but `version` are ignored if `credentials` is provided.\n            signature: {\n                customHeaders: {},\n                endpoint: null,\n                version: 2\n            },\n\n            uploadSuccess: {\n                endpoint: null,\n\n                method: \"POST\",\n\n                // In addition to the default params sent by Fine Uploader\n                params: {},\n\n                customHeaders: {}\n            },\n\n            // required if non-File-API browsers, such as IE9 and older, are used\n            iframeSupport: {\n                localBlankPagePath: null\n            },\n\n            chunking: {\n                // minimum part size is 5 MiB when uploading to S3\n                partSize: 5242880\n            },\n\n            cors: {\n                allowXdr: true\n            },\n\n            callbacks: {\n                onCredentialsExpired: function() {}\n            }\n        };\n\n        // Replace any default options with user defined ones\n        qq.extend(options, o, true);\n\n        if (!this.setCredentials(options.credentials, true)) {\n            this._currentCredentials.accessKey = options.request.accessKey;\n        }\n\n        this._aclStore = this._createStore(options.objectProperties.acl);\n\n        // Call base module\n        qq.FineUploaderBasic.call(this, options);\n\n        this._uploadSuccessParamsStore = this._createStore(this._options.uploadSuccess.params);\n        this._uploadSuccessEndpointStore = this._createStore(this._options.uploadSuccess.endpoint);\n\n        // This will hold callbacks for failed uploadSuccess requests that will be invoked on retry.\n        // Indexed by file ID.\n        this._failedSuccessRequestCallbacks = {};\n\n        // Holds S3 keys for file representations constructed from a session request.\n        this._cannedKeys = {};\n        // Holds S3 buckets for file representations constructed from a session request.\n        this._cannedBuckets = {};\n\n        this._buckets = {};\n        this._hosts = {};\n    };\n\n    // Inherit basic public & private API methods.\n    qq.extend(qq.s3.FineUploaderBasic.prototype, qq.basePublicApi);\n    qq.extend(qq.s3.FineUploaderBasic.prototype, qq.basePrivateApi);\n    qq.extend(qq.s3.FineUploaderBasic.prototype, qq.nonTraditionalBasePublicApi);\n    qq.extend(qq.s3.FineUploaderBasic.prototype, qq.nonTraditionalBasePrivateApi);\n\n    // Define public & private API methods for this module.\n    qq.extend(qq.s3.FineUploaderBasic.prototype, {\n        getBucket: function(id) {\n            if (this._cannedBuckets[id] == null) {\n                return this._buckets[id];\n            }\n            return this._cannedBuckets[id];\n        },\n\n        /**\n         * @param id File ID\n         * @returns {*} Key name associated w/ the file, if one exists\n         */\n        getKey: function(id) {\n            /* jshint eqnull:true */\n            if (this._cannedKeys[id] == null) {\n                return this._handler.getThirdPartyFileId(id);\n            }\n\n            return this._cannedKeys[id];\n        },\n\n        /**\n         * Override the parent's reset function to cleanup various S3-related items.\n         */\n        reset: function() {\n            qq.FineUploaderBasic.prototype.reset.call(this);\n            this._failedSuccessRequestCallbacks = [];\n            this._buckets = {};\n            this._hosts = {};\n        },\n\n        setCredentials: function(credentials, ignoreEmpty) {\n            if (credentials && credentials.secretKey) {\n                if (!credentials.accessKey) {\n                    throw new qq.Error(\"Invalid credentials: no accessKey\");\n                }\n                else if (!credentials.expiration) {\n                    throw new qq.Error(\"Invalid credentials: no expiration\");\n                }\n                else {\n                    this._currentCredentials = qq.extend({}, credentials);\n\n                    // Ensure expiration is a `Date`.  If initially a string, assuming it is in ISO format.\n                    if (qq.isString(credentials.expiration)) {\n                        this._currentCredentials.expiration = new Date(credentials.expiration);\n                    }\n                }\n\n                return true;\n            }\n            else if (!ignoreEmpty) {\n                throw new qq.Error(\"Invalid credentials parameter!\");\n            }\n            else {\n                this._currentCredentials = {};\n            }\n        },\n\n        setAcl: function(acl, id) {\n            this._aclStore.set(acl, id);\n        },\n\n        /**\n         * Ensures the parent's upload handler creator passes any additional S3-specific options to the handler as well\n         * as information required to instantiate the specific handler based on the current browser's capabilities.\n         *\n         * @returns {qq.UploadHandlerController}\n         * @private\n         */\n        _createUploadHandler: function() {\n            var self = this,\n                additionalOptions = {\n                    aclStore: this._aclStore,\n                    getBucket: qq.bind(this._determineBucket, this),\n                    getHost: qq.bind(this._determineHost, this),\n                    getKeyName: qq.bind(this._determineKeyName, this),\n                    iframeSupport: this._options.iframeSupport,\n                    objectProperties: this._options.objectProperties,\n                    signature: this._options.signature,\n                    clockDrift: this._options.request.clockDrift,\n                    // pass size limit validation values to include in the request so AWS enforces this server-side\n                    validation: {\n                        minSizeLimit: this._options.validation.minSizeLimit,\n                        maxSizeLimit: this._options.validation.sizeLimit\n                    }\n                };\n\n            // We assume HTTP if it is missing from the start of the endpoint string.\n            qq.override(this._endpointStore, function(super_) {\n                return {\n                    get: function(id) {\n                        var endpoint = super_.get(id);\n\n                        if (endpoint.indexOf(\"http\") < 0) {\n                            return \"http://\" + endpoint;\n                        }\n\n                        return endpoint;\n                    }\n                };\n            });\n\n            // Some param names should be lower case to avoid signature mismatches\n            qq.override(this._paramsStore, function(super_) {\n                return {\n                    get: function(id) {\n                        var oldParams = super_.get(id),\n                            modifiedParams = {};\n\n                        qq.each(oldParams, function(name, val) {\n                            var paramName = name;\n\n                            if (qq.indexOf(qq.s3.util.CASE_SENSITIVE_PARAM_NAMES, paramName) < 0) {\n                                paramName = paramName.toLowerCase();\n                            }\n\n                            modifiedParams[paramName] = qq.isFunction(val) ? val() : val;\n                        });\n\n                        return modifiedParams;\n                    }\n                };\n            });\n\n            additionalOptions.signature.credentialsProvider = {\n                get: function() {\n                    return self._currentCredentials;\n                },\n\n                onExpired: function() {\n                    var updateCredentials = new qq.Promise(),\n                        callbackRetVal = self._options.callbacks.onCredentialsExpired();\n\n                    if (qq.isGenericPromise(callbackRetVal)) {\n                        callbackRetVal.then(function(credentials) {\n                            try {\n                                self.setCredentials(credentials);\n                                updateCredentials.success();\n                            }\n                            catch (error) {\n                                self.log(\"Invalid credentials returned from onCredentialsExpired callback! (\" + error.message + \")\", \"error\");\n                                updateCredentials.failure(\"onCredentialsExpired did not return valid credentials.\");\n                            }\n                        }, function(errorMsg) {\n                            self.log(\"onCredentialsExpired callback indicated failure! (\" + errorMsg + \")\", \"error\");\n                            updateCredentials.failure(\"onCredentialsExpired callback failed.\");\n                        });\n                    }\n                    else {\n                        self.log(\"onCredentialsExpired callback did not return a promise!\", \"error\");\n                        updateCredentials.failure(\"Unexpected return value for onCredentialsExpired.\");\n                    }\n\n                    return updateCredentials;\n                }\n            };\n\n            return qq.FineUploaderBasic.prototype._createUploadHandler.call(this, additionalOptions, \"s3\");\n        },\n\n        _determineObjectPropertyValue: function(id, property) {\n            var maybe = this._options.objectProperties[property],\n                promise = new qq.Promise(),\n                self = this;\n\n            if (qq.isFunction(maybe)) {\n                maybe = maybe(id);\n                if (qq.isGenericPromise(maybe)) {\n                    promise = maybe;\n                }\n                else {\n                    promise.success(maybe);\n                }\n            }\n            else if (qq.isString(maybe)) {\n                promise.success(maybe);\n            }\n\n            promise.then(\n                function success(value) {\n                    self[\"_\" + property + \"s\"][id] = value;\n                },\n\n                function failure(errorMsg) {\n                    qq.log(\"Problem determining \" + property + \" for ID \" + id + \" (\" + errorMsg + \")\", \"error\");\n                }\n            );\n\n            return promise;\n        },\n\n        _determineBucket: function(id) {\n            return this._determineObjectPropertyValue(id, \"bucket\");\n        },\n\n        _determineHost: function(id) {\n            return this._determineObjectPropertyValue(id, \"host\");\n        },\n\n        /**\n         * Determine the file's key name and passes it to the caller via a promissory callback.  This also may\n         * delegate to an integrator-defined function that determines the file's key name on demand,\n         * which also may be promissory.\n         *\n         * @param id ID of the file\n         * @param filename Name of the file\n         * @returns {qq.Promise} A promise that will be fulfilled when the key name has been determined (and will be passed to the caller via the success callback).\n         * @private\n         */\n        _determineKeyName: function(id, filename) {\n            /*jshint -W015*/\n            var promise = new qq.Promise(),\n                keynameLogic = this._options.objectProperties.key,\n                extension = qq.getExtension(filename),\n                onGetKeynameFailure = promise.failure,\n                onGetKeynameSuccess = function(keyname, extension) {\n                    var keynameToUse = keyname;\n\n                    if (extension !== undefined) {\n                        keynameToUse += \".\" + extension;\n                    }\n\n                    promise.success(keynameToUse);\n                };\n\n            switch (keynameLogic) {\n                case \"uuid\":\n                    onGetKeynameSuccess(this.getUuid(id), extension);\n                    break;\n                case \"filename\":\n                    onGetKeynameSuccess(filename);\n                    break;\n                default:\n                    if (qq.isFunction(keynameLogic)) {\n                        this._handleKeynameFunction(keynameLogic, id, onGetKeynameSuccess, onGetKeynameFailure);\n                    }\n                    else {\n                        this.log(keynameLogic + \" is not a valid value for the s3.keyname option!\", \"error\");\n                        onGetKeynameFailure();\n                    }\n            }\n\n            return promise;\n        },\n\n        /**\n         * Called by the internal onUpload handler if the integrator has supplied a function to determine\n         * the file's key name.  The integrator's function may be promissory.  We also need to fulfill\n         * the promise contract associated with the caller as well.\n         *\n         * @param keynameFunc Integrator-supplied function that must be executed to determine the key name.  May be promissory.\n         * @param id ID of the associated file\n         * @param successCallback Invoke this if key name retrieval is successful, passing in the key name.\n         * @param failureCallback Invoke this if key name retrieval was unsuccessful.\n         * @private\n         */\n        _handleKeynameFunction: function(keynameFunc, id, successCallback, failureCallback) {\n            var self = this,\n                onSuccess = function(keyname) {\n                    successCallback(keyname);\n                },\n                onFailure = function(reason) {\n                    self.log(qq.format(\"Failed to retrieve key name for {}.  Reason: {}\", id, reason || \"null\"), \"error\");\n                    failureCallback(reason);\n                },\n                keyname = keynameFunc.call(this, id);\n\n            if (qq.isGenericPromise(keyname)) {\n                keyname.then(onSuccess, onFailure);\n            }\n            /*jshint -W116*/\n            else if (keyname == null) {\n                onFailure();\n            }\n            else {\n                onSuccess(keyname);\n            }\n        },\n\n        _getEndpointSpecificParams: function(id, response, maybeXhr) {\n            var params = {\n                key: this.getKey(id),\n                uuid: this.getUuid(id),\n                name: this.getName(id),\n                bucket: this.getBucket(id)\n            };\n\n            if (maybeXhr && maybeXhr.getResponseHeader(\"ETag\")) {\n                params.etag = maybeXhr.getResponseHeader(\"ETag\");\n            }\n            else if (response.etag) {\n                params.etag = response.etag;\n            }\n\n            return params;\n        },\n\n        // Hooks into the base internal `_onSubmitDelete` to add key and bucket params to the delete file request.\n        _onSubmitDelete: function(id, onSuccessCallback) {\n            var additionalMandatedParams = {\n                key: this.getKey(id),\n                bucket: this.getBucket(id)\n            };\n\n            return qq.FineUploaderBasic.prototype._onSubmitDelete.call(this, id, onSuccessCallback, additionalMandatedParams);\n        },\n\n        _addCannedFile: function(sessionData) {\n            var id;\n\n            /* jshint eqnull:true */\n            if (sessionData.s3Key == null) {\n                throw new qq.Error(\"Did not find s3Key property in server session response.  This is required!\");\n            }\n            else {\n                id = qq.FineUploaderBasic.prototype._addCannedFile.apply(this, arguments);\n                this._cannedKeys[id] = sessionData.s3Key;\n                this._cannedBuckets[id] = sessionData.s3Bucket;\n            }\n\n            return id;\n        }\n    });\n}());\n","/* globals qq, CryptoJS */\n\n// IE 10 does not support Uint8ClampedArray. We don't need it, but CryptoJS attempts to reference it\n// inside a conditional via an instanceof check, which breaks S3 v4 signatures for chunked uploads.\nif (!window.Uint8ClampedArray) {\n    window.Uint8ClampedArray = function() {};\n}\n/**\n * Handles signature determination for HTML Form Upload requests and Multipart Uploader requests (via the S3 REST API).\n *\n * If the S3 requests are to be signed server side, this module will send a POST request to the server in an attempt\n * to solicit signatures for various S3-related requests.  This module also parses the response and attempts\n * to determine if the effort was successful.\n *\n * If the S3 requests are to be signed client-side, without the help of a server, this module will utilize CryptoJS to\n * sign the requests directly in the browser and send them off to S3.\n *\n * @param o Options associated with all such requests\n * @returns {{getSignature: Function}} API method used to initiate the signature request.\n * @constructor\n */\nqq.s3.RequestSigner = function(o) {\n    \"use strict\";\n\n    var requester,\n        thisSignatureRequester = this,\n        pendingSignatures = {},\n        options = {\n            expectingPolicy: false,\n            method: \"POST\",\n            signatureSpec: {\n                drift: 0,\n                credentialsProvider: {},\n                endpoint: null,\n                customHeaders: {},\n                version: 2\n            },\n            maxConnections: 3,\n            endpointStore: {},\n            paramsStore: {},\n            cors: {\n                expected: false,\n                sendCredentials: false\n            },\n            log: function(str, level) {}\n        },\n        credentialsProvider,\n\n        generateHeaders = function(signatureConstructor, signature, promise) {\n            var headers = signatureConstructor.getHeaders();\n\n            if (options.signatureSpec.version === 4) {\n                headers.Authorization = qq.s3.util.V4_ALGORITHM_PARAM_VALUE +\n                    \" Credential=\" + options.signatureSpec.credentialsProvider.get().accessKey + \"/\" +\n                    qq.s3.util.getCredentialsDate(signatureConstructor.getRequestDate()) + \"/\" +\n                    options.signatureSpec.region + \"/\" +\n                    \"s3/aws4_request,\" +\n                    \"SignedHeaders=\" + signatureConstructor.getSignedHeaders() + \",\" +\n                    \"Signature=\" + signature;\n            }\n            else {\n                headers.Authorization = \"AWS \" + options.signatureSpec.credentialsProvider.get().accessKey + \":\" + signature;\n            }\n\n            promise.success(headers, signatureConstructor.getEndOfUrl());\n        },\n\n        v2 = {\n            getStringToSign: function(signatureSpec) {\n                return qq.format(\"{}\\n{}\\n{}\\n\\n{}/{}/{}\",\n                    signatureSpec.method,\n                    signatureSpec.contentMd5 || \"\",\n                    signatureSpec.contentType || \"\",\n                    signatureSpec.headersStr || \"\\n\",\n                    signatureSpec.bucket,\n                    signatureSpec.endOfUrl);\n            },\n\n            signApiRequest: function(signatureConstructor, headersStr, signatureEffort) {\n                var headersWordArray = qq.CryptoJS.enc.Utf8.parse(headersStr),\n                    headersHmacSha1 = qq.CryptoJS.HmacSHA1(headersWordArray, credentialsProvider.get().secretKey),\n                    headersHmacSha1Base64 = qq.CryptoJS.enc.Base64.stringify(headersHmacSha1);\n\n                generateHeaders(signatureConstructor, headersHmacSha1Base64, signatureEffort);\n            },\n\n            signPolicy: function(policy, signatureEffort, updatedAccessKey, updatedSessionToken) {\n                var policyStr = JSON.stringify(policy),\n                    policyWordArray = qq.CryptoJS.enc.Utf8.parse(policyStr),\n                    base64Policy = qq.CryptoJS.enc.Base64.stringify(policyWordArray),\n                    policyHmacSha1 = qq.CryptoJS.HmacSHA1(base64Policy, credentialsProvider.get().secretKey),\n                    policyHmacSha1Base64 = qq.CryptoJS.enc.Base64.stringify(policyHmacSha1);\n\n                signatureEffort.success({\n                    policy: base64Policy,\n                    signature: policyHmacSha1Base64\n                }, updatedAccessKey, updatedSessionToken);\n            }\n        },\n\n        v4 = {\n            getCanonicalQueryString: function(endOfUri) {\n                var queryParamIdx = endOfUri.indexOf(\"?\"),\n                    canonicalQueryString = \"\",\n                    encodedQueryParams, encodedQueryParamNames, queryStrings;\n\n                if (queryParamIdx >= 0) {\n                    encodedQueryParams = {};\n                    queryStrings = endOfUri.substr(queryParamIdx + 1).split(\"&\");\n\n                    qq.each(queryStrings, function(idx, queryString) {\n                        var nameAndVal = queryString.split(\"=\"),\n                            paramVal = nameAndVal[1];\n\n                        if (paramVal == null) {\n                            paramVal = \"\";\n                        }\n\n                        encodedQueryParams[encodeURIComponent(nameAndVal[0])] = encodeURIComponent(paramVal);\n                    });\n\n                    encodedQueryParamNames = Object.keys(encodedQueryParams).sort();\n                    encodedQueryParamNames.forEach(function(encodedQueryParamName, idx) {\n                        canonicalQueryString += encodedQueryParamName + \"=\" + encodedQueryParams[encodedQueryParamName];\n                        if (idx < encodedQueryParamNames.length - 1) {\n                            canonicalQueryString += \"&\";\n                        }\n                    });\n                }\n\n                return canonicalQueryString;\n            },\n\n            getCanonicalRequest: function(signatureSpec) {\n                return qq.format(\"{}\\n{}\\n{}\\n{}\\n{}\\n{}\",\n                    signatureSpec.method,\n                    v4.getCanonicalUri(signatureSpec.endOfUrl),\n                    v4.getCanonicalQueryString(signatureSpec.endOfUrl),\n                    signatureSpec.headersStr || \"\\n\",\n                    v4.getSignedHeaders(signatureSpec.headerNames),\n                    signatureSpec.hashedContent);\n            },\n\n            getCanonicalUri: function(endOfUri) {\n                var path = endOfUri,\n                    queryParamIdx = endOfUri.indexOf(\"?\");\n\n                if (queryParamIdx > 0) {\n                    path = endOfUri.substr(0, queryParamIdx);\n                }\n                return \"/\" + path;\n            },\n\n            getEncodedHashedPayload: function(body) {\n                var promise = new qq.Promise(),\n                    reader;\n\n                if (qq.isBlob(body)) {\n                    // TODO hash blob in webworker if this becomes a notable perf issue\n                    reader = new FileReader();\n                    reader.onloadend = function(e) {\n                        if (e.target.readyState === FileReader.DONE) {\n                            if (e.target.error) {\n                                promise.failure(e.target.error);\n                            }\n                            else {\n                                var wordArray = qq.CryptoJS.lib.WordArray.create(e.target.result);\n                                promise.success(qq.CryptoJS.SHA256(wordArray).toString());\n                            }\n                        }\n                    };\n                    reader.readAsArrayBuffer(body);\n                }\n                else {\n                    body = body || \"\";\n                    promise.success(qq.CryptoJS.SHA256(body).toString());\n                }\n\n                return promise;\n            },\n\n            getScope: function(date, region) {\n                return qq.s3.util.getCredentialsDate(date) + \"/\" +\n                    region + \"/s3/aws4_request\";\n            },\n\n            getStringToSign: function(signatureSpec) {\n                var canonicalRequest = v4.getCanonicalRequest(signatureSpec),\n                    date = qq.s3.util.getV4PolicyDate(signatureSpec.date, signatureSpec.drift),\n                    hashedRequest = qq.CryptoJS.SHA256(canonicalRequest).toString(),\n                    scope = v4.getScope(signatureSpec.date, options.signatureSpec.region),\n                    stringToSignTemplate = \"AWS4-HMAC-SHA256\\n{}\\n{}\\n{}\";\n\n                return {\n                    hashed: qq.format(stringToSignTemplate, date, scope, hashedRequest),\n                    raw: qq.format(stringToSignTemplate, date, scope, canonicalRequest)\n                };\n            },\n\n            getSignedHeaders: function(headerNames) {\n                var signedHeaders = \"\";\n\n                headerNames.forEach(function(headerName, idx) {\n                    signedHeaders += headerName.toLowerCase();\n\n                    if (idx < headerNames.length - 1) {\n                        signedHeaders += \";\";\n                    }\n                });\n\n                return signedHeaders;\n            },\n\n            signApiRequest: function(signatureConstructor, headersStr, signatureEffort) {\n                var secretKey = credentialsProvider.get().secretKey,\n                    headersPattern = /.+\\n.+\\n(\\d+)\\/(.+)\\/s3\\/.+\\n(.+)/,\n                    matches = headersPattern.exec(headersStr),\n                    dateKey, dateRegionKey, dateRegionServiceKey, signingKey;\n\n                dateKey = qq.CryptoJS.HmacSHA256(matches[1], \"AWS4\" + secretKey);\n                dateRegionKey = qq.CryptoJS.HmacSHA256(matches[2], dateKey);\n                dateRegionServiceKey = qq.CryptoJS.HmacSHA256(\"s3\", dateRegionKey);\n                signingKey = qq.CryptoJS.HmacSHA256(\"aws4_request\", dateRegionServiceKey);\n\n                generateHeaders(signatureConstructor, qq.CryptoJS.HmacSHA256(headersStr, signingKey), signatureEffort);\n            },\n\n            signPolicy: function(policy, signatureEffort, updatedAccessKey, updatedSessionToken) {\n                var policyStr = JSON.stringify(policy),\n                    policyWordArray = qq.CryptoJS.enc.Utf8.parse(policyStr),\n                    base64Policy = qq.CryptoJS.enc.Base64.stringify(policyWordArray),\n                    secretKey = credentialsProvider.get().secretKey,\n                    credentialPattern = /.+\\/(.+)\\/(.+)\\/s3\\/aws4_request/,\n                    credentialCondition = (function() {\n                        var credential = null;\n                        qq.each(policy.conditions, function(key, condition) {\n                            var val = condition[\"x-amz-credential\"];\n                            if (val) {\n                                credential = val;\n                                return false;\n                            }\n                        });\n                        return credential;\n                    }()),\n                    matches, dateKey, dateRegionKey, dateRegionServiceKey, signingKey;\n\n                matches = credentialPattern.exec(credentialCondition);\n                dateKey = qq.CryptoJS.HmacSHA256(matches[1], \"AWS4\" + secretKey);\n                dateRegionKey = qq.CryptoJS.HmacSHA256(matches[2], dateKey);\n                dateRegionServiceKey = qq.CryptoJS.HmacSHA256(\"s3\", dateRegionKey);\n                signingKey = qq.CryptoJS.HmacSHA256(\"aws4_request\", dateRegionServiceKey);\n\n                signatureEffort.success({\n                    policy: base64Policy,\n                    signature: qq.CryptoJS.HmacSHA256(base64Policy, signingKey).toString()\n                }, updatedAccessKey, updatedSessionToken);\n            }\n        };\n\n    qq.extend(options, o, true);\n    credentialsProvider = options.signatureSpec.credentialsProvider;\n\n    function handleSignatureReceived(id, xhrOrXdr, isError) {\n        var responseJson = xhrOrXdr.responseText,\n            pendingSignatureData = pendingSignatures[id],\n            promise = pendingSignatureData.promise,\n            signatureConstructor = pendingSignatureData.signatureConstructor,\n            errorMessage, response;\n\n        delete pendingSignatures[id];\n\n        // Attempt to parse what we would expect to be a JSON response\n        if (responseJson) {\n            try {\n                response = qq.parseJson(responseJson);\n            }\n            catch (error) {\n                options.log(\"Error attempting to parse signature response: \" + error, \"error\");\n            }\n        }\n\n        // If the response is parsable and contains an `error` property, use it as the error message\n        if (response && response.error) {\n            isError = true;\n            errorMessage = response.error;\n        }\n        // If we have received a parsable response, and it has an `invalid` property,\n        // the policy document or request headers may have been tampered with client-side.\n        else if (response && response.invalid) {\n            isError = true;\n            errorMessage = \"Invalid policy document or request headers!\";\n        }\n        // Make sure the response contains policy & signature properties\n        else if (response) {\n            if (options.expectingPolicy && !response.policy) {\n                isError = true;\n                errorMessage = \"Response does not include the base64 encoded policy!\";\n            }\n            else if (!response.signature) {\n                isError = true;\n                errorMessage = \"Response does not include the signature!\";\n            }\n        }\n        // Something unknown went wrong\n        else {\n            isError = true;\n            errorMessage = \"Received an empty or invalid response from the server!\";\n        }\n\n        if (isError) {\n            if (errorMessage) {\n                options.log(errorMessage, \"error\");\n            }\n\n            promise.failure(errorMessage);\n        }\n        else if (signatureConstructor) {\n            generateHeaders(signatureConstructor, response.signature, promise);\n        }\n        else {\n            promise.success(response);\n        }\n    }\n\n    function getStringToSignArtifacts(id, version, requestInfo) {\n        var promise = new qq.Promise(),\n            method = \"POST\",\n            headerNames = [],\n            headersStr = \"\",\n            now = new Date(),\n            endOfUrl, signatureSpec, toSign,\n\n            generateStringToSign = function(requestInfo) {\n                var contentMd5,\n                    headerIndexesToRemove = [];\n\n                qq.each(requestInfo.headers, function(name) {\n                    headerNames.push(name);\n                });\n                headerNames.sort();\n\n                qq.each(headerNames, function(idx, headerName) {\n                    if (qq.indexOf(qq.s3.util.UNSIGNABLE_REST_HEADER_NAMES, headerName) < 0) {\n                        headersStr += headerName.toLowerCase() + \":\" + requestInfo.headers[headerName].trim() + \"\\n\";\n                    }\n                    else if (headerName === \"Content-MD5\") {\n                        contentMd5 = requestInfo.headers[headerName];\n                    }\n                    else {\n                        headerIndexesToRemove.unshift(idx);\n                    }\n                });\n\n                qq.each(headerIndexesToRemove, function(idx, headerIdx) {\n                    headerNames.splice(headerIdx, 1);\n                });\n\n                signatureSpec = {\n                    bucket: requestInfo.bucket,\n                    contentMd5: contentMd5,\n                    contentType: requestInfo.contentType,\n                    date: now,\n                    drift: options.signatureSpec.drift,\n                    endOfUrl: endOfUrl,\n                    hashedContent: requestInfo.hashedContent,\n                    headerNames: headerNames,\n                    headersStr: headersStr,\n                    method: method\n                };\n\n                toSign = version === 2 ? v2.getStringToSign(signatureSpec) : v4.getStringToSign(signatureSpec);\n\n                return {\n                    date: now,\n                    endOfUrl: endOfUrl,\n                    signedHeaders: version === 4 ? v4.getSignedHeaders(signatureSpec.headerNames) : null,\n                    toSign: version === 4 ? toSign.hashed : toSign,\n                    toSignRaw: version === 4 ? toSign.raw : toSign\n                };\n            };\n\n        /*jshint indent:false */\n        switch (requestInfo.type) {\n            case thisSignatureRequester.REQUEST_TYPE.MULTIPART_ABORT:\n                method = \"DELETE\";\n                endOfUrl = qq.format(\"uploadId={}\", requestInfo.uploadId);\n                break;\n            case thisSignatureRequester.REQUEST_TYPE.MULTIPART_INITIATE:\n                endOfUrl = \"uploads\";\n                break;\n            case thisSignatureRequester.REQUEST_TYPE.MULTIPART_COMPLETE:\n                endOfUrl = qq.format(\"uploadId={}\", requestInfo.uploadId);\n                break;\n            case thisSignatureRequester.REQUEST_TYPE.MULTIPART_UPLOAD:\n                method = \"PUT\";\n                endOfUrl = qq.format(\"partNumber={}&uploadId={}\", requestInfo.partNum, requestInfo.uploadId);\n                break;\n        }\n\n        endOfUrl = requestInfo.key + \"?\" + endOfUrl;\n\n        if (version === 4) {\n            v4.getEncodedHashedPayload(requestInfo.content).then(function(hashedContent) {\n                requestInfo.headers[\"x-amz-content-sha256\"] = hashedContent;\n                requestInfo.headers.Host = requestInfo.host;\n                requestInfo.headers[\"x-amz-date\"] = qq.s3.util.getV4PolicyDate(now, options.signatureSpec.drift);\n                requestInfo.hashedContent = hashedContent;\n\n                promise.success(generateStringToSign(requestInfo));\n            }, function (err) {\n                promise.failure(err);\n            });\n        }\n        else {\n            promise.success(generateStringToSign(requestInfo));\n        }\n\n        return promise;\n    }\n\n    function determineSignatureClientSide(id, toBeSigned, signatureEffort, updatedAccessKey, updatedSessionToken) {\n        var updatedHeaders;\n\n        // REST API request\n        if (toBeSigned.signatureConstructor) {\n            if (updatedSessionToken) {\n                updatedHeaders = toBeSigned.signatureConstructor.getHeaders();\n                updatedHeaders[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = updatedSessionToken;\n                toBeSigned.signatureConstructor.withHeaders(updatedHeaders);\n            }\n\n            toBeSigned.signatureConstructor.getToSign(id).then(function(signatureArtifacts) {\n                signApiRequest(toBeSigned.signatureConstructor, signatureArtifacts.stringToSign, signatureEffort);\n            }, function (err) {\n                signatureEffort.failure(err);\n            });\n        }\n        // Form upload (w/ policy document)\n        else {\n            updatedSessionToken && qq.s3.util.refreshPolicyCredentials(toBeSigned, updatedSessionToken);\n            signPolicy(toBeSigned, signatureEffort, updatedAccessKey, updatedSessionToken);\n        }\n    }\n\n    function signPolicy(policy, signatureEffort, updatedAccessKey, updatedSessionToken) {\n        if (options.signatureSpec.version === 4) {\n            v4.signPolicy(policy, signatureEffort, updatedAccessKey, updatedSessionToken);\n        }\n        else {\n            v2.signPolicy(policy, signatureEffort, updatedAccessKey, updatedSessionToken);\n        }\n    }\n\n    function signApiRequest(signatureConstructor, headersStr, signatureEffort) {\n        if (options.signatureSpec.version === 4) {\n            v4.signApiRequest(signatureConstructor, headersStr, signatureEffort);\n        }\n        else {\n            v2.signApiRequest(signatureConstructor, headersStr, signatureEffort);\n        }\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        acceptHeader: \"application/json\",\n        method: options.method,\n        contentType: \"application/json; charset=utf-8\",\n        endpointStore: {\n            get: function() {\n                return options.signatureSpec.endpoint;\n            }\n        },\n        paramsStore: options.paramsStore,\n        maxConnections: options.maxConnections,\n        customHeaders: options.signatureSpec.customHeaders,\n        log: options.log,\n        onComplete: handleSignatureReceived,\n        cors: options.cors\n    }));\n\n    qq.extend(this, {\n        /**\n         * On success, an object containing the parsed JSON response will be passed into the success handler if the\n         * request succeeds.  Otherwise an error message will be passed into the failure method.\n         *\n         * @param id File ID.\n         * @param toBeSigned an Object that holds the item(s) to be signed\n         * @returns {qq.Promise} A promise that is fulfilled when the response has been received.\n         */\n        getSignature: function(id, toBeSigned) {\n            var params = toBeSigned,\n                signatureConstructor = toBeSigned.signatureConstructor,\n                signatureEffort = new qq.Promise(),\n                queryParams;\n\n            if (options.signatureSpec.version === 4) {\n                queryParams = {v4: true};\n            }\n\n            if (credentialsProvider.get().secretKey && qq.CryptoJS) {\n                if (credentialsProvider.get().expiration.getTime() > Date.now()) {\n                    determineSignatureClientSide(id, toBeSigned, signatureEffort);\n                }\n                // If credentials are expired, ask for new ones before attempting to sign request\n                else {\n                    credentialsProvider.onExpired().then(function() {\n                        determineSignatureClientSide(id, toBeSigned,\n                            signatureEffort,\n                            credentialsProvider.get().accessKey,\n                            credentialsProvider.get().sessionToken);\n                    }, function(errorMsg) {\n                        options.log(\"Attempt to update expired credentials apparently failed! Unable to sign request.  \", \"error\");\n                        signatureEffort.failure(\"Unable to sign request - expired credentials.\");\n                    });\n                }\n            }\n            else {\n                options.log(\"Submitting S3 signature request for \" + id);\n\n                if (signatureConstructor) {\n                    signatureConstructor.getToSign(id).then(function(signatureArtifacts) {\n                        params = {headers: signatureArtifacts.stringToSignRaw};\n                        requester.initTransport(id)\n                            .withParams(params)\n                            .withQueryParams(queryParams)\n                            .send();\n                    }, function (err) {\n                        options.log(\"Failed to construct signature. \", \"error\");\n                        signatureEffort.failure(\"Failed to construct signature.\");\n                    });\n                }\n                else {\n                    requester.initTransport(id)\n                        .withParams(params)\n                        .withQueryParams(queryParams)\n                        .send();\n                }\n\n                pendingSignatures[id] = {\n                    promise: signatureEffort,\n                    signatureConstructor: signatureConstructor\n                };\n            }\n\n            return signatureEffort;\n        },\n\n        constructStringToSign: function(type, bucket, host, key) {\n            var headers = {},\n                uploadId, content, contentType, partNum, artifacts;\n\n            return {\n                withHeaders: function(theHeaders) {\n                    headers = theHeaders;\n                    return this;\n                },\n\n                withUploadId: function(theUploadId) {\n                    uploadId = theUploadId;\n                    return this;\n                },\n\n                withContent: function(theContent) {\n                    content = theContent;\n                    return this;\n                },\n\n                withContentType: function(theContentType) {\n                    contentType = theContentType;\n                    return this;\n                },\n\n                withPartNum: function(thePartNum) {\n                    partNum = thePartNum;\n                    return this;\n                },\n\n                getToSign: function(id) {\n                    var sessionToken = credentialsProvider.get().sessionToken,\n                        promise = new qq.Promise(),\n                        adjustedDate = new Date(Date.now() + options.signatureSpec.drift);\n\n                    headers[\"x-amz-date\"] = adjustedDate.toUTCString();\n\n                    if (sessionToken) {\n                        headers[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;\n                    }\n\n                    getStringToSignArtifacts(id, options.signatureSpec.version, {\n                        bucket: bucket,\n                        content: content,\n                        contentType: contentType,\n                        headers: headers,\n                        host: host,\n                        key: key,\n                        partNum: partNum,\n                        type: type,\n                        uploadId: uploadId\n                    }).then(function(_artifacts_) {\n                        artifacts = _artifacts_;\n                        promise.success({\n                            headers: (function() {\n                                if (contentType) {\n                                    headers[\"Content-Type\"] = contentType;\n                                }\n\n                                delete headers.Host; // we don't want this to be set on the XHR-initiated request\n                                return headers;\n                            }()),\n                            date: artifacts.date,\n                            endOfUrl: artifacts.endOfUrl,\n                            signedHeaders: artifacts.signedHeaders,\n                            stringToSign: artifacts.toSign,\n                            stringToSignRaw: artifacts.toSignRaw\n                        });\n                    }, function (err) {\n                        promise.failure(err);\n                    });\n\n                    return promise;\n                },\n\n                getHeaders: function() {\n                    return qq.extend({}, headers);\n                },\n\n                getEndOfUrl: function() {\n                    return artifacts && artifacts.endOfUrl;\n                },\n\n                getRequestDate: function() {\n                    return artifacts && artifacts.date;\n                },\n\n                getSignedHeaders: function() {\n                    return artifacts && artifacts.signedHeaders;\n                }\n            };\n        }\n    });\n};\n\nqq.s3.RequestSigner.prototype.REQUEST_TYPE = {\n    MULTIPART_INITIATE: \"multipart_initiate\",\n    MULTIPART_COMPLETE: \"multipart_complete\",\n    MULTIPART_ABORT: \"multipart_abort\",\n    MULTIPART_UPLOAD: \"multipart_upload\"\n};\n","/*globals qq, XMLHttpRequest*/\n/**\n * Sends a POST request to the server to notify it of a successful upload to an endpoint.  The server is expected to indicate success\n * or failure via the response status.  Specific information about the failure can be passed from the server via an `error`\n * property (by default) in an \"application/json\" response.\n *\n * @param o Options associated with all requests.\n * @constructor\n */\nqq.UploadSuccessAjaxRequester = function(o) {\n    \"use strict\";\n\n    var requester,\n        pendingRequests = [],\n        options = {\n            method: \"POST\",\n            endpoint: null,\n            maxConnections: 3,\n            customHeaders: {},\n            paramsStore: {},\n            cors: {\n                expected: false,\n                sendCredentials: false\n            },\n            log: function(str, level) {}\n        };\n\n    qq.extend(options, o);\n\n    function handleSuccessResponse(id, xhrOrXdr, isError) {\n        var promise = pendingRequests[id],\n            responseJson = xhrOrXdr.responseText,\n            successIndicator = {success: true},\n            failureIndicator = {success: false},\n            parsedResponse;\n\n        delete pendingRequests[id];\n\n        options.log(qq.format(\"Received the following response body to an upload success request for id {}: {}\", id, responseJson));\n\n        try {\n            parsedResponse = qq.parseJson(responseJson);\n\n            // If this is a cross-origin request, the server may return a 200 response w/ error or success properties\n            // in order to ensure any specific error message is picked up by Fine Uploader for all browsers,\n            // since XDomainRequest (used in IE9 and IE8) doesn't give you access to the\n            // response body for an \"error\" response.\n            if (isError || (parsedResponse && (parsedResponse.error || parsedResponse.success === false))) {\n                options.log(\"Upload success request was rejected by the server.\", \"error\");\n                promise.failure(qq.extend(parsedResponse, failureIndicator));\n            }\n            else {\n                options.log(\"Upload success was acknowledged by the server.\");\n                promise.success(qq.extend(parsedResponse, successIndicator));\n            }\n        }\n        catch (error) {\n            // This will be executed if a JSON response is not present.  This is not mandatory, so account for this properly.\n            if (isError) {\n                options.log(qq.format(\"Your server indicated failure in its upload success request response for id {}!\", id), \"error\");\n                promise.failure(failureIndicator);\n            }\n            else {\n                options.log(\"Upload success was acknowledged by the server.\");\n                promise.success(successIndicator);\n            }\n        }\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        acceptHeader: \"application/json\",\n        method: options.method,\n        endpointStore: {\n            get: function() {\n                return options.endpoint;\n            }\n        },\n        paramsStore: options.paramsStore,\n        maxConnections: options.maxConnections,\n        customHeaders: options.customHeaders,\n        log: options.log,\n        onComplete: handleSuccessResponse,\n        cors: options.cors\n    }));\n\n    qq.extend(this, {\n        /**\n         * Sends a request to the server, notifying it that a recently submitted file was successfully sent.\n         *\n         * @param id ID of the associated file\n         * @param spec `Object` with the properties that correspond to important values that we want to\n         * send to the server with this request.\n         * @returns {qq.Promise} A promise to be fulfilled when the response has been received and parsed.  The parsed\n         * payload of the response will be passed into the `failure` or `success` promise method.\n         */\n        sendSuccessRequest: function(id, spec) {\n            var promise = new qq.Promise();\n\n            options.log(\"Submitting upload success request/notification for \" + id);\n\n            requester.initTransport(id)\n                .withParams(spec)\n                .send();\n\n            pendingRequests[id] = promise;\n\n            return promise;\n        }\n    });\n};\n","/*globals qq*/\n/**\n * Ajax requester used to send an [\"Initiate Multipart Upload\"](http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadInitiate.html)\n * request to S3 via the REST API.\n *\n * @param o Options from the caller - will override the defaults.\n * @constructor\n */\nqq.s3.InitiateMultipartAjaxRequester = function(o) {\n    \"use strict\";\n\n    var requester,\n        pendingInitiateRequests = {},\n        options = {\n            filenameParam: \"qqfilename\",\n            method: \"POST\",\n            endpointStore: null,\n            paramsStore: null,\n            signatureSpec: null,\n            aclStore: null,\n            reducedRedundancy: false,\n            serverSideEncryption: false,\n            maxConnections: 3,\n            getContentType: function(id) {},\n            getBucket: function(id) {},\n            getHost: function(id) {},\n            getKey: function(id) {},\n            getName: function(id) {},\n            log: function(str, level) {}\n        },\n        getSignatureAjaxRequester;\n\n    qq.extend(options, o);\n\n    getSignatureAjaxRequester = new qq.s3.RequestSigner({\n        endpointStore: options.endpointStore,\n        signatureSpec: options.signatureSpec,\n        cors: options.cors,\n        log: options.log\n    });\n\n    /**\n     * Determine all headers for the \"Initiate MPU\" request, including the \"Authorization\" header, which must be determined\n     * by the local server.  This is a promissory function.  If the server responds with a signature, the headers\n     * (including the Authorization header) will be passed into the success method of the promise.  Otherwise, the failure\n     * method on the promise will be called.\n     *\n     * @param id Associated file ID\n     * @returns {qq.Promise}\n     */\n    function getHeaders(id) {\n        var bucket = options.getBucket(id),\n            host = options.getHost(id),\n            headers = {},\n            promise = new qq.Promise(),\n            key = options.getKey(id),\n            signatureConstructor;\n\n        headers[\"x-amz-acl\"] = options.aclStore.get(id);\n\n        if (options.reducedRedundancy) {\n            headers[qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;\n        }\n\n        if (options.serverSideEncryption) {\n            headers[qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;\n        }\n\n        headers[qq.s3.util.AWS_PARAM_PREFIX + options.filenameParam] = encodeURIComponent(options.getName(id));\n\n        qq.each(options.paramsStore.get(id), function(name, val) {\n            if (qq.indexOf(qq.s3.util.UNPREFIXED_PARAM_NAMES, name) >= 0) {\n                headers[name] = val;\n            }\n            else {\n                headers[qq.s3.util.AWS_PARAM_PREFIX + name] = encodeURIComponent(val);\n            }\n        });\n\n        signatureConstructor = getSignatureAjaxRequester.constructStringToSign\n            (getSignatureAjaxRequester.REQUEST_TYPE.MULTIPART_INITIATE, bucket, host, key)\n            .withContentType(options.getContentType(id))\n            .withHeaders(headers);\n\n        // Ask the local server to sign the request.  Use this signature to form the Authorization header.\n        getSignatureAjaxRequester.getSignature(id, {signatureConstructor: signatureConstructor}).then(promise.success, promise.failure);\n\n        return promise;\n    }\n\n    /**\n     * Called by the base ajax requester when the response has been received.  We definitively determine here if the\n     * \"Initiate MPU\" request has been a success or not.\n     *\n     * @param id ID associated with the file.\n     * @param xhr `XMLHttpRequest` object containing the response, among other things.\n     * @param isError A boolean indicating success or failure according to the base ajax requester (primarily based on status code).\n     */\n    function handleInitiateRequestComplete(id, xhr, isError) {\n        var promise = pendingInitiateRequests[id],\n            domParser = new DOMParser(),\n            responseDoc = domParser.parseFromString(xhr.responseText, \"application/xml\"),\n            uploadIdElements, messageElements, uploadId, errorMessage, status;\n\n        delete pendingInitiateRequests[id];\n\n        // The base ajax requester may declare the request to be a failure based on status code.\n        if (isError) {\n            status = xhr.status;\n\n            messageElements = responseDoc.getElementsByTagName(\"Message\");\n            if (messageElements.length > 0) {\n                errorMessage = messageElements[0].textContent;\n            }\n        }\n        // If the base ajax requester has not declared this a failure, make sure we can retrieve the uploadId from the response.\n        else {\n            uploadIdElements = responseDoc.getElementsByTagName(\"UploadId\");\n            if (uploadIdElements.length > 0) {\n                uploadId = uploadIdElements[0].textContent;\n            }\n            else {\n                errorMessage = \"Upload ID missing from request\";\n            }\n        }\n\n        // Either fail the promise (passing a descriptive error message) or declare it a success (passing the upload ID)\n        if (uploadId === undefined) {\n            if (errorMessage) {\n                options.log(qq.format(\"Specific problem detected initiating multipart upload request for {}: '{}'.\", id, errorMessage), \"error\");\n            }\n            else {\n                options.log(qq.format(\"Unexplained error with initiate multipart upload request for {}.  Status code {}.\", id, status), \"error\");\n            }\n\n            promise.failure(\"Problem initiating upload request.\", xhr);\n        }\n        else {\n            options.log(qq.format(\"Initiate multipart upload request successful for {}.  Upload ID is {}\", id, uploadId));\n            promise.success(uploadId, xhr);\n        }\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        method: options.method,\n        contentType: null,\n        endpointStore: options.endpointStore,\n        maxConnections: options.maxConnections,\n        allowXRequestedWithAndCacheControl: false, //These headers are not necessary & would break some installations if added\n        log: options.log,\n        onComplete: handleInitiateRequestComplete,\n        successfulResponseCodes: {\n            POST: [200]\n        }\n    }));\n\n    qq.extend(this, {\n        /**\n         * Sends the \"Initiate MPU\" request to AWS via the REST API.  First, though, we must get a signature from the\n         * local server for the request.  If all is successful, the uploadId from AWS will be passed into the promise's\n         * success handler. Otherwise, an error message will ultimately be passed into the failure method.\n         *\n         * @param id The ID associated with the file\n         * @returns {qq.Promise}\n         */\n        send: function(id) {\n            var promise = new qq.Promise();\n\n            getHeaders(id).then(function(headers, endOfUrl) {\n                options.log(\"Submitting S3 initiate multipart upload request for \" + id);\n\n                pendingInitiateRequests[id] = promise;\n                requester.initTransport(id)\n                    .withPath(endOfUrl)\n                    .withHeaders(headers)\n                    .send();\n            }, promise.failure);\n\n            return promise;\n        }\n    });\n};\n","/*globals qq*/\n/**\n * Ajax requester used to send an [\"Complete Multipart Upload\"](http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html)\n * request to S3 via the REST API.\n *\n * @param o Options passed by the creator, to overwrite any default option values.\n * @constructor\n */\nqq.s3.CompleteMultipartAjaxRequester = function(o) {\n    \"use strict\";\n\n    var requester,\n        pendingCompleteRequests = {},\n        options = {\n            method: \"POST\",\n            contentType: \"text/xml\",\n            endpointStore: null,\n            signatureSpec: null,\n            maxConnections: 3,\n            getBucket: function(id) {},\n            getHost: function(id) {},\n            getKey: function(id) {},\n            log: function(str, level) {}\n        },\n        getSignatureAjaxRequester;\n\n    qq.extend(options, o);\n\n    // Transport for requesting signatures (for the \"Complete\" requests) from the local server\n    getSignatureAjaxRequester = new qq.s3.RequestSigner({\n        endpointStore: options.endpointStore,\n        signatureSpec: options.signatureSpec,\n        cors: options.cors,\n        log: options.log\n    });\n\n    /**\n     * Attach all required headers (including Authorization) to the \"Complete\" request.  This is a promissory function\n     * that will fulfill the associated promise once all headers have been attached or when an error has occurred that\n     * prevents headers from being attached.\n     *\n     * @returns {qq.Promise}\n     */\n    function getHeaders(id, uploadId, body) {\n        var headers = {},\n            promise = new qq.Promise(),\n            bucket = options.getBucket(id),\n            host = options.getHost(id),\n            signatureConstructor = getSignatureAjaxRequester.constructStringToSign\n                (getSignatureAjaxRequester.REQUEST_TYPE.MULTIPART_COMPLETE, bucket, host, options.getKey(id))\n                .withUploadId(uploadId)\n                .withContent(body)\n                .withContentType(\"application/xml; charset=UTF-8\");\n\n        // Ask the local server to sign the request.  Use this signature to form the Authorization header.\n        getSignatureAjaxRequester.getSignature(id, {signatureConstructor: signatureConstructor}).then(promise.success, promise.failure);\n\n        return promise;\n    }\n\n    /**\n     * Called by the base ajax requester when the response has been received.  We definitively determine here if the\n     * \"Complete MPU\" request has been a success or not.\n     *\n     * @param id ID associated with the file.\n     * @param xhr `XMLHttpRequest` object containing the response, among other things.\n     * @param isError A boolean indicating success or failure according to the base ajax requester (primarily based on status code).\n     */\n    function handleCompleteRequestComplete(id, xhr, isError) {\n        var promise = pendingCompleteRequests[id],\n            domParser = new DOMParser(),\n            bucket = options.getBucket(id),\n            key = options.getKey(id),\n            responseDoc = domParser.parseFromString(xhr.responseText, \"application/xml\"),\n            bucketEls = responseDoc.getElementsByTagName(\"Bucket\"),\n            keyEls = responseDoc.getElementsByTagName(\"Key\");\n\n        delete pendingCompleteRequests[id];\n\n        options.log(qq.format(\"Complete response status {}, body = {}\", xhr.status, xhr.responseText));\n\n        // If the base requester has determine this a failure, give up.\n        if (isError) {\n            options.log(qq.format(\"Complete Multipart Upload request for {} failed with status {}.\", id, xhr.status), \"error\");\n        }\n        else {\n            // Make sure the correct bucket and key has been specified in the XML response from AWS.\n            if (bucketEls.length && keyEls.length) {\n                if (bucketEls[0].textContent !== bucket) {\n                    isError = true;\n                    options.log(qq.format(\"Wrong bucket in response to Complete Multipart Upload request for {}.\", id), \"error\");\n                }\n\n                // TODO Compare key name from response w/ expected key name if AWS ever fixes the encoding of key names in this response.\n            }\n            else {\n                isError = true;\n                options.log(qq.format(\"Missing bucket and/or key in response to Complete Multipart Upload request for {}.\", id), \"error\");\n            }\n        }\n\n        if (isError) {\n            promise.failure(\"Problem combining the file parts!\", xhr);\n        }\n        else {\n            promise.success({}, xhr);\n        }\n    }\n\n    /**\n     * @param etagEntries Array of objects containing `etag` values and their associated `part` numbers.\n     * @returns {string} XML string containing the body to send with the \"Complete\" request\n     */\n    function getCompleteRequestBody(etagEntries) {\n        var doc = document.implementation.createDocument(null, \"CompleteMultipartUpload\", null);\n\n        // The entries MUST be sorted by part number, per the AWS API spec.\n        etagEntries.sort(function(a, b) {\n            return a.part - b.part;\n        });\n\n        // Construct an XML document for each pair of etag/part values that correspond to part uploads.\n        qq.each(etagEntries, function(idx, etagEntry) {\n            var part = etagEntry.part,\n                etag = etagEntry.etag,\n                partEl = doc.createElement(\"Part\"),\n                partNumEl = doc.createElement(\"PartNumber\"),\n                partNumTextEl = doc.createTextNode(part),\n                etagTextEl = doc.createTextNode(etag),\n                etagEl = doc.createElement(\"ETag\");\n\n            etagEl.appendChild(etagTextEl);\n            partNumEl.appendChild(partNumTextEl);\n            partEl.appendChild(partNumEl);\n            partEl.appendChild(etagEl);\n            qq(doc).children()[0].appendChild(partEl);\n        });\n\n        // Turn the resulting XML document into a string fit for transport.\n        return new XMLSerializer().serializeToString(doc);\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        method: options.method,\n        contentType: \"application/xml; charset=UTF-8\",\n        endpointStore: options.endpointStore,\n        maxConnections: options.maxConnections,\n        allowXRequestedWithAndCacheControl: false, //These headers are not necessary & would break some installations if added\n        log: options.log,\n        onComplete: handleCompleteRequestComplete,\n        successfulResponseCodes: {\n            POST: [200]\n        }\n    }));\n\n    qq.extend(this, {\n        /**\n         * Sends the \"Complete\" request and fulfills the returned promise when the success of this request is known.\n         *\n         * @param id ID associated with the file.\n         * @param uploadId AWS uploadId for this file\n         * @param etagEntries Array of objects containing `etag` values and their associated `part` numbers.\n         * @returns {qq.Promise}\n         */\n        send: function(id, uploadId, etagEntries) {\n            var promise = new qq.Promise(),\n                body = getCompleteRequestBody(etagEntries);\n\n            getHeaders(id, uploadId, body).then(function(headers, endOfUrl) {\n                options.log(\"Submitting S3 complete multipart upload request for \" + id);\n\n                pendingCompleteRequests[id] = promise;\n                delete headers[\"Content-Type\"];\n\n                requester.initTransport(id)\n                    .withPath(endOfUrl)\n                    .withHeaders(headers)\n                    .withPayload(body)\n                    .send();\n            }, promise.failure);\n\n            return promise;\n        }\n    });\n};\n","/*globals qq */\n/**\n * Ajax requester used to send an [\"Abort Multipart Upload\"](http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadAbort.html)\n * request to S3 via the REST API.\n\n * @param o\n * @constructor\n */\nqq.s3.AbortMultipartAjaxRequester = function(o) {\n    \"use strict\";\n\n    var requester,\n        options = {\n            method: \"DELETE\",\n            endpointStore: null,\n            signatureSpec: null,\n            maxConnections: 3,\n            getBucket: function(id) {},\n            getHost: function(id) {},\n            getKey: function(id) {},\n            log: function(str, level) {}\n        },\n        getSignatureAjaxRequester;\n\n    qq.extend(options, o);\n\n    // Transport for requesting signatures (for the \"Complete\" requests) from the local server\n    getSignatureAjaxRequester = new qq.s3.RequestSigner({\n        endpointStore: options.endpointStore,\n        signatureSpec: options.signatureSpec,\n        cors: options.cors,\n        log: options.log\n    });\n\n    /**\n     * Attach all required headers (including Authorization) to the \"Abort\" request.  This is a promissory function\n     * that will fulfill the associated promise once all headers have been attached or when an error has occurred that\n     * prevents headers from being attached.\n     *\n     * @param id Associated file ID\n     * @param uploadId ID of the associated upload, according to AWS\n     * @returns {qq.Promise}\n     */\n    function getHeaders(id, uploadId) {\n        var headers = {},\n            promise = new qq.Promise(),\n            bucket = options.getBucket(id),\n            host = options.getHost(id),\n            signatureConstructor = getSignatureAjaxRequester.constructStringToSign\n                (getSignatureAjaxRequester.REQUEST_TYPE.MULTIPART_ABORT, bucket, host, options.getKey(id))\n                .withUploadId(uploadId);\n\n        // Ask the local server to sign the request.  Use this signature to form the Authorization header.\n        getSignatureAjaxRequester.getSignature(id, {signatureConstructor: signatureConstructor}).then(promise.success, promise.failure);\n\n        return promise;\n    }\n\n    /**\n     * Called by the base ajax requester when the response has been received.  We definitively determine here if the\n     * \"Abort MPU\" request has been a success or not.\n     *\n     * @param id ID associated with the file.\n     * @param xhr `XMLHttpRequest` object containing the response, among other things.\n     * @param isError A boolean indicating success or failure according to the base ajax requester (primarily based on status code).\n     */\n    function handleAbortRequestComplete(id, xhr, isError) {\n        var domParser = new DOMParser(),\n            responseDoc = domParser.parseFromString(xhr.responseText, \"application/xml\"),\n            errorEls = responseDoc.getElementsByTagName(\"Error\"),\n            awsErrorMsg;\n\n        options.log(qq.format(\"Abort response status {}, body = {}\", xhr.status, xhr.responseText));\n\n        // If the base requester has determine this a failure, give up.\n        if (isError) {\n            options.log(qq.format(\"Abort Multipart Upload request for {} failed with status {}.\", id, xhr.status), \"error\");\n        }\n        else {\n            // Make sure the correct bucket and key has been specified in the XML response from AWS.\n            if (errorEls.length) {\n                isError = true;\n                awsErrorMsg = responseDoc.getElementsByTagName(\"Message\")[0].textContent;\n                options.log(qq.format(\"Failed to Abort Multipart Upload request for {}.  Error: {}\", id, awsErrorMsg), \"error\");\n            }\n            else {\n                options.log(qq.format(\"Abort MPU request succeeded for file ID {}.\", id));\n            }\n        }\n    }\n\n    requester = qq.extend(this, new qq.AjaxRequester({\n        validMethods: [\"DELETE\"],\n        method: options.method,\n        contentType: null,\n        endpointStore: options.endpointStore,\n        maxConnections: options.maxConnections,\n        allowXRequestedWithAndCacheControl: false, //These headers are not necessary & would break some installations if added\n        log: options.log,\n        onComplete: handleAbortRequestComplete,\n        successfulResponseCodes: {\n            DELETE: [204]\n        }\n    }));\n\n    qq.extend(this, {\n        /**\n         * Sends the \"Abort\" request.\n         *\n         * @param id ID associated with the file.\n         * @param uploadId AWS uploadId for this file\n         */\n        send: function(id, uploadId) {\n            getHeaders(id, uploadId).then(function(headers, endOfUrl) {\n                options.log(\"Submitting S3 Abort multipart upload request for \" + id);\n                requester.initTransport(id)\n                    .withPath(endOfUrl)\n                    .withHeaders(headers)\n                    .send();\n            });\n        }\n    });\n};\n","/*globals qq */\n/**\n * Upload handler used by the upload to S3 module that depends on File API support, and, therefore, makes use of\n * `XMLHttpRequest` level 2 to upload `File`s and `Blob`s directly to S3 buckets via the associated AWS API.\n *\n * If chunking is supported and enabled, the S3 Multipart Upload REST API is utilized.\n *\n * @param spec Options passed from the base handler\n * @param proxy Callbacks & methods used to query for or push out data/changes\n */\nqq.s3.XhrUploadHandler = function(spec, proxy) {\n    \"use strict\";\n\n    var getName = proxy.getName,\n        log = proxy.log,\n        clockDrift = spec.clockDrift,\n        expectedStatus = 200,\n        onGetBucket = spec.getBucket,\n        onGetHost = spec.getHost,\n        onGetKeyName = spec.getKeyName,\n        filenameParam = spec.filenameParam,\n        paramsStore = spec.paramsStore,\n        endpointStore = spec.endpointStore,\n        aclStore = spec.aclStore,\n        reducedRedundancy = spec.objectProperties.reducedRedundancy,\n        region = spec.objectProperties.region,\n        serverSideEncryption = spec.objectProperties.serverSideEncryption,\n        validation = spec.validation,\n        signature = qq.extend({region: region, drift: clockDrift}, spec.signature),\n        handler = this,\n        credentialsProvider = spec.signature.credentialsProvider,\n\n        chunked = {\n            // Sends a \"Complete Multipart Upload\" request and then signals completion of the upload\n            // when the response to this request has been parsed.\n            combine: function(id) {\n                var uploadId = handler._getPersistableData(id).uploadId,\n                    etagMap = handler._getPersistableData(id).etags,\n                    result = new qq.Promise();\n\n                requesters.completeMultipart.send(id, uploadId, etagMap).then(\n                    result.success,\n\n                    function failure(reason, xhr) {\n                        result.failure(upload.done(id, xhr).response, xhr);\n                    }\n                );\n\n                return result;\n            },\n\n            // The last step in handling a chunked upload.  This is called after each chunk has been sent.\n            // The request may be successful, or not.  If it was successful, we must extract the \"ETag\" element\n            // in the XML response and store that along with the associated part number.\n            // We need these items to \"Complete\" the multipart upload after all chunks have been successfully sent.\n            done: function(id, xhr, chunkIdx) {\n                var response = upload.response.parse(id, xhr),\n                    etag;\n\n                if (response.success) {\n                    etag = xhr.getResponseHeader(\"ETag\");\n\n                    if (!handler._getPersistableData(id).etags) {\n                        handler._getPersistableData(id).etags = [];\n                    }\n                    handler._getPersistableData(id).etags.push({part: chunkIdx + 1, etag: etag});\n                }\n            },\n\n            /**\n             * Determines headers that must be attached to the chunked (Multipart Upload) request.  One of these headers is an\n             * Authorization value, which must be determined by asking the local server to sign the request first.  So, this\n             * function returns a promise.  Once all headers are determined, the `success` method of the promise is called with\n             * the headers object.  If there was some problem determining the headers, we delegate to the caller's `failure`\n             * callback.\n             *\n             * @param id File ID\n             * @param chunkIdx Index of the chunk to PUT\n             * @returns {qq.Promise}\n             */\n            initHeaders: function(id, chunkIdx, blob) {\n                var headers = {},\n                    bucket = upload.bucket.getName(id),\n                    host = upload.host.getName(id),\n                    key = upload.key.urlSafe(id),\n                    promise = new qq.Promise(),\n                    signatureConstructor = requesters.restSignature.constructStringToSign\n                        (requesters.restSignature.REQUEST_TYPE.MULTIPART_UPLOAD, bucket, host, key)\n                        .withPartNum(chunkIdx + 1)\n                        .withContent(blob)\n                        .withUploadId(handler._getPersistableData(id).uploadId);\n\n                // Ask the local server to sign the request.  Use this signature to form the Authorization header.\n                requesters.restSignature.getSignature(id + \".\" + chunkIdx, {signatureConstructor: signatureConstructor}).then(promise.success, promise.failure);\n\n                return promise;\n            },\n\n            put: function(id, chunkIdx) {\n                var xhr = handler._createXhr(id, chunkIdx),\n                    chunkData = handler._getChunkData(id, chunkIdx),\n                    domain = spec.endpointStore.get(id),\n                    promise = new qq.Promise();\n\n                // Add appropriate headers to the multipart upload request.\n                // Once these have been determined (asynchronously) attach the headers and send the chunk.\n                chunked.initHeaders(id, chunkIdx, chunkData.blob).then(function(headers, endOfUrl) {\n                    if (xhr._cancelled) {\n                        log(qq.format(\"Upload of item {}.{} cancelled. Upload will not start after successful signature request.\", id, chunkIdx));\n                        promise.failure({error: \"Chunk upload cancelled\"});\n                    }\n                    else {\n                        var url = domain + \"/\" + endOfUrl;\n                        handler._registerProgressHandler(id, chunkIdx, chunkData.size);\n                        upload.track(id, xhr, chunkIdx).then(promise.success, promise.failure);\n                        xhr.open(\"PUT\", url, true);\n\n                        qq.each(headers, function(name, val) {\n                            xhr.setRequestHeader(name, val);\n                        });\n\n                        xhr.send(chunkData.blob);\n                    }\n                }, function() {\n                    promise.failure({error: \"Problem signing the chunk!\"}, xhr);\n                });\n\n                return promise;\n            },\n\n            send: function(id, chunkIdx) {\n                var promise = new qq.Promise();\n\n                chunked.setup(id).then(\n                    // The \"Initiate\" request succeeded.  We are ready to send the first chunk.\n                    function() {\n                        chunked.put(id, chunkIdx).then(promise.success, promise.failure);\n                    },\n\n                    // We were unable to initiate the chunked upload process.\n                    function(errorMessage, xhr) {\n                        promise.failure({error: errorMessage}, xhr);\n                    }\n                );\n\n                return promise;\n            },\n\n            /**\n             * Sends an \"Initiate Multipart Upload\" request to S3 via the REST API, but only if the MPU has not already been\n             * initiated.\n             *\n             * @param id Associated file ID\n             * @returns {qq.Promise} A promise that is fulfilled when the initiate request has been sent and the response has been parsed.\n             */\n            setup: function(id) {\n                var promise = new qq.Promise(),\n                    uploadId = handler._getPersistableData(id).uploadId,\n                    uploadIdPromise = new qq.Promise();\n\n                if (!uploadId) {\n                    handler._getPersistableData(id).uploadId = uploadIdPromise;\n                    requesters.initiateMultipart.send(id).then(\n                        function(uploadId) {\n                            handler._getPersistableData(id).uploadId = uploadId;\n                            uploadIdPromise.success(uploadId);\n                            promise.success(uploadId);\n                        },\n                        function(errorMsg, xhr) {\n                            handler._getPersistableData(id).uploadId = null;\n                            promise.failure(errorMsg, xhr);\n                            uploadIdPromise.failure(errorMsg, xhr);\n                        }\n                    );\n                }\n                else if (uploadId instanceof qq.Promise) {\n                    uploadId.then(function(uploadId) {\n                        promise.success(uploadId);\n                    });\n                }\n                else {\n                    promise.success(uploadId);\n                }\n\n                return promise;\n            }\n        },\n\n        requesters = {\n            abortMultipart: new qq.s3.AbortMultipartAjaxRequester({\n                endpointStore: endpointStore,\n                signatureSpec: signature,\n                cors: spec.cors,\n                log: log,\n                getBucket: function(id) {\n                    return upload.bucket.getName(id);\n                },\n                getHost: function(id) {\n                    return upload.host.getName(id);\n                },\n                getKey: function(id) {\n                    return upload.key.urlSafe(id);\n                }\n            }),\n\n            completeMultipart: new qq.s3.CompleteMultipartAjaxRequester({\n                endpointStore: endpointStore,\n                signatureSpec: signature,\n                cors: spec.cors,\n                log: log,\n                getBucket: function(id) {\n                    return upload.bucket.getName(id);\n                },\n                getHost: function(id) {\n                    return upload.host.getName(id);\n                },\n                getKey: function(id) {\n                    return upload.key.urlSafe(id);\n                }\n            }),\n\n            initiateMultipart: new qq.s3.InitiateMultipartAjaxRequester({\n                filenameParam: filenameParam,\n                endpointStore: endpointStore,\n                paramsStore: paramsStore,\n                signatureSpec: signature,\n                aclStore: aclStore,\n                reducedRedundancy: reducedRedundancy,\n                serverSideEncryption: serverSideEncryption,\n                cors: spec.cors,\n                log: log,\n                getContentType: function(id) {\n                    return handler._getMimeType(id);\n                },\n                getBucket: function(id) {\n                    return upload.bucket.getName(id);\n                },\n                getHost: function(id) {\n                    return upload.host.getName(id);\n                },\n                getKey: function(id) {\n                    return upload.key.urlSafe(id);\n                },\n                getName: function(id) {\n                    return getName(id);\n                }\n            }),\n\n            policySignature: new qq.s3.RequestSigner({\n                expectingPolicy: true,\n                signatureSpec: signature,\n                cors: spec.cors,\n                log: log\n            }),\n\n            restSignature: new qq.s3.RequestSigner({\n                endpointStore: endpointStore,\n                signatureSpec: signature,\n                cors: spec.cors,\n                log: log\n            })\n        },\n\n        simple = {\n            /**\n             * Used for simple (non-chunked) uploads to determine the parameters to send along with the request.  Part of this\n             * process involves asking the local server to sign the request, so this function returns a promise.  The promise\n             * is fulfilled when all parameters are determined, or when we determine that all parameters cannot be calculated\n             * due to some error.\n             *\n             * @param id File ID\n             * @returns {qq.Promise}\n             */\n            initParams: function(id) {\n                /*jshint -W040 */\n                var customParams = paramsStore.get(id);\n                customParams[filenameParam] = getName(id);\n\n                return qq.s3.util.generateAwsParams({\n                    endpoint: endpointStore.get(id),\n                    clockDrift: clockDrift,\n                    params: customParams,\n                    type: handler._getMimeType(id),\n                    bucket: upload.bucket.getName(id),\n                    key: handler.getThirdPartyFileId(id),\n                    accessKey: credentialsProvider.get().accessKey,\n                    sessionToken: credentialsProvider.get().sessionToken,\n                    acl: aclStore.get(id),\n                    expectedStatus: expectedStatus,\n                    minFileSize: validation.minSizeLimit,\n                    maxFileSize: validation.maxSizeLimit,\n                    reducedRedundancy: reducedRedundancy,\n                    region: region,\n                    serverSideEncryption: serverSideEncryption,\n                    signatureVersion: signature.version,\n                    log: log\n                },\n                qq.bind(requesters.policySignature.getSignature, this, id));\n            },\n\n            send: function(id) {\n                var promise = new qq.Promise(),\n                    xhr = handler._createXhr(id),\n                    fileOrBlob = handler.getFile(id);\n\n                handler._registerProgressHandler(id);\n                upload.track(id, xhr).then(promise.success, promise.failure);\n\n                // Delegate to a function the sets up the XHR request and notifies us when it is ready to be sent, along w/ the payload.\n                simple.setup(id, xhr, fileOrBlob).then(function(toSend) {\n                    log(\"Sending upload request for \" + id);\n                    xhr.send(toSend);\n                }, promise.failure);\n\n                return promise;\n            },\n\n            /**\n             * Starts the upload process by delegating to an async function that determine parameters to be attached to the\n             * request.  If all params can be determined, we are called back with the params and the caller of this function is\n             * informed by invoking the `success` method on the promise returned by this function, passing the payload of the\n             * request.  If some error occurs here, we delegate to a function that signals a failure for this upload attempt.\n             *\n             * Note that this is only used by the simple (non-chunked) upload process.\n             *\n             * @param id File ID\n             * @param xhr XMLHttpRequest to use for the upload\n             * @param fileOrBlob `File` or `Blob` to send\n             * @returns {qq.Promise}\n             */\n            setup: function(id, xhr, fileOrBlob) {\n                var formData = new FormData(),\n                    endpoint = endpointStore.get(id),\n                    url = endpoint,\n                    promise = new qq.Promise();\n\n                simple.initParams(id).then(\n                    // Success - all params determined\n                    function(awsParams) {\n                        xhr.open(\"POST\", url, true);\n\n                        qq.obj2FormData(awsParams, formData);\n\n                        // AWS requires the file field be named \"file\".\n                        formData.append(\"file\", fileOrBlob);\n\n                        promise.success(formData);\n                    },\n\n                    // Failure - we couldn't determine some params (likely the signature)\n                    function(errorMessage) {\n                        promise.failure({error: errorMessage});\n                    }\n                );\n\n                return promise;\n            }\n        },\n\n        upload = {\n            /**\n             * Note that this is called when an upload has reached a termination point,\n             * regardless of success/failure.  For example, it is called when we have\n             * encountered an error during the upload or when the file may have uploaded successfully.\n             *\n             * @param id file ID\n             */\n            bucket: {\n                promise: function(id) {\n                    var promise = new qq.Promise(),\n                        cachedBucket = handler._getFileState(id).bucket;\n\n                    if (cachedBucket) {\n                        promise.success(cachedBucket);\n                    }\n                    else {\n                        onGetBucket(id).then(function(bucket) {\n                            handler._getFileState(id).bucket = bucket;\n                            promise.success(bucket);\n                        }, promise.failure);\n                    }\n\n                    return promise;\n                },\n\n                getName: function(id) {\n                    return handler._getFileState(id).bucket;\n                }\n            },\n\n            host: {\n                promise: function(id) {\n                    var promise = new qq.Promise(),\n                        cachedHost = handler._getFileState(id).host;\n\n                    if (cachedHost) {\n                        promise.success(cachedHost);\n                    }\n                    else {\n                        onGetHost(id).then(function(host) {\n                            handler._getFileState(id).host = host;\n                            promise.success(host);\n                        }, promise.failure);\n                    }\n\n                    return promise;\n                },\n\n                getName: function(id) {\n                    return handler._getFileState(id).host;\n                }\n            },\n\n            done: function(id, xhr) {\n                var response = upload.response.parse(id, xhr),\n                    isError = response.success !== true;\n\n                if (isError && upload.response.shouldReset(response.code)) {\n                    log(\"This is an unrecoverable error, we must restart the upload entirely on the next retry attempt.\", \"error\");\n                    response.reset = true;\n                }\n\n                return {\n                    success: !isError,\n                    response: response\n                };\n            },\n\n            key: {\n                promise: function(id) {\n                    var promise = new qq.Promise(),\n                        key = handler.getThirdPartyFileId(id);\n\n                    /* jshint eqnull:true */\n                    if (key == null) {\n                        handler._setThirdPartyFileId(id, promise);\n                        onGetKeyName(id, getName(id)).then(\n                            function(keyName) {\n                                handler._setThirdPartyFileId(id, keyName);\n                                promise.success(keyName);\n                            },\n                            function(errorReason) {\n                                handler._setThirdPartyFileId(id, null);\n                                promise.failure(errorReason);\n                            }\n                        );\n                    }\n                    else if (qq.isGenericPromise(key)) {\n                        key.then(promise.success, promise.failure);\n                    }\n                    else {\n                        promise.success(key);\n                    }\n\n                    return promise;\n                },\n\n                urlSafe: function(id) {\n                    var encodedKey = handler.getThirdPartyFileId(id);\n                    return qq.s3.util.uriEscapePath(encodedKey);\n                }\n            },\n\n            response: {\n                parse: function(id, xhr) {\n                    var response = {},\n                        parsedErrorProps;\n\n                    try {\n                        log(qq.format(\"Received response status {} with body: {}\", xhr.status, xhr.responseText));\n\n                        if (xhr.status === expectedStatus) {\n                            response.success = true;\n                        }\n                        else {\n                            parsedErrorProps = upload.response.parseError(xhr.responseText);\n\n                            if (parsedErrorProps) {\n                                response.error = parsedErrorProps.message;\n                                response.code = parsedErrorProps.code;\n                            }\n                        }\n                    }\n                    catch (error) {\n                        log(\"Error when attempting to parse xhr response text (\" + error.message + \")\", \"error\");\n                    }\n\n                    return response;\n                },\n\n                /**\n                 * This parses an XML response by extracting the \"Message\" and \"Code\" elements that accompany AWS error responses.\n                 *\n                 * @param awsResponseXml XML response from AWS\n                 * @returns {object} Object w/ `code` and `message` properties, or undefined if we couldn't find error info in the XML document.\n                 */\n                parseError: function(awsResponseXml) {\n                    var parser = new DOMParser(),\n                        parsedDoc = parser.parseFromString(awsResponseXml, \"application/xml\"),\n                        errorEls = parsedDoc.getElementsByTagName(\"Error\"),\n                        errorDetails = {},\n                        codeEls, messageEls;\n\n                    if (errorEls.length) {\n                        codeEls = parsedDoc.getElementsByTagName(\"Code\");\n                        messageEls = parsedDoc.getElementsByTagName(\"Message\");\n\n                        if (messageEls.length) {\n                            errorDetails.message = messageEls[0].textContent;\n                        }\n\n                        if (codeEls.length) {\n                            errorDetails.code = codeEls[0].textContent;\n                        }\n\n                        return errorDetails;\n                    }\n                },\n\n                // Determine if the upload should be restarted on the next retry attempt\n                // based on the error code returned in the response from AWS.\n                shouldReset: function(errorCode) {\n                    /*jshint -W014 */\n                    return errorCode === \"EntityTooSmall\"\n                        || errorCode === \"InvalidPart\"\n                        || errorCode === \"InvalidPartOrder\"\n                        || errorCode === \"NoSuchUpload\";\n                }\n            },\n\n            start: function(id, optChunkIdx) {\n                var promise = new qq.Promise();\n\n                upload.key.promise(id).then(function() {\n                    upload.bucket.promise(id).then(function() {\n                        upload.host.promise(id).then(function() {\n                            /* jshint eqnull:true */\n                            if (optChunkIdx == null) {\n                                simple.send(id).then(promise.success, promise.failure);\n                            }\n                            else {\n                                chunked.send(id, optChunkIdx).then(promise.success, promise.failure);\n                            }\n                        });\n                    });\n                },\n                function(errorReason) {\n                    promise.failure({error: errorReason});\n                });\n\n                return promise;\n            },\n\n            track: function(id, xhr, optChunkIdx) {\n                var promise = new qq.Promise();\n\n                xhr.onreadystatechange = function() {\n                    if (xhr.readyState === 4) {\n                        var result;\n\n                        /* jshint eqnull:true */\n                        if (optChunkIdx == null) {\n                            result = upload.done(id, xhr);\n                            promise[result.success ? \"success\" : \"failure\"](result.response, xhr);\n                        }\n                        else {\n                            chunked.done(id, xhr, optChunkIdx);\n                            result = upload.done(id, xhr);\n                            promise[result.success ? \"success\" : \"failure\"](result.response, xhr);\n                        }\n                    }\n                };\n\n                return promise;\n            }\n        };\n\n    qq.extend(this, {\n        uploadChunk: upload.start,\n        uploadFile: upload.start\n    });\n\n    qq.extend(this, new qq.XhrUploadHandler({\n        options: qq.extend({namespace: \"s3\"}, spec),\n        proxy: qq.extend({getEndpoint: spec.endpointStore.get}, proxy)\n    }));\n\n    qq.override(this, function(super_) {\n        return {\n            expunge: function(id) {\n                var uploadId = handler._getPersistableData(id) && handler._getPersistableData(id).uploadId,\n                    existedInLocalStorage = handler._maybeDeletePersistedChunkData(id);\n\n                if (uploadId !== undefined && existedInLocalStorage) {\n                    requesters.abortMultipart.send(id, uploadId);\n                }\n\n                super_.expunge(id);\n            },\n\n            finalizeChunks: function(id) {\n                return chunked.combine(id);\n            },\n\n            _getLocalStorageId: function(id) {\n                var baseStorageId = super_._getLocalStorageId(id),\n                    bucketName = upload.bucket.getName(id);\n\n                return baseStorageId + \"-\" + bucketName;\n            }\n        };\n    });\n};\n","/*globals qq */\n/**\n * Upload handler used by the upload to S3 module that assumes the current user agent does not have any support for the\n * File API, and, therefore, makes use of iframes and forms to submit the files directly to S3 buckets via the associated\n * AWS API.\n *\n * @param options Options passed from the base handler\n * @param proxy Callbacks & methods used to query for or push out data/changes\n */\nqq.s3.FormUploadHandler = function(options, proxy) {\n    \"use strict\";\n\n    var handler = this,\n        clockDrift = options.clockDrift,\n        onUuidChanged = proxy.onUuidChanged,\n        getName = proxy.getName,\n        getUuid = proxy.getUuid,\n        log = proxy.log,\n        onGetBucket = options.getBucket,\n        onGetKeyName = options.getKeyName,\n        filenameParam = options.filenameParam,\n        paramsStore = options.paramsStore,\n        endpointStore = options.endpointStore,\n        aclStore = options.aclStore,\n        reducedRedundancy = options.objectProperties.reducedRedundancy,\n        region = options.objectProperties.region,\n        serverSideEncryption = options.objectProperties.serverSideEncryption,\n        validation = options.validation,\n        signature = options.signature,\n        successRedirectUrl = options.iframeSupport.localBlankPagePath,\n        credentialsProvider = options.signature.credentialsProvider,\n        getSignatureAjaxRequester = new qq.s3.RequestSigner({\n            signatureSpec: signature,\n            cors: options.cors,\n            log: log\n        });\n\n    if (successRedirectUrl === undefined) {\n        throw new Error(\"successRedirectEndpoint MUST be defined if you intend to use browsers that do not support the File API!\");\n    }\n\n    /**\n     * Attempt to parse the contents of an iframe after receiving a response from the server.  If the contents cannot be\n     * read (perhaps due to a security error) it is safe to assume that the upload was not successful since Amazon should\n     * have redirected to a known endpoint that should provide a parseable response.\n     *\n     * @param id ID of the associated file\n     * @param iframe target of the form submit\n     * @returns {boolean} true if the contents can be read, false otherwise\n     */\n    function isValidResponse(id, iframe) {\n        var response,\n            endpoint = options.endpointStore.get(id),\n            bucket = handler._getFileState(id).bucket,\n            doc,\n            innerHtml,\n            responseData;\n\n        //IE may throw an \"access is denied\" error when attempting to access contentDocument on the iframe in some cases\n        try {\n            // iframe.contentWindow.document - for IE<7\n            doc = iframe.contentDocument || iframe.contentWindow.document;\n            innerHtml = doc.body.innerHTML;\n\n            responseData = qq.s3.util.parseIframeResponse(iframe);\n            if (responseData.bucket === bucket &&\n                responseData.key === qq.s3.util.encodeQueryStringParam(handler.getThirdPartyFileId(id))) {\n\n                return true;\n            }\n\n            log(\"Response from AWS included an unexpected bucket or key name.\", \"error\");\n\n        }\n        catch (error) {\n            log(\"Error when attempting to parse form upload response (\" + error.message + \")\", \"error\");\n        }\n\n        return false;\n    }\n\n    function generateAwsParams(id) {\n        /*jshint -W040 */\n        var customParams = paramsStore.get(id);\n\n        customParams[filenameParam] = getName(id);\n\n        return qq.s3.util.generateAwsParams({\n            endpoint: endpointStore.get(id),\n            clockDrift: clockDrift,\n            params: customParams,\n            bucket: handler._getFileState(id).bucket,\n            key: handler.getThirdPartyFileId(id),\n            accessKey: credentialsProvider.get().accessKey,\n            sessionToken: credentialsProvider.get().sessionToken,\n            acl: aclStore.get(id),\n            minFileSize: validation.minSizeLimit,\n            maxFileSize: validation.maxSizeLimit,\n            successRedirectUrl: successRedirectUrl,\n            reducedRedundancy: reducedRedundancy,\n            region: region,\n            serverSideEncryption: serverSideEncryption,\n            signatureVersion: signature.version,\n            log: log\n        },\n        qq.bind(getSignatureAjaxRequester.getSignature, this, id));\n    }\n\n    /**\n     * Creates form, that will be submitted to iframe\n     */\n    function createForm(id, iframe) {\n        var promise = new qq.Promise(),\n            method = \"POST\",\n            endpoint = options.endpointStore.get(id),\n            fileName = getName(id);\n\n        generateAwsParams(id).then(function(params) {\n            var form = handler._initFormForUpload({\n                method: method,\n                endpoint: endpoint,\n                params: params,\n                paramsInBody: true,\n                targetName: iframe.name\n            });\n\n            promise.success(form);\n        }, function(errorMessage) {\n            promise.failure(errorMessage);\n            handleFinishedUpload(id, iframe, fileName, {error: errorMessage});\n        });\n\n        return promise;\n    }\n\n    function handleUpload(id) {\n        var iframe = handler._createIframe(id),\n            input = handler.getInput(id),\n            promise = new qq.Promise();\n\n        createForm(id, iframe).then(function(form) {\n            form.appendChild(input);\n\n            // Register a callback when the response comes in from S3\n            handler._attachLoadEvent(iframe, function(response) {\n                log(\"iframe loaded\");\n\n                // If the common response handler has determined success or failure immediately\n                if (response) {\n                    // If there is something fundamentally wrong with the response (such as iframe content is not accessible)\n                    if (response.success === false) {\n                        log(\"Amazon likely rejected the upload request\", \"error\");\n                        promise.failure(response);\n                    }\n                }\n                // The generic response (iframe onload) handler was not able to make a determination regarding the success of the request\n                else {\n                    response = {};\n                    response.success = isValidResponse(id, iframe);\n\n                    // If the more specific response handle detected a problem with the response from S3\n                    if (response.success === false) {\n                        log(\"A success response was received by Amazon, but it was invalid in some way.\", \"error\");\n                        promise.failure(response);\n                    }\n                    else {\n                        qq.extend(response, qq.s3.util.parseIframeResponse(iframe));\n                        promise.success(response);\n                    }\n                }\n\n                handleFinishedUpload(id, iframe);\n            });\n\n            log(\"Sending upload request for \" + id);\n            form.submit();\n            qq(form).remove();\n        }, promise.failure);\n\n        return promise;\n    }\n\n    function handleFinishedUpload(id, iframe) {\n        handler._detachLoadEvent(id);\n        iframe && qq(iframe).remove();\n    }\n\n    qq.extend(this, new qq.FormUploadHandler({\n        options: {\n            isCors: false,\n            inputName: \"file\"\n        },\n\n        proxy: {\n            onCancel: options.onCancel,\n            onUuidChanged: onUuidChanged,\n            getName: getName,\n            getUuid: getUuid,\n            log: log\n        }\n    }));\n\n    qq.extend(this, {\n        uploadFile: function(id) {\n            var name = getName(id),\n                promise = new qq.Promise();\n\n            if (handler.getThirdPartyFileId(id)) {\n                if (handler._getFileState(id).bucket) {\n                    handleUpload(id).then(promise.success, promise.failure);\n                }\n                else {\n                    onGetBucket(id).then(function(bucket) {\n                        handler._getFileState(id).bucket = bucket;\n                        handleUpload(id).then(promise.success, promise.failure);\n                    });\n                }\n            }\n            else {\n                // The S3 uploader module will either calculate the key or ask the server for it\n                // and will call us back once it is known.\n                onGetKeyName(id, name).then(function(key) {\n                    onGetBucket(id).then(function(bucket) {\n                        handler._getFileState(id).bucket = bucket;\n                        handler._setThirdPartyFileId(id, key);\n                        handleUpload(id).then(promise.success, promise.failure);\n                    }, function(errorReason) {\n                        promise.failure({error: errorReason});\n                    });\n                }, function(errorReason) {\n                    promise.failure({error: errorReason});\n                });\n            }\n\n            return promise;\n        }\n    });\n};\n","/*globals qq, document, CustomEvent*/\nqq.DragAndDrop = function(o) {\n    \"use strict\";\n\n    var options,\n        HIDE_ZONES_EVENT_NAME = \"qq-hidezones\",\n        HIDE_BEFORE_ENTER_ATTR = \"qq-hide-dropzone\",\n        uploadDropZones = [],\n        droppedFiles = [],\n        disposeSupport = new qq.DisposeSupport();\n\n    options = {\n        dropZoneElements: [],\n        allowMultipleItems: true,\n        classes: {\n            dropActive: null\n        },\n        callbacks: new qq.DragAndDrop.callbacks()\n    };\n\n    qq.extend(options, o, true);\n\n    function uploadDroppedFiles(files, uploadDropZone) {\n        // We need to convert the `FileList` to an actual `Array` to avoid iteration issues\n        var filesAsArray = Array.prototype.slice.call(files);\n\n        options.callbacks.dropLog(\"Grabbed \" + files.length + \" dropped files.\");\n        uploadDropZone.dropDisabled(false);\n        options.callbacks.processingDroppedFilesComplete(filesAsArray, uploadDropZone.getElement());\n    }\n\n    function traverseFileTree(entry) {\n        var parseEntryPromise = new qq.Promise();\n\n        if (entry.isFile) {\n            entry.file(function(file) {\n                var name = entry.name,\n                    fullPath = entry.fullPath,\n                    indexOfNameInFullPath = fullPath.indexOf(name);\n\n                // remove file name from full path string\n                fullPath = fullPath.substr(0, indexOfNameInFullPath);\n\n                // remove leading slash in full path string\n                if (fullPath.charAt(0) === \"/\") {\n                    fullPath = fullPath.substr(1);\n                }\n\n                file.qqPath = fullPath;\n                droppedFiles.push(file);\n                parseEntryPromise.success();\n            },\n            function(fileError) {\n                options.callbacks.dropLog(\"Problem parsing '\" + entry.fullPath + \"'.  FileError code \" + fileError.code + \".\", \"error\");\n                parseEntryPromise.failure();\n            });\n        }\n        else if (entry.isDirectory) {\n            getFilesInDirectory(entry).then(\n                function allEntriesRead(entries) {\n                    var entriesLeft = entries.length;\n\n                    qq.each(entries, function(idx, entry) {\n                        traverseFileTree(entry).done(function() {\n                            entriesLeft -= 1;\n\n                            if (entriesLeft === 0) {\n                                parseEntryPromise.success();\n                            }\n                        });\n                    });\n\n                    if (!entries.length) {\n                        parseEntryPromise.success();\n                    }\n                },\n\n                function readFailure(fileError) {\n                    options.callbacks.dropLog(\"Problem parsing '\" + entry.fullPath + \"'.  FileError code \" + fileError.code + \".\", \"error\");\n                    parseEntryPromise.failure();\n                }\n            );\n        }\n\n        return parseEntryPromise;\n    }\n\n    // Promissory.  Guaranteed to read all files in the root of the passed directory.\n    function getFilesInDirectory(entry, reader, accumEntries, existingPromise) {\n        var promise = existingPromise || new qq.Promise(),\n            dirReader = reader || entry.createReader();\n\n        dirReader.readEntries(\n            function readSuccess(entries) {\n                var newEntries = accumEntries ? accumEntries.concat(entries) : entries;\n\n                if (entries.length) {\n                    setTimeout(function() { // prevent stack overflow, however unlikely\n                        getFilesInDirectory(entry, dirReader, newEntries, promise);\n                    }, 0);\n                }\n                else {\n                    promise.success(newEntries);\n                }\n            },\n\n            promise.failure\n        );\n\n        return promise;\n    }\n\n    function handleDataTransfer(dataTransfer, uploadDropZone) {\n        var pendingFolderPromises = [],\n            handleDataTransferPromise = new qq.Promise();\n\n        options.callbacks.processingDroppedFiles();\n        uploadDropZone.dropDisabled(true);\n\n        if (dataTransfer.files.length > 1 && !options.allowMultipleItems) {\n            options.callbacks.processingDroppedFilesComplete([]);\n            options.callbacks.dropError(\"tooManyFilesError\", \"\");\n            uploadDropZone.dropDisabled(false);\n            handleDataTransferPromise.failure();\n        }\n        else {\n            droppedFiles = [];\n\n            if (qq.isFolderDropSupported(dataTransfer)) {\n                qq.each(dataTransfer.items, function(idx, item) {\n                    var entry = item.webkitGetAsEntry();\n\n                    if (entry) {\n                        //due to a bug in Chrome's File System API impl - #149735\n                        if (entry.isFile) {\n                            droppedFiles.push(item.getAsFile());\n                        }\n\n                        else {\n                            pendingFolderPromises.push(traverseFileTree(entry).done(function() {\n                                pendingFolderPromises.pop();\n                                if (pendingFolderPromises.length === 0) {\n                                    handleDataTransferPromise.success();\n                                }\n                            }));\n                        }\n                    }\n                });\n            }\n            else {\n                droppedFiles = dataTransfer.files;\n            }\n\n            if (pendingFolderPromises.length === 0) {\n                handleDataTransferPromise.success();\n            }\n        }\n\n        return handleDataTransferPromise;\n    }\n\n    function setupDropzone(dropArea) {\n        var dropZone = new qq.UploadDropZone({\n            HIDE_ZONES_EVENT_NAME: HIDE_ZONES_EVENT_NAME,\n            element: dropArea,\n            onEnter: function(e) {\n                qq(dropArea).addClass(options.classes.dropActive);\n                e.stopPropagation();\n            },\n            onLeaveNotDescendants: function(e) {\n                qq(dropArea).removeClass(options.classes.dropActive);\n            },\n            onDrop: function(e) {\n                handleDataTransfer(e.dataTransfer, dropZone).then(\n                    function() {\n                        uploadDroppedFiles(droppedFiles, dropZone);\n                    },\n                    function() {\n                        options.callbacks.dropLog(\"Drop event DataTransfer parsing failed.  No files will be uploaded.\", \"error\");\n                    }\n                );\n            }\n        });\n\n        disposeSupport.addDisposer(function() {\n            dropZone.dispose();\n        });\n\n        qq(dropArea).hasAttribute(HIDE_BEFORE_ENTER_ATTR) && qq(dropArea).hide();\n\n        uploadDropZones.push(dropZone);\n\n        return dropZone;\n    }\n\n    function isFileDrag(dragEvent) {\n        var fileDrag;\n\n        qq.each(dragEvent.dataTransfer.types, function(key, val) {\n            if (val === \"Files\") {\n                fileDrag = true;\n                return false;\n            }\n        });\n\n        return fileDrag;\n    }\n\n    // Attempt to determine when the file has left the document.  It is not always possible to detect this\n    // in all cases, but it is generally possible in all browsers, with a few exceptions.\n    //\n    // Exceptions:\n    // * IE10+ & Safari: We can't detect a file leaving the document if the Explorer window housing the file\n    //                   overlays the browser window.\n    // * IE10+: If the file is dragged out of the window too quickly, IE does not set the expected values of the\n    //          event's X & Y properties.\n    function leavingDocumentOut(e) {\n        if (qq.firefox()) {\n            return !e.relatedTarget;\n        }\n\n        if (qq.safari()) {\n            return e.x < 0 || e.y < 0;\n        }\n\n        return e.x === 0 && e.y === 0;\n    }\n\n    function setupDragDrop() {\n        var dropZones = options.dropZoneElements,\n\n            maybeHideDropZones = function() {\n                setTimeout(function() {\n                    qq.each(dropZones, function(idx, dropZone) {\n                        qq(dropZone).hasAttribute(HIDE_BEFORE_ENTER_ATTR) && qq(dropZone).hide();\n                        qq(dropZone).removeClass(options.classes.dropActive);\n                    });\n                }, 10);\n            };\n\n        qq.each(dropZones, function(idx, dropZone) {\n            var uploadDropZone = setupDropzone(dropZone);\n\n            // IE <= 9 does not support the File API used for drag+drop uploads\n            if (dropZones.length && qq.supportedFeatures.fileDrop) {\n                disposeSupport.attach(document, \"dragenter\", function(e) {\n                    if (!uploadDropZone.dropDisabled() && isFileDrag(e)) {\n                        qq.each(dropZones, function(idx, dropZone) {\n                            // We can't apply styles to non-HTMLElements, since they lack the `style` property.\n                            // Also, if the drop zone isn't initially hidden, let's not mess with `style.display`.\n                            if (dropZone instanceof HTMLElement &&\n                                qq(dropZone).hasAttribute(HIDE_BEFORE_ENTER_ATTR)) {\n\n                                qq(dropZone).css({display: \"block\"});\n                            }\n                        });\n                    }\n                });\n            }\n        });\n\n        disposeSupport.attach(document, \"dragleave\", function(e) {\n            if (leavingDocumentOut(e)) {\n                maybeHideDropZones();\n            }\n        });\n\n        // Just in case we were not able to detect when a dragged file has left the document,\n        // hide all relevant drop zones the next time the mouse enters the document.\n        // Note that mouse events such as this one are not fired during drag operations.\n        disposeSupport.attach(qq(document).children()[0], \"mouseenter\", function(e) {\n            maybeHideDropZones();\n        });\n\n        disposeSupport.attach(document, \"drop\", function(e) {\n            e.preventDefault();\n            maybeHideDropZones();\n        });\n\n        disposeSupport.attach(document, HIDE_ZONES_EVENT_NAME, maybeHideDropZones);\n    }\n\n    setupDragDrop();\n\n    qq.extend(this, {\n        setupExtraDropzone: function(element) {\n            options.dropZoneElements.push(element);\n            setupDropzone(element);\n        },\n\n        removeDropzone: function(element) {\n            var i,\n                dzs = options.dropZoneElements;\n\n            for (i in dzs) {\n                if (dzs[i] === element) {\n                    return dzs.splice(i, 1);\n                }\n            }\n        },\n\n        dispose: function() {\n            disposeSupport.dispose();\n            qq.each(uploadDropZones, function(idx, dropZone) {\n                dropZone.dispose();\n            });\n        }\n    });\n};\n\nqq.DragAndDrop.callbacks = function() {\n    \"use strict\";\n\n    return {\n        processingDroppedFiles: function() {},\n        processingDroppedFilesComplete: function(files, targetEl) {},\n        dropError: function(code, errorSpecifics) {\n            qq.log(\"Drag & drop error code '\" + code + \" with these specifics: '\" + errorSpecifics + \"'\", \"error\");\n        },\n        dropLog: function(message, level) {\n            qq.log(message, level);\n        }\n    };\n};\n\nqq.UploadDropZone = function(o) {\n    \"use strict\";\n\n    var disposeSupport = new qq.DisposeSupport(),\n        options, element, preventDrop, dropOutsideDisabled;\n\n    options = {\n        element: null,\n        onEnter: function(e) {},\n        onLeave: function(e) {},\n        // is not fired when leaving element by hovering descendants\n        onLeaveNotDescendants: function(e) {},\n        onDrop: function(e) {}\n    };\n\n    qq.extend(options, o);\n    element = options.element;\n\n    function dragoverShouldBeCanceled() {\n        return qq.safari() || (qq.firefox() && qq.windows());\n    }\n\n    function disableDropOutside(e) {\n        // run only once for all instances\n        if (!dropOutsideDisabled) {\n\n            // for these cases we need to catch onDrop to reset dropArea\n            if (dragoverShouldBeCanceled) {\n                disposeSupport.attach(document, \"dragover\", function(e) {\n                    e.preventDefault();\n                });\n            } else {\n                disposeSupport.attach(document, \"dragover\", function(e) {\n                    if (e.dataTransfer) {\n                        e.dataTransfer.dropEffect = \"none\";\n                        e.preventDefault();\n                    }\n                });\n            }\n\n            dropOutsideDisabled = true;\n        }\n    }\n\n    function isValidFileDrag(e) {\n        // e.dataTransfer currently causing IE errors\n        // IE9 does NOT support file API, so drag-and-drop is not possible\n        if (!qq.supportedFeatures.fileDrop) {\n            return false;\n        }\n\n        var effectTest, dt = e.dataTransfer,\n        // do not check dt.types.contains in webkit, because it crashes safari 4\n        isSafari = qq.safari();\n\n        // dt.effectAllowed is none in Safari 5\n        // dt.types.contains check is for firefox\n\n        // dt.effectAllowed crashes IE 11 & 10 when files have been dragged from\n        // the filesystem\n        effectTest = qq.ie() && qq.supportedFeatures.fileDrop ? true : dt.effectAllowed !== \"none\";\n        return dt && effectTest && (dt.files || (!isSafari && dt.types.contains && dt.types.contains(\"Files\")));\n    }\n\n    function isOrSetDropDisabled(isDisabled) {\n        if (isDisabled !== undefined) {\n            preventDrop = isDisabled;\n        }\n        return preventDrop;\n    }\n\n    function triggerHidezonesEvent() {\n        var hideZonesEvent;\n\n        function triggerUsingOldApi() {\n            hideZonesEvent = document.createEvent(\"Event\");\n            hideZonesEvent.initEvent(options.HIDE_ZONES_EVENT_NAME, true, true);\n        }\n\n        if (window.CustomEvent) {\n            try {\n                hideZonesEvent = new CustomEvent(options.HIDE_ZONES_EVENT_NAME);\n            }\n            catch (err) {\n                triggerUsingOldApi();\n            }\n        }\n        else {\n            triggerUsingOldApi();\n        }\n\n        document.dispatchEvent(hideZonesEvent);\n    }\n\n    function attachEvents() {\n        disposeSupport.attach(element, \"dragover\", function(e) {\n            if (!isValidFileDrag(e)) {\n                return;\n            }\n\n            // dt.effectAllowed crashes IE 11 & 10 when files have been dragged from\n            // the filesystem\n            var effect = qq.ie() && qq.supportedFeatures.fileDrop ? null : e.dataTransfer.effectAllowed;\n            if (effect === \"move\" || effect === \"linkMove\") {\n                e.dataTransfer.dropEffect = \"move\"; // for FF (only move allowed)\n            } else {\n                e.dataTransfer.dropEffect = \"copy\"; // for Chrome\n            }\n\n            e.stopPropagation();\n            e.preventDefault();\n        });\n\n        disposeSupport.attach(element, \"dragenter\", function(e) {\n            if (!isOrSetDropDisabled()) {\n                if (!isValidFileDrag(e)) {\n                    return;\n                }\n                options.onEnter(e);\n            }\n        });\n\n        disposeSupport.attach(element, \"dragleave\", function(e) {\n            if (!isValidFileDrag(e)) {\n                return;\n            }\n\n            options.onLeave(e);\n\n            var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);\n            // do not fire when moving a mouse over a descendant\n            if (qq(this).contains(relatedTarget)) {\n                return;\n            }\n\n            options.onLeaveNotDescendants(e);\n        });\n\n        disposeSupport.attach(element, \"drop\", function(e) {\n            if (!isOrSetDropDisabled()) {\n                if (!isValidFileDrag(e)) {\n                    return;\n                }\n\n                e.preventDefault();\n                e.stopPropagation();\n                options.onDrop(e);\n\n                triggerHidezonesEvent();\n            }\n        });\n    }\n\n    disableDropOutside();\n    attachEvents();\n\n    qq.extend(this, {\n        dropDisabled: function(isDisabled) {\n            return isOrSetDropDisabled(isDisabled);\n        },\n\n        dispose: function() {\n            disposeSupport.dispose();\n        },\n\n        getElement: function() {\n            return element;\n        }\n    });\n};\n","/*globals qq */\n/**\n * Defines the public API for FineUploader mode.\n */\n(function() {\n    \"use strict\";\n\n    qq.uiPublicApi = {\n        addInitialFiles: function(cannedFileList) {\n            this._parent.prototype.addInitialFiles.apply(this, arguments);\n            this._templating.addCacheToDom();\n        },\n\n        clearStoredFiles: function() {\n            this._parent.prototype.clearStoredFiles.apply(this, arguments);\n            this._templating.clearFiles();\n        },\n\n        addExtraDropzone: function(element) {\n            this._dnd && this._dnd.setupExtraDropzone(element);\n        },\n\n        removeExtraDropzone: function(element) {\n            if (this._dnd) {\n                return this._dnd.removeDropzone(element);\n            }\n        },\n\n        getItemByFileId: function(id) {\n            if (!this._templating.isHiddenForever(id)) {\n                return this._templating.getFileContainer(id);\n            }\n        },\n\n        reset: function() {\n            this._parent.prototype.reset.apply(this, arguments);\n            this._templating.reset();\n\n            if (!this._options.button && this._templating.getButton()) {\n                this._defaultButtonId = this._createUploadButton({\n                    element: this._templating.getButton(),\n                    title: this._options.text.fileInputTitle\n                }).getButtonId();\n            }\n\n            if (this._dnd) {\n                this._dnd.dispose();\n                this._dnd = this._setupDragAndDrop();\n            }\n\n            this._totalFilesInBatch = 0;\n            this._filesInBatchAddedToUi = 0;\n\n            this._setupClickAndEditEventHandlers();\n        },\n\n        setName: function(id, newName) {\n            var formattedFilename = this._options.formatFileName(newName);\n\n            this._parent.prototype.setName.apply(this, arguments);\n            this._templating.updateFilename(id, formattedFilename);\n        },\n\n        pauseUpload: function(id) {\n            var paused = this._parent.prototype.pauseUpload.apply(this, arguments);\n\n            paused && this._templating.uploadPaused(id);\n            return paused;\n        },\n\n        continueUpload: function(id) {\n            var continued = this._parent.prototype.continueUpload.apply(this, arguments);\n\n            continued && this._templating.uploadContinued(id);\n            return continued;\n        },\n\n        getId: function(fileContainerOrChildEl) {\n            return this._templating.getFileId(fileContainerOrChildEl);\n        },\n\n        getDropTarget: function(fileId) {\n            var file = this.getFile(fileId);\n\n            return file.qqDropTarget;\n        }\n    };\n\n    /**\n     * Defines the private (internal) API for FineUploader mode.\n     */\n    qq.uiPrivateApi = {\n        _getButton: function(buttonId) {\n            var button = this._parent.prototype._getButton.apply(this, arguments);\n\n            if (!button) {\n                if (buttonId === this._defaultButtonId) {\n                    button = this._templating.getButton();\n                }\n            }\n\n            return button;\n        },\n\n        _removeFileItem: function(fileId) {\n            this._templating.removeFile(fileId);\n        },\n\n        _setupClickAndEditEventHandlers: function() {\n            this._fileButtonsClickHandler = qq.FileButtonsClickHandler && this._bindFileButtonsClickEvent();\n\n            // A better approach would be to check specifically for focusin event support by querying the DOM API,\n            // but the DOMFocusIn event is not exposed as a property, so we have to resort to UA string sniffing.\n            this._focusinEventSupported = !qq.firefox();\n\n            if (this._isEditFilenameEnabled())\n            {\n                this._filenameClickHandler = this._bindFilenameClickEvent();\n                this._filenameInputFocusInHandler = this._bindFilenameInputFocusInEvent();\n                this._filenameInputFocusHandler = this._bindFilenameInputFocusEvent();\n            }\n        },\n\n        _setupDragAndDrop: function() {\n            var self = this,\n                dropZoneElements = this._options.dragAndDrop.extraDropzones,\n                templating = this._templating,\n                defaultDropZone = templating.getDropZone();\n\n            defaultDropZone && dropZoneElements.push(defaultDropZone);\n\n            return new qq.DragAndDrop({\n                dropZoneElements: dropZoneElements,\n                allowMultipleItems: this._options.multiple,\n                classes: {\n                    dropActive: this._options.classes.dropActive\n                },\n                callbacks: {\n                    processingDroppedFiles: function() {\n                        templating.showDropProcessing();\n                    },\n                    processingDroppedFilesComplete: function(files, targetEl) {\n                        templating.hideDropProcessing();\n\n                        qq.each(files, function(idx, file) {\n                            file.qqDropTarget = targetEl;\n                        });\n\n                        if (files.length) {\n                            self.addFiles(files, null, null);\n                        }\n                    },\n                    dropError: function(code, errorData) {\n                        self._itemError(code, errorData);\n                    },\n                    dropLog: function(message, level) {\n                        self.log(message, level);\n                    }\n                }\n            });\n        },\n\n        _bindFileButtonsClickEvent: function() {\n            var self = this;\n\n            return new qq.FileButtonsClickHandler({\n                templating: this._templating,\n\n                log: function(message, lvl) {\n                    self.log(message, lvl);\n                },\n\n                onDeleteFile: function(fileId) {\n                    self.deleteFile(fileId);\n                },\n\n                onCancel: function(fileId) {\n                    self.cancel(fileId);\n                },\n\n                onRetry: function(fileId) {\n                    self.retry(fileId);\n                },\n\n                onPause: function(fileId) {\n                    self.pauseUpload(fileId);\n                },\n\n                onContinue: function(fileId) {\n                    self.continueUpload(fileId);\n                },\n\n                onGetName: function(fileId) {\n                    return self.getName(fileId);\n                }\n            });\n        },\n\n        _isEditFilenameEnabled: function() {\n            /*jshint -W014 */\n            return this._templating.isEditFilenamePossible()\n                && !this._options.autoUpload\n                && qq.FilenameClickHandler\n                && qq.FilenameInputFocusHandler\n                && qq.FilenameInputFocusHandler;\n        },\n\n        _filenameEditHandler: function() {\n            var self = this,\n                templating = this._templating;\n\n            return {\n                templating: templating,\n                log: function(message, lvl) {\n                    self.log(message, lvl);\n                },\n                onGetUploadStatus: function(fileId) {\n                    return self.getUploads({id: fileId}).status;\n                },\n                onGetName: function(fileId) {\n                    return self.getName(fileId);\n                },\n                onSetName: function(id, newName) {\n                    self.setName(id, newName);\n                },\n                onEditingStatusChange: function(id, isEditing) {\n                    var qqInput = qq(templating.getEditInput(id)),\n                        qqFileContainer = qq(templating.getFileContainer(id));\n\n                    if (isEditing) {\n                        qqInput.addClass(\"qq-editing\");\n                        templating.hideFilename(id);\n                        templating.hideEditIcon(id);\n                    }\n                    else {\n                        qqInput.removeClass(\"qq-editing\");\n                        templating.showFilename(id);\n                        templating.showEditIcon(id);\n                    }\n\n                    // Force IE8 and older to repaint\n                    qqFileContainer.addClass(\"qq-temp\").removeClass(\"qq-temp\");\n                }\n            };\n        },\n\n        _onUploadStatusChange: function(id, oldStatus, newStatus) {\n            this._parent.prototype._onUploadStatusChange.apply(this, arguments);\n\n            if (this._isEditFilenameEnabled()) {\n                // Status for a file exists before it has been added to the DOM, so we must be careful here.\n                if (this._templating.getFileContainer(id) && newStatus !== qq.status.SUBMITTED) {\n                    this._templating.markFilenameEditable(id);\n                    this._templating.hideEditIcon(id);\n                }\n            }\n\n            if (oldStatus === qq.status.UPLOAD_RETRYING && newStatus === qq.status.UPLOADING) {\n                this._templating.hideRetry(id);\n                this._templating.setStatusText(id);\n                qq(this._templating.getFileContainer(id)).removeClass(this._classes.retrying);\n            }\n            else if (newStatus === qq.status.UPLOAD_FAILED) {\n                this._templating.hidePause(id);\n            }\n        },\n\n        _bindFilenameInputFocusInEvent: function() {\n            var spec = qq.extend({}, this._filenameEditHandler());\n\n            return new qq.FilenameInputFocusInHandler(spec);\n        },\n\n        _bindFilenameInputFocusEvent: function() {\n            var spec = qq.extend({}, this._filenameEditHandler());\n\n            return new qq.FilenameInputFocusHandler(spec);\n        },\n\n        _bindFilenameClickEvent: function() {\n            var spec = qq.extend({}, this._filenameEditHandler());\n\n            return new qq.FilenameClickHandler(spec);\n        },\n\n        _storeForLater: function(id) {\n            this._parent.prototype._storeForLater.apply(this, arguments);\n            this._templating.hideSpinner(id);\n        },\n\n        _onAllComplete: function(successful, failed) {\n            this._parent.prototype._onAllComplete.apply(this, arguments);\n            this._templating.resetTotalProgress();\n        },\n\n        _onSubmit: function(id, name) {\n            var file = this.getFile(id);\n\n            if (file && file.qqPath && this._options.dragAndDrop.reportDirectoryPaths) {\n                this._paramsStore.addReadOnly(id, {\n                    qqpath: file.qqPath\n                });\n            }\n\n            this._parent.prototype._onSubmit.apply(this, arguments);\n            this._addToList(id, name);\n        },\n\n        // The file item has been added to the DOM.\n        _onSubmitted: function(id) {\n            // If the edit filename feature is enabled, mark the filename element as \"editable\" and the associated edit icon\n            if (this._isEditFilenameEnabled()) {\n                this._templating.markFilenameEditable(id);\n                this._templating.showEditIcon(id);\n\n                // If the focusin event is not supported, we must add a focus handler to the newly create edit filename text input\n                if (!this._focusinEventSupported) {\n                    this._filenameInputFocusHandler.addHandler(this._templating.getEditInput(id));\n                }\n            }\n        },\n\n        // Update the progress bar & percentage as the file is uploaded\n        _onProgress: function(id, name, loaded, total) {\n            this._parent.prototype._onProgress.apply(this, arguments);\n\n            this._templating.updateProgress(id, loaded, total);\n\n            if (total === 0 || Math.round(loaded / total * 100) === 100) {\n                this._templating.hideCancel(id);\n                this._templating.hidePause(id);\n                this._templating.hideProgress(id);\n                this._templating.setStatusText(id, this._options.text.waitingForResponse);\n\n                // If ~last byte was sent, display total file size\n                this._displayFileSize(id);\n            }\n            else {\n                // If still uploading, display percentage - total size is actually the total request(s) size\n                this._displayFileSize(id, loaded, total);\n            }\n        },\n\n        _onTotalProgress: function(loaded, total) {\n            this._parent.prototype._onTotalProgress.apply(this, arguments);\n            this._templating.updateTotalProgress(loaded, total);\n        },\n\n        _onComplete: function(id, name, result, xhr) {\n            var parentRetVal = this._parent.prototype._onComplete.apply(this, arguments),\n                templating = this._templating,\n                fileContainer = templating.getFileContainer(id),\n                self = this;\n\n            function completeUpload(result) {\n                // If this file is not represented in the templating module, perhaps it was hidden intentionally.\n                // If so, don't perform any UI-related tasks related to this file.\n                if (!fileContainer) {\n                    return;\n                }\n\n                templating.setStatusText(id);\n\n                qq(fileContainer).removeClass(self._classes.retrying);\n                templating.hideProgress(id);\n\n                if (self.getUploads({id: id}).status !== qq.status.UPLOAD_FAILED) {\n                    templating.hideCancel(id);\n                }\n                templating.hideSpinner(id);\n\n                if (result.success) {\n                    self._markFileAsSuccessful(id);\n                }\n                else {\n                    qq(fileContainer).addClass(self._classes.fail);\n                    templating.showCancel(id);\n\n                    if (templating.isRetryPossible() && !self._preventRetries[id]) {\n                        qq(fileContainer).addClass(self._classes.retryable);\n                        templating.showRetry(id);\n                    }\n                    self._controlFailureTextDisplay(id, result);\n                }\n            }\n\n            // The parent may need to perform some async operation before we can accurately determine the status of the upload.\n            if (parentRetVal instanceof qq.Promise) {\n                parentRetVal.done(function(newResult) {\n                    completeUpload(newResult);\n                });\n\n            }\n            else {\n                completeUpload(result);\n            }\n\n            return parentRetVal;\n        },\n\n        _markFileAsSuccessful: function(id) {\n            var templating = this._templating;\n\n            if (this._isDeletePossible()) {\n                templating.showDeleteButton(id);\n            }\n\n            qq(templating.getFileContainer(id)).addClass(this._classes.success);\n\n            this._maybeUpdateThumbnail(id);\n        },\n\n        _onUploadPrep: function(id) {\n            this._parent.prototype._onUploadPrep.apply(this, arguments);\n            this._templating.showSpinner(id);\n        },\n\n        _onUpload: function(id, name) {\n            var parentRetVal = this._parent.prototype._onUpload.apply(this, arguments);\n\n            this._templating.showSpinner(id);\n\n            return parentRetVal;\n        },\n\n        _onUploadChunk: function(id, chunkData) {\n            this._parent.prototype._onUploadChunk.apply(this, arguments);\n\n            // Only display the pause button if we have finished uploading at least one chunk\n            // & this file can be resumed\n            if (chunkData.partIndex > 0 && this._handler.isResumable(id)) {\n                this._templating.allowPause(id);\n            }\n        },\n\n        _onCancel: function(id, name) {\n            this._parent.prototype._onCancel.apply(this, arguments);\n            this._removeFileItem(id);\n\n            if (this._getNotFinished() === 0) {\n                this._templating.resetTotalProgress();\n            }\n        },\n\n        _onBeforeAutoRetry: function(id) {\n            var retryNumForDisplay, maxAuto, retryNote;\n\n            this._parent.prototype._onBeforeAutoRetry.apply(this, arguments);\n\n            this._showCancelLink(id);\n\n            if (this._options.retry.showAutoRetryNote) {\n                retryNumForDisplay = this._autoRetries[id];\n                maxAuto = this._options.retry.maxAutoAttempts;\n\n                retryNote = this._options.retry.autoRetryNote.replace(/\\{retryNum\\}/g, retryNumForDisplay);\n                retryNote = retryNote.replace(/\\{maxAuto\\}/g, maxAuto);\n\n                this._templating.setStatusText(id, retryNote);\n                qq(this._templating.getFileContainer(id)).addClass(this._classes.retrying);\n            }\n        },\n\n        //return false if we should not attempt the requested retry\n        _onBeforeManualRetry: function(id) {\n            if (this._parent.prototype._onBeforeManualRetry.apply(this, arguments)) {\n                this._templating.resetProgress(id);\n                qq(this._templating.getFileContainer(id)).removeClass(this._classes.fail);\n                this._templating.setStatusText(id);\n                this._templating.showSpinner(id);\n                this._showCancelLink(id);\n                return true;\n            }\n            else {\n                qq(this._templating.getFileContainer(id)).addClass(this._classes.retryable);\n                this._templating.showRetry(id);\n                return false;\n            }\n        },\n\n        _onSubmitDelete: function(id) {\n            var onSuccessCallback = qq.bind(this._onSubmitDeleteSuccess, this);\n\n            this._parent.prototype._onSubmitDelete.call(this, id, onSuccessCallback);\n        },\n\n        _onSubmitDeleteSuccess: function(id, uuid, additionalMandatedParams) {\n            if (this._options.deleteFile.forceConfirm) {\n                this._showDeleteConfirm.apply(this, arguments);\n            }\n            else {\n                this._sendDeleteRequest.apply(this, arguments);\n            }\n        },\n\n        _onDeleteComplete: function(id, xhr, isError) {\n            this._parent.prototype._onDeleteComplete.apply(this, arguments);\n\n            this._templating.hideSpinner(id);\n\n            if (isError) {\n                this._templating.setStatusText(id, this._options.deleteFile.deletingFailedText);\n                this._templating.showDeleteButton(id);\n            }\n            else {\n                this._removeFileItem(id);\n            }\n        },\n\n        _sendDeleteRequest: function(id, uuid, additionalMandatedParams) {\n            this._templating.hideDeleteButton(id);\n            this._templating.showSpinner(id);\n            this._templating.setStatusText(id, this._options.deleteFile.deletingStatusText);\n            this._deleteHandler.sendDelete.apply(this, arguments);\n        },\n\n        _showDeleteConfirm: function(id, uuid, mandatedParams) {\n            /*jshint -W004 */\n            var fileName = this.getName(id),\n                confirmMessage = this._options.deleteFile.confirmMessage.replace(/\\{filename\\}/g, fileName),\n                uuid = this.getUuid(id),\n                deleteRequestArgs = arguments,\n                self = this,\n                retVal;\n\n            retVal = this._options.showConfirm(confirmMessage);\n\n            if (qq.isGenericPromise(retVal)) {\n                retVal.then(function() {\n                    self._sendDeleteRequest.apply(self, deleteRequestArgs);\n                });\n            }\n            else if (retVal !== false) {\n                self._sendDeleteRequest.apply(self, deleteRequestArgs);\n            }\n        },\n\n        _addToList: function(id, name, canned) {\n            var prependData,\n                prependIndex = 0,\n                dontDisplay = this._handler.isProxied(id) && this._options.scaling.hideScaled,\n                record;\n\n            if (this._options.display.prependFiles) {\n                if (this._totalFilesInBatch > 1 && this._filesInBatchAddedToUi > 0) {\n                    prependIndex = this._filesInBatchAddedToUi - 1;\n                }\n\n                prependData = {\n                    index: prependIndex\n                };\n            }\n\n            if (!canned) {\n                if (this._options.disableCancelForFormUploads && !qq.supportedFeatures.ajaxUploading) {\n                    this._templating.disableCancel();\n                }\n\n                // Cancel all existing (previous) files and clear the list if this file is not part of\n                // a scaled file group that has already been accepted, or if this file is not part of\n                // a scaled file group at all.\n                if (!this._options.multiple) {\n                    record = this.getUploads({id: id});\n\n                    this._handledProxyGroup = this._handledProxyGroup || record.proxyGroupId;\n\n                    if (record.proxyGroupId !== this._handledProxyGroup || !record.proxyGroupId) {\n                        this._handler.cancelAll();\n                        this._clearList();\n                        this._handledProxyGroup = null;\n                    }\n                }\n            }\n\n            if (canned) {\n                this._templating.addFileToCache(id, this._options.formatFileName(name), prependData, dontDisplay);\n                this._templating.updateThumbnail(id, this._thumbnailUrls[id], true, this._options.thumbnails.customResizer);\n            }\n            else {\n                this._templating.addFile(id, this._options.formatFileName(name), prependData, dontDisplay);\n                this._templating.generatePreview(id, this.getFile(id), this._options.thumbnails.customResizer);\n            }\n\n            this._filesInBatchAddedToUi += 1;\n\n            if (canned ||\n                (this._options.display.fileSizeOnSubmit && qq.supportedFeatures.ajaxUploading)) {\n\n                this._displayFileSize(id);\n            }\n        },\n\n        _clearList: function() {\n            this._templating.clearFiles();\n            this.clearStoredFiles();\n        },\n\n        _displayFileSize: function(id, loadedSize, totalSize) {\n            var size = this.getSize(id),\n                sizeForDisplay = this._formatSize(size);\n\n            if (size >= 0) {\n                if (loadedSize !== undefined && totalSize !== undefined) {\n                    sizeForDisplay = this._formatProgress(loadedSize, totalSize);\n                }\n\n                this._templating.updateSize(id, sizeForDisplay);\n            }\n        },\n\n        _formatProgress: function(uploadedSize, totalSize) {\n            var message = this._options.text.formatProgress;\n            function r(name, replacement) { message = message.replace(name, replacement); }\n\n            r(\"{percent}\", Math.round(uploadedSize / totalSize * 100));\n            r(\"{total_size}\", this._formatSize(totalSize));\n            return message;\n        },\n\n        _controlFailureTextDisplay: function(id, response) {\n            var mode, responseProperty, failureReason;\n\n            mode = this._options.failedUploadTextDisplay.mode;\n            responseProperty = this._options.failedUploadTextDisplay.responseProperty;\n\n            if (mode === \"custom\") {\n                failureReason = response[responseProperty];\n                if (!failureReason) {\n                    failureReason = this._options.text.failUpload;\n                }\n\n                this._templating.setStatusText(id, failureReason);\n\n                if (this._options.failedUploadTextDisplay.enableTooltip) {\n                    this._showTooltip(id, failureReason);\n                }\n            }\n            else if (mode === \"default\") {\n                this._templating.setStatusText(id, this._options.text.failUpload);\n            }\n            else if (mode !== \"none\") {\n                this.log(\"failedUploadTextDisplay.mode value of '\" + mode + \"' is not valid\", \"warn\");\n            }\n        },\n\n        _showTooltip: function(id, text) {\n            this._templating.getFileContainer(id).title = text;\n        },\n\n        _showCancelLink: function(id) {\n            if (!this._options.disableCancelForFormUploads || qq.supportedFeatures.ajaxUploading) {\n                this._templating.showCancel(id);\n            }\n        },\n\n        _itemError: function(code, name, item) {\n            var message = this._parent.prototype._itemError.apply(this, arguments);\n            this._options.showMessage(message);\n        },\n\n        _batchError: function(message) {\n            this._parent.prototype._batchError.apply(this, arguments);\n            this._options.showMessage(message);\n        },\n\n        _setupPastePrompt: function() {\n            var self = this;\n\n            this._options.callbacks.onPasteReceived = function() {\n                var message = self._options.paste.namePromptMessage,\n                    defaultVal = self._options.paste.defaultName;\n\n                return self._options.showPrompt(message, defaultVal);\n            };\n        },\n\n        _fileOrBlobRejected: function(id, name) {\n            this._totalFilesInBatch -= 1;\n            this._parent.prototype._fileOrBlobRejected.apply(this, arguments);\n        },\n\n        _prepareItemsForUpload: function(items, params, endpoint) {\n            this._totalFilesInBatch = items.length;\n            this._filesInBatchAddedToUi = 0;\n            this._parent.prototype._prepareItemsForUpload.apply(this, arguments);\n        },\n\n        _maybeUpdateThumbnail: function(fileId) {\n            var thumbnailUrl = this._thumbnailUrls[fileId],\n                fileStatus = this.getUploads({id: fileId}).status;\n\n            if (fileStatus !== qq.status.DELETED &&\n                (thumbnailUrl ||\n                this._options.thumbnails.placeholders.waitUntilResponse ||\n                !qq.supportedFeatures.imagePreviews)) {\n\n                // This will replace the \"waiting\" placeholder with a \"preview not available\" placeholder\n                // if called with a null thumbnailUrl.\n                this._templating.updateThumbnail(fileId, thumbnailUrl, this._options.thumbnails.customResizer);\n            }\n        },\n\n        _addCannedFile: function(sessionData) {\n            var id = this._parent.prototype._addCannedFile.apply(this, arguments);\n\n            this._addToList(id, this.getName(id), true);\n            this._templating.hideSpinner(id);\n            this._templating.hideCancel(id);\n            this._markFileAsSuccessful(id);\n\n            return id;\n        },\n\n        _setSize: function(id, newSize) {\n            this._parent.prototype._setSize.apply(this, arguments);\n\n            this._templating.updateSize(id, this._formatSize(newSize));\n        },\n\n        _sessionRequestComplete: function() {\n            this._templating.addCacheToDom();\n            this._parent.prototype._sessionRequestComplete.apply(this, arguments);\n        }\n    };\n}());\n","/*globals qq */\n/**\n * This defines FineUploader mode, which is a default UI w/ drag & drop uploading.\n */\nqq.FineUploader = function(o, namespace) {\n    \"use strict\";\n\n    var self = this;\n\n    // By default this should inherit instance data from FineUploaderBasic, but this can be overridden\n    // if the (internal) caller defines a different parent.  The parent is also used by\n    // the private and public API functions that need to delegate to a parent function.\n    this._parent = namespace ? qq[namespace].FineUploaderBasic : qq.FineUploaderBasic;\n    this._parent.apply(this, arguments);\n\n    // Options provided by FineUploader mode\n    qq.extend(this._options, {\n        element: null,\n\n        button: null,\n\n        listElement: null,\n\n        dragAndDrop: {\n            extraDropzones: [],\n            reportDirectoryPaths: false\n        },\n\n        text: {\n            formatProgress: \"{percent}% of {total_size}\",\n            failUpload: \"Upload failed\",\n            waitingForResponse: \"Processing...\",\n            paused: \"Paused\"\n        },\n\n        template: \"qq-template\",\n\n        classes: {\n            retrying: \"qq-upload-retrying\",\n            retryable: \"qq-upload-retryable\",\n            success: \"qq-upload-success\",\n            fail: \"qq-upload-fail\",\n            editable: \"qq-editable\",\n            hide: \"qq-hide\",\n            dropActive: \"qq-upload-drop-area-active\"\n        },\n\n        failedUploadTextDisplay: {\n            mode: \"default\", //default, custom, or none\n            responseProperty: \"error\",\n            enableTooltip: true\n        },\n\n        messages: {\n            tooManyFilesError: \"You may only drop one file\",\n            unsupportedBrowser: \"Unrecoverable error - this browser does not permit file uploading of any kind.\"\n        },\n\n        retry: {\n            showAutoRetryNote: true,\n            autoRetryNote: \"Retrying {retryNum}/{maxAuto}...\"\n        },\n\n        deleteFile: {\n            forceConfirm: false,\n            confirmMessage: \"Are you sure you want to delete {filename}?\",\n            deletingStatusText: \"Deleting...\",\n            deletingFailedText: \"Delete failed\"\n\n        },\n\n        display: {\n            fileSizeOnSubmit: false,\n            prependFiles: false\n        },\n\n        paste: {\n            promptForName: false,\n            namePromptMessage: \"Please name this image\"\n        },\n\n        thumbnails: {\n            customResizer: null,\n            maxCount: 0,\n            placeholders: {\n                waitUntilResponse: false,\n                notAvailablePath: null,\n                waitingPath: null\n            },\n            timeBetweenThumbs: 750\n        },\n\n        scaling: {\n            hideScaled: false\n        },\n\n        showMessage: function(message) {\n            if (self._templating.hasDialog(\"alert\")) {\n                return self._templating.showDialog(\"alert\", message);\n            }\n            else {\n                setTimeout(function() {\n                    window.alert(message);\n                }, 0);\n            }\n        },\n\n        showConfirm: function(message) {\n            if (self._templating.hasDialog(\"confirm\")) {\n                return self._templating.showDialog(\"confirm\", message);\n            }\n            else {\n                return window.confirm(message);\n            }\n        },\n\n        showPrompt: function(message, defaultValue) {\n            if (self._templating.hasDialog(\"prompt\")) {\n                return self._templating.showDialog(\"prompt\", message, defaultValue);\n            }\n            else {\n                return window.prompt(message, defaultValue);\n            }\n        }\n    }, true);\n\n    // Replace any default options with user defined ones\n    qq.extend(this._options, o, true);\n\n    this._templating = new qq.Templating({\n        log: qq.bind(this.log, this),\n        templateIdOrEl: this._options.template,\n        containerEl: this._options.element,\n        fileContainerEl: this._options.listElement,\n        button: this._options.button,\n        imageGenerator: this._imageGenerator,\n        classes: {\n            hide: this._options.classes.hide,\n            editable: this._options.classes.editable\n        },\n        limits: {\n            maxThumbs: this._options.thumbnails.maxCount,\n            timeBetweenThumbs: this._options.thumbnails.timeBetweenThumbs\n        },\n        placeholders: {\n            waitUntilUpdate: this._options.thumbnails.placeholders.waitUntilResponse,\n            thumbnailNotAvailable: this._options.thumbnails.placeholders.notAvailablePath,\n            waitingForThumbnail: this._options.thumbnails.placeholders.waitingPath\n        },\n        text: this._options.text\n    });\n\n    if (this._options.workarounds.ios8SafariUploads && qq.ios800() && qq.iosSafari()) {\n        this._templating.renderFailure(this._options.messages.unsupportedBrowserIos8Safari);\n    }\n    else if (!qq.supportedFeatures.uploading || (this._options.cors.expected && !qq.supportedFeatures.uploadCors)) {\n        this._templating.renderFailure(this._options.messages.unsupportedBrowser);\n    }\n    else {\n        this._wrapCallbacks();\n\n        this._templating.render();\n\n        this._classes = this._options.classes;\n\n        if (!this._options.button && this._templating.getButton()) {\n            this._defaultButtonId = this._createUploadButton({\n                element: this._templating.getButton(),\n                title: this._options.text.fileInputTitle\n            }).getButtonId();\n        }\n\n        this._setupClickAndEditEventHandlers();\n\n        if (qq.DragAndDrop && qq.supportedFeatures.fileDrop) {\n            this._dnd = this._setupDragAndDrop();\n        }\n\n        if (this._options.paste.targetElement && this._options.paste.promptForName) {\n            if (qq.PasteSupport) {\n                this._setupPastePrompt();\n            }\n            else {\n                this.log(\"Paste support module not found.\", \"error\");\n            }\n        }\n\n        this._totalFilesInBatch = 0;\n        this._filesInBatchAddedToUi = 0;\n    }\n};\n\n// Inherit the base public & private API methods\nqq.extend(qq.FineUploader.prototype, qq.basePublicApi);\nqq.extend(qq.FineUploader.prototype, qq.basePrivateApi);\n\n// Add the FineUploader/default UI public & private UI methods, which may override some base methods.\nqq.extend(qq.FineUploader.prototype, qq.uiPublicApi);\nqq.extend(qq.FineUploader.prototype, qq.uiPrivateApi);\n","/* globals qq */\n/* jshint -W065 */\n/**\n * Module responsible for rendering all Fine Uploader UI templates.  This module also asserts at least\n * a limited amount of control over the template elements after they are added to the DOM.\n * Wherever possible, this module asserts total control over template elements present in the DOM.\n *\n * @param spec Specification object used to control various templating behaviors\n * @constructor\n */\nqq.Templating = function(spec) {\n    \"use strict\";\n\n    var FILE_ID_ATTR = \"qq-file-id\",\n        FILE_CLASS_PREFIX = \"qq-file-id-\",\n        THUMBNAIL_MAX_SIZE_ATTR = \"qq-max-size\",\n        THUMBNAIL_SERVER_SCALE_ATTR = \"qq-server-scale\",\n        // This variable is duplicated in the DnD module since it can function as a standalone as well\n        HIDE_DROPZONE_ATTR = \"qq-hide-dropzone\",\n        DROPZPONE_TEXT_ATTR = \"qq-drop-area-text\",\n        IN_PROGRESS_CLASS = \"qq-in-progress\",\n        HIDDEN_FOREVER_CLASS = \"qq-hidden-forever\",\n        fileBatch = {\n            content: document.createDocumentFragment(),\n            map: {}\n        },\n        isCancelDisabled = false,\n        generatedThumbnails = 0,\n        thumbnailQueueMonitorRunning = false,\n        thumbGenerationQueue = [],\n        thumbnailMaxSize = -1,\n        options = {\n            log: null,\n            limits: {\n                maxThumbs: 0,\n                timeBetweenThumbs: 750\n            },\n            templateIdOrEl: \"qq-template\",\n            containerEl: null,\n            fileContainerEl: null,\n            button: null,\n            imageGenerator: null,\n            classes: {\n                hide: \"qq-hide\",\n                editable: \"qq-editable\"\n            },\n            placeholders: {\n                waitUntilUpdate: false,\n                thumbnailNotAvailable: null,\n                waitingForThumbnail: null\n            },\n            text: {\n                paused: \"Paused\"\n            }\n        },\n        selectorClasses = {\n            button: \"qq-upload-button-selector\",\n            alertDialog: \"qq-alert-dialog-selector\",\n            dialogCancelButton: \"qq-cancel-button-selector\",\n            confirmDialog: \"qq-confirm-dialog-selector\",\n            dialogMessage: \"qq-dialog-message-selector\",\n            dialogOkButton: \"qq-ok-button-selector\",\n            promptDialog: \"qq-prompt-dialog-selector\",\n            uploader: \"qq-uploader-selector\",\n            drop: \"qq-upload-drop-area-selector\",\n            list: \"qq-upload-list-selector\",\n            progressBarContainer: \"qq-progress-bar-container-selector\",\n            progressBar: \"qq-progress-bar-selector\",\n            totalProgressBarContainer: \"qq-total-progress-bar-container-selector\",\n            totalProgressBar: \"qq-total-progress-bar-selector\",\n            file: \"qq-upload-file-selector\",\n            spinner: \"qq-upload-spinner-selector\",\n            size: \"qq-upload-size-selector\",\n            cancel: \"qq-upload-cancel-selector\",\n            pause: \"qq-upload-pause-selector\",\n            continueButton: \"qq-upload-continue-selector\",\n            deleteButton: \"qq-upload-delete-selector\",\n            retry: \"qq-upload-retry-selector\",\n            statusText: \"qq-upload-status-text-selector\",\n            editFilenameInput: \"qq-edit-filename-selector\",\n            editNameIcon: \"qq-edit-filename-icon-selector\",\n            dropText: \"qq-upload-drop-area-text-selector\",\n            dropProcessing: \"qq-drop-processing-selector\",\n            dropProcessingSpinner: \"qq-drop-processing-spinner-selector\",\n            thumbnail: \"qq-thumbnail-selector\"\n        },\n        previewGeneration = {},\n        cachedThumbnailNotAvailableImg = new qq.Promise(),\n        cachedWaitingForThumbnailImg = new qq.Promise(),\n        log,\n        isEditElementsExist,\n        isRetryElementExist,\n        templateHtml,\n        container,\n        fileList,\n        showThumbnails,\n        serverScale,\n\n        // During initialization of the templating module we should cache any\n        // placeholder images so we can quickly swap them into the file list on demand.\n        // Any placeholder images that cannot be loaded/found are simply ignored.\n        cacheThumbnailPlaceholders = function() {\n            var notAvailableUrl =  options.placeholders.thumbnailNotAvailable,\n                waitingUrl = options.placeholders.waitingForThumbnail,\n                spec = {\n                    maxSize: thumbnailMaxSize,\n                    scale: serverScale\n                };\n\n            if (showThumbnails) {\n                if (notAvailableUrl) {\n                    options.imageGenerator.generate(notAvailableUrl, new Image(), spec).then(\n                        function(updatedImg) {\n                            cachedThumbnailNotAvailableImg.success(updatedImg);\n                        },\n                        function() {\n                            cachedThumbnailNotAvailableImg.failure();\n                            log(\"Problem loading 'not available' placeholder image at \" + notAvailableUrl, \"error\");\n                        }\n                    );\n                }\n                else {\n                    cachedThumbnailNotAvailableImg.failure();\n                }\n\n                if (waitingUrl) {\n                    options.imageGenerator.generate(waitingUrl, new Image(), spec).then(\n                        function(updatedImg) {\n                            cachedWaitingForThumbnailImg.success(updatedImg);\n                        },\n                        function() {\n                            cachedWaitingForThumbnailImg.failure();\n                            log(\"Problem loading 'waiting for thumbnail' placeholder image at \" + waitingUrl, \"error\");\n                        }\n                    );\n                }\n                else {\n                    cachedWaitingForThumbnailImg.failure();\n                }\n            }\n        },\n\n        // Displays a \"waiting for thumbnail\" type placeholder image\n        // iff we were able to load it during initialization of the templating module.\n        displayWaitingImg = function(thumbnail) {\n            var waitingImgPlacement = new qq.Promise();\n\n            cachedWaitingForThumbnailImg.then(function(img) {\n                maybeScalePlaceholderViaCss(img, thumbnail);\n                /* jshint eqnull:true */\n                if (!thumbnail.src) {\n                    thumbnail.src = img.src;\n                    thumbnail.onload = function() {\n                        thumbnail.onload = null;\n                        show(thumbnail);\n                        waitingImgPlacement.success();\n                    };\n                }\n                else {\n                    waitingImgPlacement.success();\n                }\n            }, function() {\n                // In some browsers (such as IE9 and older) an img w/out a src attribute\n                // are displayed as \"broken\" images, so we should just hide the img tag\n                // if we aren't going to display the \"waiting\" placeholder.\n                hide(thumbnail);\n                waitingImgPlacement.success();\n            });\n\n            return waitingImgPlacement;\n        },\n\n        generateNewPreview = function(id, blob, spec) {\n            var thumbnail = getThumbnail(id);\n\n            log(\"Generating new thumbnail for \" + id);\n            blob.qqThumbnailId = id;\n\n            return options.imageGenerator.generate(blob, thumbnail, spec).then(\n                function() {\n                    generatedThumbnails++;\n                    show(thumbnail);\n                    previewGeneration[id].success();\n                },\n                function() {\n                    previewGeneration[id].failure();\n\n                    // Display the \"not available\" placeholder img only if we are\n                    // not expecting a thumbnail at a later point, such as in a server response.\n                    if (!options.placeholders.waitUntilUpdate) {\n                        maybeSetDisplayNotAvailableImg(id, thumbnail);\n                    }\n                });\n        },\n\n        generateNextQueuedPreview = function() {\n            if (thumbGenerationQueue.length) {\n                thumbnailQueueMonitorRunning = true;\n\n                var queuedThumbRequest = thumbGenerationQueue.shift();\n\n                if (queuedThumbRequest.update) {\n                    processUpdateQueuedPreviewRequest(queuedThumbRequest);\n                }\n                else {\n                    processNewQueuedPreviewRequest(queuedThumbRequest);\n                }\n            }\n            else {\n                thumbnailQueueMonitorRunning = false;\n            }\n        },\n\n        getCancel = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.cancel);\n        },\n\n        getContinue = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.continueButton);\n        },\n\n        getDialog = function(type) {\n            return getTemplateEl(container, selectorClasses[type + \"Dialog\"]);\n        },\n\n        getDelete = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.deleteButton);\n        },\n\n        getDropProcessing = function() {\n            return getTemplateEl(container, selectorClasses.dropProcessing);\n        },\n\n        getEditIcon = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.editNameIcon);\n        },\n\n        getFile = function(id) {\n            return fileBatch.map[id] || qq(fileList).getFirstByClass(FILE_CLASS_PREFIX + id);\n        },\n\n        getFilename = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.file);\n        },\n\n        getPause = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.pause);\n        },\n\n        getProgress = function(id) {\n            /* jshint eqnull:true */\n            // Total progress bar\n            if (id == null) {\n                return getTemplateEl(container, selectorClasses.totalProgressBarContainer) ||\n                    getTemplateEl(container, selectorClasses.totalProgressBar);\n            }\n\n            // Per-file progress bar\n            return getTemplateEl(getFile(id), selectorClasses.progressBarContainer) ||\n                getTemplateEl(getFile(id), selectorClasses.progressBar);\n        },\n\n        getRetry = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.retry);\n        },\n\n        getSize = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.size);\n        },\n\n        getSpinner = function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.spinner);\n        },\n\n        getTemplateEl = function(context, cssClass) {\n            return context && qq(context).getFirstByClass(cssClass);\n        },\n\n        getThumbnail = function(id) {\n            return showThumbnails && getTemplateEl(getFile(id), selectorClasses.thumbnail);\n        },\n\n        hide = function(el) {\n            el && qq(el).addClass(options.classes.hide);\n        },\n\n        // Ensures a placeholder image does not exceed any max size specified\n        // via `style` attribute properties iff <canvas> was not used to scale\n        // the placeholder AND the target <img> doesn't already have these `style` attribute properties set.\n        maybeScalePlaceholderViaCss = function(placeholder, thumbnail) {\n            var maxWidth = placeholder.style.maxWidth,\n                maxHeight = placeholder.style.maxHeight;\n\n            if (maxHeight && maxWidth && !thumbnail.style.maxWidth && !thumbnail.style.maxHeight) {\n                qq(thumbnail).css({\n                    maxWidth: maxWidth,\n                    maxHeight: maxHeight\n                });\n            }\n        },\n\n        // Displays a \"thumbnail not available\" type placeholder image\n        // iff we were able to load this placeholder during initialization\n        // of the templating module or after preview generation has failed.\n        maybeSetDisplayNotAvailableImg = function(id, thumbnail) {\n            var previewing = previewGeneration[id] || new qq.Promise().failure(),\n                notAvailableImgPlacement = new qq.Promise();\n\n            cachedThumbnailNotAvailableImg.then(function(img) {\n                previewing.then(\n                    function() {\n                        notAvailableImgPlacement.success();\n                    },\n                    function() {\n                        maybeScalePlaceholderViaCss(img, thumbnail);\n\n                        thumbnail.onload = function() {\n                            thumbnail.onload = null;\n                            notAvailableImgPlacement.success();\n                        };\n\n                        thumbnail.src = img.src;\n                        show(thumbnail);\n                    }\n                );\n            });\n\n            return notAvailableImgPlacement;\n        },\n\n        /**\n         * Grabs the HTML from the script tag holding the template markup.  This function will also adjust\n         * some internally-tracked state variables based on the contents of the template.\n         * The template is filtered so that irrelevant elements (such as the drop zone if DnD is not supported)\n         * are omitted from the DOM.  Useful errors will be thrown if the template cannot be parsed.\n         *\n         * @returns {{template: *, fileTemplate: *}} HTML for the top-level file items templates\n         */\n        parseAndGetTemplate = function() {\n            var scriptEl,\n                scriptHtml,\n                fileListNode,\n                tempTemplateEl,\n                fileListHtml,\n                defaultButton,\n                dropArea,\n                thumbnail,\n                dropProcessing,\n                dropTextEl,\n                uploaderEl;\n\n            log(\"Parsing template\");\n\n            /*jshint -W116*/\n            if (options.templateIdOrEl == null) {\n                throw new Error(\"You MUST specify either a template element or ID!\");\n            }\n\n            // Grab the contents of the script tag holding the template.\n            if (qq.isString(options.templateIdOrEl)) {\n                scriptEl = document.getElementById(options.templateIdOrEl);\n\n                if (scriptEl === null) {\n                    throw new Error(qq.format(\"Cannot find template script at ID '{}'!\", options.templateIdOrEl));\n                }\n\n                scriptHtml = scriptEl.innerHTML;\n            }\n            else {\n                if (options.templateIdOrEl.innerHTML === undefined) {\n                    throw new Error(\"You have specified an invalid value for the template option!  \" +\n                        \"It must be an ID or an Element.\");\n                }\n\n                scriptHtml = options.templateIdOrEl.innerHTML;\n            }\n\n            scriptHtml = qq.trimStr(scriptHtml);\n            tempTemplateEl = document.createElement(\"div\");\n            tempTemplateEl.appendChild(qq.toElement(scriptHtml));\n            uploaderEl = qq(tempTemplateEl).getFirstByClass(selectorClasses.uploader);\n\n            // Don't include the default template button in the DOM\n            // if an alternate button container has been specified.\n            if (options.button) {\n                defaultButton = qq(tempTemplateEl).getFirstByClass(selectorClasses.button);\n                if (defaultButton) {\n                    qq(defaultButton).remove();\n                }\n            }\n\n            // Omit the drop processing element from the DOM if DnD is not supported by the UA,\n            // or the drag and drop module is not found.\n            // NOTE: We are consciously not removing the drop zone if the UA doesn't support DnD\n            // to support layouts where the drop zone is also a container for visible elements,\n            // such as the file list.\n            if (!qq.DragAndDrop || !qq.supportedFeatures.fileDrop) {\n                dropProcessing = qq(tempTemplateEl).getFirstByClass(selectorClasses.dropProcessing);\n                if (dropProcessing) {\n                    qq(dropProcessing).remove();\n                }\n            }\n\n            dropArea = qq(tempTemplateEl).getFirstByClass(selectorClasses.drop);\n\n            // If DnD is not available then remove\n            // it from the DOM as well.\n            if (dropArea && !qq.DragAndDrop) {\n                log(\"DnD module unavailable.\", \"info\");\n                qq(dropArea).remove();\n            }\n\n            if (!qq.supportedFeatures.fileDrop) {\n                // don't display any \"drop files to upload\" background text\n                uploaderEl.removeAttribute(DROPZPONE_TEXT_ATTR);\n\n                if (dropArea && qq(dropArea).hasAttribute(HIDE_DROPZONE_ATTR)) {\n                    // If there is a drop area defined in the template, and the current UA doesn't support DnD,\n                    // and the drop area is marked as \"hide before enter\", ensure it is hidden as the DnD module\n                    // will not do this (since we will not be loading the DnD module)\n                    qq(dropArea).css({\n                        display: \"none\"\n                    });\n                }\n            }\n            else if (qq(uploaderEl).hasAttribute(DROPZPONE_TEXT_ATTR) && dropArea) {\n                dropTextEl = qq(dropArea).getFirstByClass(selectorClasses.dropText);\n                dropTextEl && qq(dropTextEl).remove();\n            }\n\n            // Ensure the `showThumbnails` flag is only set if the thumbnail element\n            // is present in the template AND the current UA is capable of generating client-side previews.\n            thumbnail = qq(tempTemplateEl).getFirstByClass(selectorClasses.thumbnail);\n            if (!showThumbnails) {\n                thumbnail && qq(thumbnail).remove();\n            }\n            else if (thumbnail) {\n                thumbnailMaxSize = parseInt(thumbnail.getAttribute(THUMBNAIL_MAX_SIZE_ATTR));\n                // Only enforce max size if the attr value is non-zero\n                thumbnailMaxSize = thumbnailMaxSize > 0 ? thumbnailMaxSize : null;\n\n                serverScale = qq(thumbnail).hasAttribute(THUMBNAIL_SERVER_SCALE_ATTR);\n            }\n            showThumbnails = showThumbnails && thumbnail;\n\n            isEditElementsExist = qq(tempTemplateEl).getByClass(selectorClasses.editFilenameInput).length > 0;\n            isRetryElementExist = qq(tempTemplateEl).getByClass(selectorClasses.retry).length > 0;\n\n            fileListNode = qq(tempTemplateEl).getFirstByClass(selectorClasses.list);\n            /*jshint -W116*/\n            if (fileListNode == null) {\n                throw new Error(\"Could not find the file list container in the template!\");\n            }\n\n            fileListHtml = fileListNode.innerHTML;\n            fileListNode.innerHTML = \"\";\n\n            // We must call `createElement` in IE8 in order to target and hide any <dialog> via CSS\n            if (tempTemplateEl.getElementsByTagName(\"DIALOG\").length) {\n                document.createElement(\"dialog\");\n            }\n\n            log(\"Template parsing complete\");\n\n            return {\n                template: qq.trimStr(tempTemplateEl.innerHTML),\n                fileTemplate: qq.trimStr(fileListHtml)\n            };\n        },\n\n        prependFile = function(el, index, fileList) {\n            var parentEl = fileList,\n                beforeEl = parentEl.firstChild;\n\n            if (index > 0) {\n                beforeEl = qq(parentEl).children()[index].nextSibling;\n\n            }\n\n            parentEl.insertBefore(el, beforeEl);\n        },\n\n        processNewQueuedPreviewRequest = function(queuedThumbRequest) {\n            var id = queuedThumbRequest.id,\n                optFileOrBlob = queuedThumbRequest.optFileOrBlob,\n                relatedThumbnailId = optFileOrBlob && optFileOrBlob.qqThumbnailId,\n                thumbnail = getThumbnail(id),\n                spec = {\n                    customResizeFunction: queuedThumbRequest.customResizeFunction,\n                    maxSize: thumbnailMaxSize,\n                    orient: true,\n                    scale: true\n                };\n\n            if (qq.supportedFeatures.imagePreviews) {\n                if (thumbnail) {\n                    if (options.limits.maxThumbs && options.limits.maxThumbs <= generatedThumbnails) {\n                        maybeSetDisplayNotAvailableImg(id, thumbnail);\n                        generateNextQueuedPreview();\n                    }\n                    else {\n                        displayWaitingImg(thumbnail).done(function() {\n                            previewGeneration[id] = new qq.Promise();\n\n                            previewGeneration[id].done(function() {\n                                setTimeout(generateNextQueuedPreview, options.limits.timeBetweenThumbs);\n                            });\n\n                            /* jshint eqnull: true */\n                            // If we've already generated an <img> for this file, use the one that exists,\n                            // don't waste resources generating a new one.\n                            if (relatedThumbnailId != null) {\n                                useCachedPreview(id, relatedThumbnailId);\n                            }\n                            else {\n                                generateNewPreview(id, optFileOrBlob, spec);\n                            }\n                        });\n                    }\n                }\n                // File element in template may have been removed, so move on to next item in queue\n                else {\n                    generateNextQueuedPreview();\n                }\n            }\n            else if (thumbnail) {\n                displayWaitingImg(thumbnail);\n                generateNextQueuedPreview();\n            }\n        },\n\n        processUpdateQueuedPreviewRequest = function(queuedThumbRequest) {\n            var id = queuedThumbRequest.id,\n                thumbnailUrl = queuedThumbRequest.thumbnailUrl,\n                showWaitingImg = queuedThumbRequest.showWaitingImg,\n                thumbnail = getThumbnail(id),\n                spec = {\n                    customResizeFunction: queuedThumbRequest.customResizeFunction,\n                    scale: serverScale,\n                    maxSize: thumbnailMaxSize\n                };\n\n            if (thumbnail) {\n                if (thumbnailUrl) {\n                    if (options.limits.maxThumbs && options.limits.maxThumbs <= generatedThumbnails) {\n                        maybeSetDisplayNotAvailableImg(id, thumbnail);\n                        generateNextQueuedPreview();\n                    }\n                    else {\n                        if (showWaitingImg) {\n                            displayWaitingImg(thumbnail);\n                        }\n\n                        return options.imageGenerator.generate(thumbnailUrl, thumbnail, spec).then(\n                            function() {\n                                show(thumbnail);\n                                generatedThumbnails++;\n                                setTimeout(generateNextQueuedPreview, options.limits.timeBetweenThumbs);\n                            },\n\n                            function() {\n                                maybeSetDisplayNotAvailableImg(id, thumbnail);\n                                setTimeout(generateNextQueuedPreview, options.limits.timeBetweenThumbs);\n                            }\n                        );\n                    }\n                }\n                else {\n                    maybeSetDisplayNotAvailableImg(id, thumbnail);\n                    generateNextQueuedPreview();\n                }\n            }\n        },\n\n        setProgressBarWidth = function(id, percent) {\n            var bar = getProgress(id),\n                /* jshint eqnull:true */\n                progressBarSelector = id == null ? selectorClasses.totalProgressBar : selectorClasses.progressBar;\n\n            if (bar && !qq(bar).hasClass(progressBarSelector)) {\n                bar = qq(bar).getFirstByClass(progressBarSelector);\n            }\n\n            if (bar) {\n                qq(bar).css({width: percent + \"%\"});\n                bar.setAttribute(\"aria-valuenow\", percent);\n            }\n        },\n\n        show = function(el) {\n            el && qq(el).removeClass(options.classes.hide);\n        },\n\n        useCachedPreview = function(targetThumbnailId, cachedThumbnailId) {\n            var targetThumbnail = getThumbnail(targetThumbnailId),\n                cachedThumbnail = getThumbnail(cachedThumbnailId);\n\n            log(qq.format(\"ID {} is the same file as ID {}.  Will use generated thumbnail from ID {} instead.\", targetThumbnailId, cachedThumbnailId, cachedThumbnailId));\n\n            // Generation of the related thumbnail may still be in progress, so, wait until it is done.\n            previewGeneration[cachedThumbnailId].then(function() {\n                generatedThumbnails++;\n                previewGeneration[targetThumbnailId].success();\n                log(qq.format(\"Now using previously generated thumbnail created for ID {} on ID {}.\", cachedThumbnailId, targetThumbnailId));\n                targetThumbnail.src = cachedThumbnail.src;\n                show(targetThumbnail);\n            },\n            function() {\n                previewGeneration[targetThumbnailId].failure();\n                if (!options.placeholders.waitUntilUpdate) {\n                    maybeSetDisplayNotAvailableImg(targetThumbnailId, targetThumbnail);\n                }\n            });\n        };\n\n    qq.extend(options, spec);\n    log = options.log;\n\n    // No need to worry about conserving CPU or memory on older browsers,\n    // since there is no ability to preview, and thumbnail display is primitive and quick.\n    if (!qq.supportedFeatures.imagePreviews) {\n        options.limits.timeBetweenThumbs = 0;\n        options.limits.maxThumbs = 0;\n    }\n\n    container = options.containerEl;\n    showThumbnails = options.imageGenerator !== undefined;\n    templateHtml = parseAndGetTemplate();\n\n    cacheThumbnailPlaceholders();\n\n    qq.extend(this, {\n        render: function() {\n            log(\"Rendering template in DOM.\");\n\n            generatedThumbnails = 0;\n\n            container.innerHTML = templateHtml.template;\n            hide(getDropProcessing());\n            this.hideTotalProgress();\n            fileList = options.fileContainerEl || getTemplateEl(container, selectorClasses.list);\n\n            log(\"Template rendering complete\");\n        },\n\n        renderFailure: function(message) {\n            var cantRenderEl = qq.toElement(message);\n            container.innerHTML = \"\";\n            container.appendChild(cantRenderEl);\n        },\n\n        reset: function() {\n            this.render();\n        },\n\n        clearFiles: function() {\n            fileList.innerHTML = \"\";\n        },\n\n        disableCancel: function() {\n            isCancelDisabled = true;\n        },\n\n        addFile: function(id, name, prependInfo, hideForever, batch) {\n            var fileEl = qq.toElement(templateHtml.fileTemplate),\n                fileNameEl = getTemplateEl(fileEl, selectorClasses.file),\n                uploaderEl = getTemplateEl(container, selectorClasses.uploader),\n                fileContainer = batch ? fileBatch.content : fileList,\n                thumb;\n\n            if (batch) {\n                fileBatch.map[id] = fileEl;\n            }\n\n            qq(fileEl).addClass(FILE_CLASS_PREFIX + id);\n            uploaderEl.removeAttribute(DROPZPONE_TEXT_ATTR);\n\n            if (fileNameEl) {\n                qq(fileNameEl).setText(name);\n                fileNameEl.setAttribute(\"title\", name);\n            }\n\n            fileEl.setAttribute(FILE_ID_ATTR, id);\n\n            if (prependInfo) {\n                prependFile(fileEl, prependInfo.index, fileContainer);\n            }\n            else {\n                fileContainer.appendChild(fileEl);\n            }\n\n            if (hideForever) {\n                fileEl.style.display = \"none\";\n                qq(fileEl).addClass(HIDDEN_FOREVER_CLASS);\n            }\n            else {\n                hide(getProgress(id));\n                hide(getSize(id));\n                hide(getDelete(id));\n                hide(getRetry(id));\n                hide(getPause(id));\n                hide(getContinue(id));\n\n                if (isCancelDisabled) {\n                    this.hideCancel(id);\n                }\n\n                thumb = getThumbnail(id);\n                if (thumb && !thumb.src) {\n                    cachedWaitingForThumbnailImg.then(function(waitingImg) {\n                        thumb.src = waitingImg.src;\n                        if (waitingImg.style.maxHeight && waitingImg.style.maxWidth) {\n                            qq(thumb).css({\n                                maxHeight: waitingImg.style.maxHeight,\n                                maxWidth: waitingImg.style.maxWidth\n                            });\n                        }\n\n                        show(thumb);\n                    });\n                }\n            }\n        },\n\n        addFileToCache: function(id, name, prependInfo, hideForever) {\n            this.addFile(id, name, prependInfo, hideForever, true);\n        },\n\n        addCacheToDom: function() {\n            fileList.appendChild(fileBatch.content);\n            fileBatch.content = document.createDocumentFragment();\n            fileBatch.map = {};\n        },\n\n        removeFile: function(id) {\n            qq(getFile(id)).remove();\n        },\n\n        getFileId: function(el) {\n            var currentNode = el;\n\n            if (currentNode) {\n                /*jshint -W116*/\n                while (currentNode.getAttribute(FILE_ID_ATTR) == null) {\n                    currentNode = currentNode.parentNode;\n                }\n\n                return parseInt(currentNode.getAttribute(FILE_ID_ATTR));\n            }\n        },\n\n        getFileList: function() {\n            return fileList;\n        },\n\n        markFilenameEditable: function(id) {\n            var filename = getFilename(id);\n\n            filename && qq(filename).addClass(options.classes.editable);\n        },\n\n        updateFilename: function(id, name) {\n            var filenameEl = getFilename(id);\n\n            if (filenameEl) {\n                qq(filenameEl).setText(name);\n                filenameEl.setAttribute(\"title\", name);\n            }\n        },\n\n        hideFilename: function(id) {\n            hide(getFilename(id));\n        },\n\n        showFilename: function(id) {\n            show(getFilename(id));\n        },\n\n        isFileName: function(el) {\n            return qq(el).hasClass(selectorClasses.file);\n        },\n\n        getButton: function() {\n            return options.button || getTemplateEl(container, selectorClasses.button);\n        },\n\n        hideDropProcessing: function() {\n            hide(getDropProcessing());\n        },\n\n        showDropProcessing: function() {\n            show(getDropProcessing());\n        },\n\n        getDropZone: function() {\n            return getTemplateEl(container, selectorClasses.drop);\n        },\n\n        isEditFilenamePossible: function() {\n            return isEditElementsExist;\n        },\n\n        hideRetry: function(id) {\n            hide(getRetry(id));\n        },\n\n        isRetryPossible: function() {\n            return isRetryElementExist;\n        },\n\n        showRetry: function(id) {\n            show(getRetry(id));\n        },\n\n        getFileContainer: function(id) {\n            return getFile(id);\n        },\n\n        showEditIcon: function(id) {\n            var icon = getEditIcon(id);\n\n            icon && qq(icon).addClass(options.classes.editable);\n        },\n\n        isHiddenForever: function(id) {\n            return qq(getFile(id)).hasClass(HIDDEN_FOREVER_CLASS);\n        },\n\n        hideEditIcon: function(id) {\n            var icon = getEditIcon(id);\n\n            icon && qq(icon).removeClass(options.classes.editable);\n        },\n\n        isEditIcon: function(el) {\n            return qq(el).hasClass(selectorClasses.editNameIcon, true);\n        },\n\n        getEditInput: function(id) {\n            return getTemplateEl(getFile(id), selectorClasses.editFilenameInput);\n        },\n\n        isEditInput: function(el) {\n            return qq(el).hasClass(selectorClasses.editFilenameInput, true);\n        },\n\n        updateProgress: function(id, loaded, total) {\n            var bar = getProgress(id),\n                percent;\n\n            if (bar && total > 0) {\n                percent = Math.round(loaded / total * 100);\n\n                if (percent === 100) {\n                    hide(bar);\n                }\n                else {\n                    show(bar);\n                }\n\n                setProgressBarWidth(id, percent);\n            }\n        },\n\n        updateTotalProgress: function(loaded, total) {\n            this.updateProgress(null, loaded, total);\n        },\n\n        hideProgress: function(id) {\n            var bar = getProgress(id);\n\n            bar && hide(bar);\n        },\n\n        hideTotalProgress: function() {\n            this.hideProgress();\n        },\n\n        resetProgress: function(id) {\n            setProgressBarWidth(id, 0);\n            this.hideTotalProgress(id);\n        },\n\n        resetTotalProgress: function() {\n            this.resetProgress();\n        },\n\n        showCancel: function(id) {\n            if (!isCancelDisabled) {\n                var cancel = getCancel(id);\n\n                cancel && qq(cancel).removeClass(options.classes.hide);\n            }\n        },\n\n        hideCancel: function(id) {\n            hide(getCancel(id));\n        },\n\n        isCancel: function(el)  {\n            return qq(el).hasClass(selectorClasses.cancel, true);\n        },\n\n        allowPause: function(id) {\n            show(getPause(id));\n            hide(getContinue(id));\n        },\n\n        uploadPaused: function(id) {\n            this.setStatusText(id, options.text.paused);\n            this.allowContinueButton(id);\n            hide(getSpinner(id));\n        },\n\n        hidePause: function(id) {\n            hide(getPause(id));\n        },\n\n        isPause: function(el) {\n            return qq(el).hasClass(selectorClasses.pause, true);\n        },\n\n        isContinueButton: function(el) {\n            return qq(el).hasClass(selectorClasses.continueButton, true);\n        },\n\n        allowContinueButton: function(id) {\n            show(getContinue(id));\n            hide(getPause(id));\n        },\n\n        uploadContinued: function(id) {\n            this.setStatusText(id, \"\");\n            this.allowPause(id);\n            show(getSpinner(id));\n        },\n\n        showDeleteButton: function(id) {\n            show(getDelete(id));\n        },\n\n        hideDeleteButton: function(id) {\n            hide(getDelete(id));\n        },\n\n        isDeleteButton: function(el) {\n            return qq(el).hasClass(selectorClasses.deleteButton, true);\n        },\n\n        isRetry: function(el) {\n            return qq(el).hasClass(selectorClasses.retry, true);\n        },\n\n        updateSize: function(id, text) {\n            var size = getSize(id);\n\n            if (size) {\n                show(size);\n                qq(size).setText(text);\n            }\n        },\n\n        setStatusText: function(id, text) {\n            var textEl = getTemplateEl(getFile(id), selectorClasses.statusText);\n\n            if (textEl) {\n                /*jshint -W116*/\n                if (text == null) {\n                    qq(textEl).clearText();\n                }\n                else {\n                    qq(textEl).setText(text);\n                }\n            }\n        },\n\n        hideSpinner: function(id) {\n            qq(getFile(id)).removeClass(IN_PROGRESS_CLASS);\n            hide(getSpinner(id));\n        },\n\n        showSpinner: function(id) {\n            qq(getFile(id)).addClass(IN_PROGRESS_CLASS);\n            show(getSpinner(id));\n        },\n\n        generatePreview: function(id, optFileOrBlob, customResizeFunction) {\n            if (!this.isHiddenForever(id)) {\n                thumbGenerationQueue.push({id: id, customResizeFunction: customResizeFunction, optFileOrBlob: optFileOrBlob});\n                !thumbnailQueueMonitorRunning && generateNextQueuedPreview();\n            }\n        },\n\n        updateThumbnail: function(id, thumbnailUrl, showWaitingImg, customResizeFunction) {\n            if (!this.isHiddenForever(id)) {\n                thumbGenerationQueue.push({customResizeFunction: customResizeFunction, update: true, id: id, thumbnailUrl: thumbnailUrl, showWaitingImg: showWaitingImg});\n                !thumbnailQueueMonitorRunning && generateNextQueuedPreview();\n            }\n        },\n\n        hasDialog: function(type) {\n            return qq.supportedFeatures.dialogElement && !!getDialog(type);\n        },\n\n        showDialog: function(type, message, defaultValue) {\n            var dialog = getDialog(type),\n                messageEl = getTemplateEl(dialog, selectorClasses.dialogMessage),\n                inputEl = dialog.getElementsByTagName(\"INPUT\")[0],\n                cancelBtn = getTemplateEl(dialog, selectorClasses.dialogCancelButton),\n                okBtn = getTemplateEl(dialog, selectorClasses.dialogOkButton),\n                promise = new qq.Promise(),\n\n                closeHandler = function() {\n                    cancelBtn.removeEventListener(\"click\", cancelClickHandler);\n                    okBtn && okBtn.removeEventListener(\"click\", okClickHandler);\n                    promise.failure();\n                },\n\n                cancelClickHandler = function() {\n                    cancelBtn.removeEventListener(\"click\", cancelClickHandler);\n                    dialog.close();\n                },\n\n                okClickHandler = function() {\n                    dialog.removeEventListener(\"close\", closeHandler);\n                    okBtn.removeEventListener(\"click\", okClickHandler);\n                    dialog.close();\n\n                    promise.success(inputEl && inputEl.value);\n                };\n\n            dialog.addEventListener(\"close\", closeHandler);\n            cancelBtn.addEventListener(\"click\", cancelClickHandler);\n            okBtn && okBtn.addEventListener(\"click\", okClickHandler);\n\n            if (inputEl) {\n                inputEl.value = defaultValue;\n            }\n            messageEl.textContent = message;\n\n            dialog.showModal();\n\n            return promise;\n        }\n    });\n};\n","/*globals qq */\n// Base handler for UI (FineUploader mode) events.\n// Some more specific handlers inherit from this one.\nqq.UiEventHandler = function(s, protectedApi) {\n    \"use strict\";\n\n    var disposer = new qq.DisposeSupport(),\n        spec = {\n            eventType: \"click\",\n            attachTo: null,\n            onHandled: function(target, event) {}\n        };\n\n    // This makes up the \"public\" API methods that will be accessible\n    // to instances constructing a base or child handler\n    qq.extend(this, {\n        addHandler: function(element) {\n            addHandler(element);\n        },\n\n        dispose: function() {\n            disposer.dispose();\n        }\n    });\n\n    function addHandler(element) {\n        disposer.attach(element, spec.eventType, function(event) {\n            // Only in IE: the `event` is a property of the `window`.\n            event = event || window.event;\n\n            // On older browsers, we must check the `srcElement` instead of the `target`.\n            var target = event.target || event.srcElement;\n\n            spec.onHandled(target, event);\n        });\n    }\n\n    // These make up the \"protected\" API methods that children of this base handler will utilize.\n    qq.extend(protectedApi, {\n        getFileIdFromItem: function(item) {\n            return item.qqFileId;\n        },\n\n        getDisposeSupport: function() {\n            return disposer;\n        }\n    });\n\n    qq.extend(spec, s);\n\n    if (spec.attachTo) {\n        addHandler(spec.attachTo);\n    }\n};\n","/* global qq */\nqq.FileButtonsClickHandler = function(s) {\n    \"use strict\";\n\n    var inheritedInternalApi = {},\n        spec = {\n            templating: null,\n            log: function(message, lvl) {},\n            onDeleteFile: function(fileId) {},\n            onCancel: function(fileId) {},\n            onRetry: function(fileId) {},\n            onPause: function(fileId) {},\n            onContinue: function(fileId) {},\n            onGetName: function(fileId) {}\n        },\n        buttonHandlers = {\n            cancel: function(id) { spec.onCancel(id); },\n            retry:  function(id) { spec.onRetry(id); },\n            deleteButton: function(id) { spec.onDeleteFile(id); },\n            pause: function(id) { spec.onPause(id); },\n            continueButton: function(id) { spec.onContinue(id); }\n        };\n\n    function examineEvent(target, event) {\n        qq.each(buttonHandlers, function(buttonType, handler) {\n            var firstLetterCapButtonType = buttonType.charAt(0).toUpperCase() + buttonType.slice(1),\n                fileId;\n\n            if (spec.templating[\"is\" + firstLetterCapButtonType](target)) {\n                fileId = spec.templating.getFileId(target);\n                qq.preventDefault(event);\n                spec.log(qq.format(\"Detected valid file button click event on file '{}', ID: {}.\", spec.onGetName(fileId), fileId));\n                handler(fileId);\n                return false;\n            }\n        });\n    }\n\n    qq.extend(spec, s);\n\n    spec.eventType = \"click\";\n    spec.onHandled = examineEvent;\n    spec.attachTo = spec.templating.getFileList();\n\n    qq.extend(this, new qq.UiEventHandler(spec, inheritedInternalApi));\n};\n","/*globals qq */\n// Child of FilenameEditHandler.  Used to detect click events on filename display elements.\nqq.FilenameClickHandler = function(s) {\n    \"use strict\";\n\n    var inheritedInternalApi = {},\n        spec = {\n            templating: null,\n            log: function(message, lvl) {},\n            classes: {\n                file: \"qq-upload-file\",\n                editNameIcon: \"qq-edit-filename-icon\"\n            },\n            onGetUploadStatus: function(fileId) {},\n            onGetName: function(fileId) {}\n        };\n\n    qq.extend(spec, s);\n\n    // This will be called by the parent handler when a `click` event is received on the list element.\n    function examineEvent(target, event) {\n        if (spec.templating.isFileName(target) || spec.templating.isEditIcon(target)) {\n            var fileId = spec.templating.getFileId(target),\n                status = spec.onGetUploadStatus(fileId);\n\n            // We only allow users to change filenames of files that have been submitted but not yet uploaded.\n            if (status === qq.status.SUBMITTED) {\n                spec.log(qq.format(\"Detected valid filename click event on file '{}', ID: {}.\", spec.onGetName(fileId), fileId));\n                qq.preventDefault(event);\n\n                inheritedInternalApi.handleFilenameEdit(fileId, target, true);\n            }\n        }\n    }\n\n    spec.eventType = \"click\";\n    spec.onHandled = examineEvent;\n\n    qq.extend(this, new qq.FilenameEditHandler(spec, inheritedInternalApi));\n};\n","/*globals qq */\n// Child of FilenameEditHandler.  Used to detect focusin events on file edit input elements.\nqq.FilenameInputFocusInHandler = function(s, inheritedInternalApi) {\n    \"use strict\";\n\n    var spec = {\n            templating: null,\n            onGetUploadStatus: function(fileId) {},\n            log: function(message, lvl) {}\n        };\n\n    if (!inheritedInternalApi) {\n        inheritedInternalApi = {};\n    }\n\n    // This will be called by the parent handler when a `focusin` event is received on the list element.\n    function handleInputFocus(target, event) {\n        if (spec.templating.isEditInput(target)) {\n            var fileId = spec.templating.getFileId(target),\n                status = spec.onGetUploadStatus(fileId);\n\n            if (status === qq.status.SUBMITTED) {\n                spec.log(qq.format(\"Detected valid filename input focus event on file '{}', ID: {}.\", spec.onGetName(fileId), fileId));\n                inheritedInternalApi.handleFilenameEdit(fileId, target);\n            }\n        }\n    }\n\n    spec.eventType = \"focusin\";\n    spec.onHandled = handleInputFocus;\n\n    qq.extend(spec, s);\n    qq.extend(this, new qq.FilenameEditHandler(spec, inheritedInternalApi));\n};\n","/*globals qq */\n/**\n * Child of FilenameInputFocusInHandler.  Used to detect focus events on file edit input elements.  This child module is only\n * needed for UAs that do not support the focusin event.  Currently, only Firefox lacks this event.\n *\n * @param spec Overrides for default specifications\n */\nqq.FilenameInputFocusHandler = function(spec) {\n    \"use strict\";\n\n    spec.eventType = \"focus\";\n    spec.attachTo = null;\n\n    qq.extend(this, new qq.FilenameInputFocusInHandler(spec, {}));\n};\n","/*globals qq */\n// Handles edit-related events on a file item (FineUploader mode).  This is meant to be a parent handler.\n// Children will delegate to this handler when specific edit-related actions are detected.\nqq.FilenameEditHandler = function(s, inheritedInternalApi) {\n    \"use strict\";\n\n    var spec = {\n            templating: null,\n            log: function(message, lvl) {},\n            onGetUploadStatus: function(fileId) {},\n            onGetName: function(fileId) {},\n            onSetName: function(fileId, newName) {},\n            onEditingStatusChange: function(fileId, isEditing) {}\n        };\n\n    function getFilenameSansExtension(fileId) {\n        var filenameSansExt = spec.onGetName(fileId),\n            extIdx = filenameSansExt.lastIndexOf(\".\");\n\n        if (extIdx > 0) {\n            filenameSansExt = filenameSansExt.substr(0, extIdx);\n        }\n\n        return filenameSansExt;\n    }\n\n    function getOriginalExtension(fileId) {\n        var origName = spec.onGetName(fileId);\n        return qq.getExtension(origName);\n    }\n\n    // Callback iff the name has been changed\n    function handleNameUpdate(newFilenameInputEl, fileId) {\n        var newName = newFilenameInputEl.value,\n            origExtension;\n\n        if (newName !== undefined && qq.trimStr(newName).length > 0) {\n            origExtension = getOriginalExtension(fileId);\n\n            if (origExtension !== undefined) {\n                newName = newName + \".\" + origExtension;\n            }\n\n            spec.onSetName(fileId, newName);\n        }\n\n        spec.onEditingStatusChange(fileId, false);\n    }\n\n    // The name has been updated if the filename edit input loses focus.\n    function registerInputBlurHandler(inputEl, fileId) {\n        inheritedInternalApi.getDisposeSupport().attach(inputEl, \"blur\", function() {\n            handleNameUpdate(inputEl, fileId);\n        });\n    }\n\n    // The name has been updated if the user presses enter.\n    function registerInputEnterKeyHandler(inputEl, fileId) {\n        inheritedInternalApi.getDisposeSupport().attach(inputEl, \"keyup\", function(event) {\n\n            var code = event.keyCode || event.which;\n\n            if (code === 13) {\n                handleNameUpdate(inputEl, fileId);\n            }\n        });\n    }\n\n    qq.extend(spec, s);\n\n    spec.attachTo = spec.templating.getFileList();\n\n    qq.extend(this, new qq.UiEventHandler(spec, inheritedInternalApi));\n\n    qq.extend(inheritedInternalApi, {\n        handleFilenameEdit: function(id, target, focusInput) {\n            var newFilenameInputEl = spec.templating.getEditInput(id);\n\n            spec.onEditingStatusChange(id, true);\n\n            newFilenameInputEl.value = getFilenameSansExtension(id);\n\n            if (focusInput) {\n                newFilenameInputEl.focus();\n            }\n\n            registerInputBlurHandler(newFilenameInputEl, id);\n            registerInputEnterKeyHandler(newFilenameInputEl, id);\n        }\n    });\n};\n","/*globals qq */\n/**\n * This defines FineUploader mode w/ support for uploading to S3, which provides all the basic\n * functionality of Fine Uploader as well as code to handle uploads directly to S3.\n * This module inherits all logic from FineUploader mode and FineUploaderBasicS3 mode and adds some UI-related logic\n * specific to the upload-to-S3 workflow.  Some inherited options and API methods have a special meaning\n * in the context of the S3 uploader.\n */\n(function() {\n    \"use strict\";\n\n    qq.s3.FineUploader = function(o) {\n        var options = {\n            failedUploadTextDisplay: {\n                mode: \"custom\"\n            }\n        };\n\n        // Replace any default options with user defined ones\n        qq.extend(options, o, true);\n\n        // Inherit instance data from FineUploader, which should in turn inherit from s3.FineUploaderBasic.\n        qq.FineUploader.call(this, options, \"s3\");\n\n        if (!qq.supportedFeatures.ajaxUploading && options.iframeSupport.localBlankPagePath === undefined) {\n            this._options.element.innerHTML = \"<div>You MUST set the <code>localBlankPagePath</code> property \" +\n                \"of the <code>iframeSupport</code> option since this browser does not support the File API!</div>\";\n        }\n    };\n\n    // Inherit the API methods from FineUploaderBasicS3\n    qq.extend(qq.s3.FineUploader.prototype, qq.s3.FineUploaderBasic.prototype);\n\n    // Inherit public and private API methods related to UI\n    qq.extend(qq.s3.FineUploader.prototype, qq.uiPublicApi);\n    qq.extend(qq.s3.FineUploader.prototype, qq.uiPrivateApi);\n}());\n"]}