diff --git a/application/extensions/yiiwheels/widgets/fineuploader/WhFineUploader.php b/application/extensions/yiiwheels/widgets/fineuploader/WhFineUploader.php deleted file mode 100644 index 026197062d9..00000000000 --- a/application/extensions/yiiwheels/widgets/fineuploader/WhFineUploader.php +++ /dev/null @@ -1,175 +0,0 @@ - - * @copyright Copyright © 2amigos.us 2013- - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @package YiiWheels.widgets.fileuploader - * @uses YiiStrap.helpers.TbArray - */ -Yii::import('bootstrap.helpers.TbArray'); - -class WhFineUploader extends CInputWidget -{ - /** - * @var string upload action url - */ - public $uploadAction; - - /** - * @var string the HTML tag to render the uploader to - */ - public $tagName = 'div'; - - /** - * @var string text to display if javascript is disabled - */ - public $noScriptText; - - /** - * @var array the plugin options - */ - public $pluginOptions = array(); - - /** - * @var array the events - */ - public $events = array(); - - /** - * @var string which scenario we get the validation from - */ - public $scenario; - - /** - * @var array d - */ - protected $defaultOptions = array(); - - /** - * @throws CException - */ - public function init() - { - if ($this->uploadAction === null) { - throw new CException(Yii::t('zii', '"uploadAction" attribute cannot be blank')); - } - if ($this->noScriptText === null) { - $this->noScriptText = Yii::t('zii', "Please enable JavaScript to use file uploader."); - } - - $this->attachBehavior('ywplugin', array('class' => 'yiiwheels.behaviors.WhPlugin')); - - $this->initDefaultOptions(); - } - - /** - * Widget's run method - */ - public function run() - { - $this->renderTag(); - $this->registerClientScript(); - } - - /** - * Renders the tag where the button is going to be rendered - */ - public function renderTag() - { - echo CHtml::tag($this->tagName, $this->htmlOptions, '', true); - } - - /** - * Registers required client script for finuploader - */ - public function registerClientScript() - { - /* publish assets dir */ - $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'assets'; - $assetsUrl = $this->getAssetsUrl($path); - - /* @var $cs CClientScript */ - $cs = Yii::app()->getClientScript(); - - $script = YII_DEBUG ? 'jquery.fineuploader-3.2.js' : 'jquery.fineuploader-3.2.min.js'; - - $cs->registerCssFile($assetsUrl . '/css/fineuploader.css'); - $cs->registerScriptFile($assetsUrl . '/js/' . $script); - - /* initialize plugin */ - $selector = '#' . TbArray::getValue('id', $this->htmlOptions, $this->getId()); - - $this->getApi()->registerPlugin( - 'fineUploader', - $selector, - CMap::mergeArray($this->defaultOptions, $this->pluginOptions) - ); - $this->getApi()->registerEvents($selector, $this->events); - } - - /** - * Sets up default options for the plugin - * - thanks https://github.com/anggiaj - */ - protected function initDefaultOptions() - { - list($name, $id) = $this->resolveNameID(); - - TbArray::defaultValue('id', $id, $this->htmlOptions); - TbArray::defaultValue('name', $name, $this->htmlOptions); - - - $this->defaultOptions = array( - 'request' => array( - 'endpoint' => $this->uploadAction, - 'inputName' => $name, - ), - 'validation' => $this->getValidator(), - 'messages' => array( - 'typeError' => Yii::t('zii', '{file} has an invalid extension. Valid extension(s): {extensions}.'), - 'sizeError' => Yii::t('zii', '{file} is too large, maximum file size is {sizeLimit}.'), - 'minSizeError' => Yii::t('zii', '{file} is too small, minimum file size is {minSizeLimit}.'), - 'emptyError:' => Yii::t('zii', '{file} is empty, please select files again without it.'), - 'noFilesError' => Yii::t('zii', 'No files to upload.'), - 'onLeave' => Yii::t( - 'zii', - 'The files are being uploaded, if you leave now the upload will be cancelled.' - ) - ), - ); - } - - /** - * @return array - */ - protected function getValidator() - { - $ret = array(); - if ($this->hasModel()) { - if ($this->scenario !== null) { - $originalScenario = $this->model->getScenario(); - $this->model->setScenario($this->scenario); - $validators = $this->model->getValidators($this->attribute); - $this->model->setScenario($originalScenario); - - } else { - $validators = $this->model->getValidators($this->attribute); - } - - // we are just looking for the first founded CFileValidator - foreach ($validators as $validator) { - if (is_a($validator, 'CFileValidator')) { - $ret = array( - 'allowedExtensions' => explode(',', str_replace(' ', '', $validator->types)), - 'sizeLimit' => $validator->maxSize, - 'minSizeLimit' => $validator->minSize, - ); - break; - } - } - } - return $ret; - } -} \ No newline at end of file diff --git a/application/extensions/yiiwheels/widgets/fineuploader/assets/css/fineuploader.css b/application/extensions/yiiwheels/widgets/fineuploader/assets/css/fineuploader.css deleted file mode 100644 index 9e5941388f5..00000000000 --- a/application/extensions/yiiwheels/widgets/fineuploader/assets/css/fineuploader.css +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Original version: 1.0 © 2010 Andrew Valums ( andrew(at)valums.com ) - * Current Maintainer (2.0+): 2012, Ray Nicholus ( fineuploader(at)garstasio.com ) - * - * Licensed under MIT license, GNU GPL 2 or later, GNU LGPL 2 or later, see license.txt. - */ -.qq-uploader { - position: relative; - width: 100%; -} - -.qq-upload-button { - display: block; - width: 105px; - padding: 7px 0; - text-align: center; - background: #880000; - border-bottom: 1px solid #DDD; - color: #FFF; -} - -.qq-upload-button-hover { - background: #CC0000; -} - -.qq-upload-button-focus { - outline: 1px dotted #000000; -} - -.qq-upload-drop-area, .qq-upload-extra-drop-area { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - min-height: 30px; - z-index: 2; - background: #FF9797; - text-align: center; -} - -.qq-upload-drop-area span { - display: block; - position: absolute; - top: 50%; - width: 100%; - margin-top: -8px; - font-size: 16px; -} - -.qq-upload-extra-drop-area { - position: relative; - margin-top: 50px; - font-size: 16px; - padding-top: 30px; - height: 20px; - min-height: 40px; -} - -.qq-upload-drop-area-active { - background: #FF7171; -} - -.qq-upload-list { - margin: 0; - padding: 0; - list-style: none; -} - -.qq-upload-list li { - margin: 0; - padding: 9px; - line-height: 15px; - font-size: 16px; - background-color: #FFF0BD; -} - -.qq-upload-file, .qq-upload-spinner, .qq-upload-size, .qq-upload-cancel, .qq-upload-retry, .qq-upload-failed-text, .qq-upload-finished { - margin-right: 12px; -} - -.qq-upload-file { -} - -.qq-upload-spinner { - display: inline-block; - background: url("../img/loading.gif"); - width: 15px; - height: 15px; - vertical-align: text-bottom; -} - -.qq-drop-processing { - display: none; -} - -.qq-drop-processing-spinner { - display: inline-block; - background: url("../img/processing.gif"); - width: 24px; - height: 24px; - vertical-align: text-bottom; -} - -.qq-upload-finished { - display: none; - width: 15px; - height: 15px; - vertical-align: text-bottom; -} - -.qq-upload-retry { - display: none; - color: #000000; -} - -.qq-upload-cancel { - color: #000000; -} - -.qq-upload-retryable .qq-upload-retry { - display: inline; -} - -.qq-upload-size, .qq-upload-cancel, .qq-upload-retry { - font-size: 12px; - font-weight: normal; -} - -.qq-upload-failed-text { - display: none; - font-style: italic; - font-weight: bold; -} - -.qq-upload-failed-icon { - display: none; - width: 15px; - height: 15px; - vertical-align: text-bottom; -} - -.qq-upload-fail .qq-upload-failed-text { - display: inline; -} - -.qq-upload-retrying .qq-upload-failed-text { - display: inline; - color: #D60000; -} - -.qq-upload-list li.qq-upload-success { - background-color: #5DA30C; - color: #FFFFFF; -} - -.qq-upload-list li.qq-upload-fail { - background-color: #D60000; - color: #FFFFFF; -} - -.qq-progress-bar { - background: -moz-linear-gradient(top, rgba(30, 87, 153, 1) 0%, rgba(41, 137, 216, 1) 50%, rgba(32, 124, 202, 1) 51%, rgba(125, 185, 232, 1) 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(30, 87, 153, 1)), color-stop(50%, rgba(41, 137, 216, 1)), color-stop(51%, rgba(32, 124, 202, 1)), color-stop(100%, rgba(125, 185, 232, 1))); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, rgba(30, 87, 153, 1) 0%, rgba(41, 137, 216, 1) 50%, rgba(32, 124, 202, 1) 51%, rgba(125, 185, 232, 1) 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, rgba(30, 87, 153, 1) 0%, rgba(41, 137, 216, 1) 50%, rgba(32, 124, 202, 1) 51%, rgba(125, 185, 232, 1) 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, rgba(30, 87, 153, 1) 0%, rgba(41, 137, 216, 1) 50%, rgba(32, 124, 202, 1) 51%, rgba(125, 185, 232, 1) 100%); /* IE10+ */ - background: linear-gradient(to bottom, rgba(30, 87, 153, 1) 0%, rgba(41, 137, 216, 1) 50%, rgba(32, 124, 202, 1) 51%, rgba(125, 185, 232, 1) 100%); /* W3C */ - width: 0%; - height: 15px; - border-radius: 6px; - margin-bottom: 3px; - display: none; -} diff --git a/application/extensions/yiiwheels/widgets/fineuploader/assets/img/loading.gif b/application/extensions/yiiwheels/widgets/fineuploader/assets/img/loading.gif deleted file mode 100644 index 6fba77609ec..00000000000 Binary files a/application/extensions/yiiwheels/widgets/fineuploader/assets/img/loading.gif and /dev/null differ diff --git a/application/extensions/yiiwheels/widgets/fineuploader/assets/img/processing.gif b/application/extensions/yiiwheels/widgets/fineuploader/assets/img/processing.gif deleted file mode 100644 index 7c99504e10b..00000000000 Binary files a/application/extensions/yiiwheels/widgets/fineuploader/assets/img/processing.gif and /dev/null differ diff --git a/application/extensions/yiiwheels/widgets/fineuploader/assets/js/jquery.fineuploader-3.2.js b/application/extensions/yiiwheels/widgets/fineuploader/assets/js/jquery.fineuploader-3.2.js deleted file mode 100644 index a872b707a77..00000000000 --- a/application/extensions/yiiwheels/widgets/fineuploader/assets/js/jquery.fineuploader-3.2.js +++ /dev/null @@ -1,3245 +0,0 @@ -/** - * http://github.com/Valums-File-Uploader/file-uploader - * - * Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers. - * - * Original version: 1.0 © 2010 Andrew Valums ( andrew(at)valums.com ) - * Current Maintainer (2.0+): © 2012, Ray Nicholus ( fineuploader(at)garstasio.com ) - * - * Licensed under MIT license, GNU GPL 2 or later, GNU LGPL 2 or later, see license.txt. - */ -/*globals window, navigator, document, FormData, File, HTMLInputElement, XMLHttpRequest*/ -var qq = function (element) { - "use strict"; - - return { - hide: function () { - element.style.display = 'none'; - return this; - }, - - /** Returns the function which detaches attached event */ - attach: function (type, fn) { - if (element.addEventListener) { - element.addEventListener(type, fn, false); - } else if (element.attachEvent) { - element.attachEvent('on' + type, fn); - } - return function () { - qq(element).detach(type, fn); - }; - }, - - detach: function (type, fn) { - if (element.removeEventListener) { - element.removeEventListener(type, fn, false); - } else if (element.attachEvent) { - element.detachEvent('on' + type, fn); - } - return this; - }, - - contains: function (descendant) { - // compareposition returns false in this case - if (element === descendant) { - return true; - } - - if (element.contains) { - return element.contains(descendant); - } else { - /*jslint bitwise: true*/ - return !!(descendant.compareDocumentPosition(element) & 8); - } - }, - - /** - * Insert this element before elementB. - */ - insertBefore: function (elementB) { - elementB.parentNode.insertBefore(element, elementB); - return this; - }, - - remove: function () { - element.parentNode.removeChild(element); - return this; - }, - - /** - * Sets styles for an element. - * Fixes opacity in IE6-8. - */ - css: function (styles) { - if (styles.opacity !== null) { - if (typeof element.style.opacity !== 'string' && typeof(element.filters) !== 'undefined') { - styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')'; - } - } - qq.extend(element.style, styles); - - return this; - }, - - hasClass: function (name) { - var re = new RegExp('(^| )' + name + '( |$)'); - return re.test(element.className); - }, - - addClass: function (name) { - if (!qq(element).hasClass(name)) { - element.className += ' ' + name; - } - return this; - }, - - removeClass: function (name) { - var re = new RegExp('(^| )' + name + '( |$)'); - element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, ""); - return this; - }, - - getByClass: function (className) { - var candidates, - result = []; - - if (element.querySelectorAll) { - return element.querySelectorAll('.' + className); - } - - candidates = element.getElementsByTagName("*"); - - qq.each(candidates, function (idx, val) { - if (qq(val).hasClass(className)) { - result.push(val); - } - }); - return result; - }, - - children: function () { - var children = [], - child = element.firstChild; - - while (child) { - if (child.nodeType === 1) { - children.push(child); - } - child = child.nextSibling; - } - - return children; - }, - - setText: function (text) { - element.innerText = text; - element.textContent = text; - return this; - }, - - clearText: function () { - return qq(element).setText(""); - } - }; -}; - -qq.log = function (message, level) { - "use strict"; - - if (window.console) { - if (!level || level === 'info') { - window.console.log(message); - } - else { - if (window.console[level]) { - window.console[level](message); - } - else { - window.console.log('<' + level + '> ' + message); - } - } - } -}; - -qq.isObject = function (variable) { - "use strict"; - return variable !== null && variable && typeof(variable) === "object" && variable.constructor === Object; -}; - -qq.isFunction = function (variable) { - "use strict"; - return typeof(variable) === "function"; -}; - -qq.isFileOrInput = function (maybeFileOrInput) { - "use strict"; - if (window.File && maybeFileOrInput instanceof File) { - return true; - } - else if (window.HTMLInputElement) { - if (maybeFileOrInput instanceof HTMLInputElement) { - if (maybeFileOrInput.type && maybeFileOrInput.type.toLowerCase() === 'file') { - return true; - } - } - } - else if (maybeFileOrInput.tagName) { - if (maybeFileOrInput.tagName.toLowerCase() === 'input') { - if (maybeFileOrInput.type && maybeFileOrInput.type.toLowerCase() === 'file') { - return true; - } - } - } - - return false; -}; - -qq.isXhrUploadSupported = function () { - "use strict"; - var input = document.createElement('input'); - input.type = 'file'; - - return ( - input.multiple !== undefined && - typeof File !== "undefined" && - typeof FormData !== "undefined" && - typeof (new XMLHttpRequest()).upload !== "undefined" ); -}; - -qq.isFolderDropSupported = function (dataTransfer) { - "use strict"; - return (dataTransfer.items && dataTransfer.items[0].webkitGetAsEntry); -}; - -qq.isFileChunkingSupported = function () { - "use strict"; - return !qq.android() && //android's impl of Blob.slice is broken - qq.isXhrUploadSupported() && - (File.prototype.slice || File.prototype.webkitSlice || File.prototype.mozSlice); -}; - -qq.extend = function (first, second, extendNested) { - "use strict"; - qq.each(second, function (prop, val) { - if (extendNested && qq.isObject(val)) { - if (first[prop] === undefined) { - first[prop] = {}; - } - qq.extend(first[prop], val, true); - } - else { - first[prop] = val; - } - }); -}; - -/** - * Searches for a given element in the array, returns -1 if it is not present. - * @param {Number} [from] The index at which to begin the search - */ -qq.indexOf = function (arr, elt, from) { - "use strict"; - - if (arr.indexOf) { - return arr.indexOf(elt, from); - } - - from = from || 0; - var len = arr.length; - - if (from < 0) { - from += len; - } - - for (; from < len; from += 1) { - if (arr.hasOwnProperty(from) && arr[from] === elt) { - return from; - } - } - return -1; -}; - -//this is a version 4 UUID -qq.getUniqueId = function () { - "use strict"; - - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - /*jslint eqeq: true, bitwise: true*/ - var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -}; - -// -// Browsers and platforms detection - -qq.ie = function () { - "use strict"; - return navigator.userAgent.indexOf('MSIE') !== -1; -}; -qq.ie10 = function () { - "use strict"; - return navigator.userAgent.indexOf('MSIE 10') !== -1; -}; -qq.safari = function () { - "use strict"; - return navigator.vendor !== undefined && navigator.vendor.indexOf("Apple") !== -1; -}; -qq.chrome = function () { - "use strict"; - return navigator.vendor !== undefined && navigator.vendor.indexOf('Google') !== -1; -}; -qq.firefox = function () { - "use strict"; - return (navigator.userAgent.indexOf('Mozilla') !== -1 && navigator.vendor !== undefined && navigator.vendor === ''); -}; -qq.windows = function () { - "use strict"; - return navigator.platform === "Win32"; -}; -qq.android = function () { - "use strict"; - return navigator.userAgent.toLowerCase().indexOf('android') !== -1; -}; - -// -// Events - -qq.preventDefault = function (e) { - "use strict"; - if (e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; - } -}; - -/** - * Creates and returns element from html string - * Uses innerHTML to create an element - */ -qq.toElement = (function () { - "use strict"; - var div = document.createElement('div'); - return function (html) { - div.innerHTML = html; - var element = div.firstChild; - div.removeChild(element); - return element; - }; -}()); - -//key and value are passed to callback for each item in the object or array -qq.each = function (obj, callback) { - "use strict"; - var key, retVal; - if (obj) { - for (key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - retVal = callback(key, obj[key]); - if (retVal === false) { - break; - } - } - } - } -}; - -/** - * obj2url() takes a json-object as argument and generates - * a querystring. pretty much like jQuery.param() - * - * how to use: - * - * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');` - * - * will result in: - * - * `http://any.url/upload?otherParam=value&a=b&c=d` - * - * @param Object JSON-Object - * @param String current querystring-part - * @return String encoded querystring - */ -qq.obj2url = function (obj, temp, prefixDone) { - "use strict"; - /*jshint laxbreak: true*/ - var i, len, - uristrings = [], - prefix = '&', - add = function (nextObj, i) { - var nextTemp = temp - ? (/\[\]$/.test(temp)) // prevent double-encoding - ? temp - : temp + '[' + i + ']' - : i; - if ((nextTemp !== 'undefined') && (i !== 'undefined')) { - uristrings.push( - (typeof nextObj === 'object') - ? qq.obj2url(nextObj, nextTemp, true) - : (Object.prototype.toString.call(nextObj) === '[object Function]') - ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj()) - : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj) - ); - } - }; - - if (!prefixDone && temp) { - prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?'; - uristrings.push(temp); - uristrings.push(qq.obj2url(obj)); - } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj !== 'undefined')) { - // we wont use a for-in-loop on an array (performance) - for (i = -1, len = obj.length; i < len; i += 1) { - add(obj[i], i); - } - } else if ((typeof obj !== 'undefined') && (obj !== null) && (typeof obj === "object")) { - // for anything else but a scalar, we will use for-in-loop - for (i in obj) { - if (obj.hasOwnProperty(i)) { - add(obj[i], i); - } - } - } else { - uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj)); - } - - if (temp) { - return uristrings.join(prefix); - } else { - return uristrings.join(prefix) - .replace(/^&/, '') - .replace(/%20/g, '+'); - } -}; - -qq.obj2FormData = function (obj, formData, arrayKeyName) { - "use strict"; - if (!formData) { - formData = new FormData(); - } - - qq.each(obj, function (key, val) { - key = arrayKeyName ? arrayKeyName + '[' + key + ']' : key; - - if (qq.isObject(val)) { - qq.obj2FormData(val, formData, key); - } - else if (qq.isFunction(val)) { - formData.append(encodeURIComponent(key), encodeURIComponent(val())); - } - else { - formData.append(encodeURIComponent(key), encodeURIComponent(val)); - } - }); - - return formData; -}; - -qq.obj2Inputs = function (obj, form) { - "use strict"; - var input; - - if (!form) { - form = document.createElement('form'); - } - - qq.obj2FormData(obj, { - append: function (key, val) { - input = document.createElement('input'); - input.setAttribute('name', key); - input.setAttribute('value', val); - form.appendChild(input); - } - }); - - return form; -}; - -qq.setCookie = function (name, value, days) { - var date = new Date(), - expires = ""; - - if (days) { - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } - - document.cookie = name + "=" + value + expires + "; path=/"; -}; - -qq.getCookie = function (name) { - var nameEQ = name + "=", - ca = document.cookie.split(';'), - c; - - for (var i = 0; i < ca.length; i++) { - c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return c.substring(nameEQ.length, c.length); - } - } -}; - -qq.getCookieNames = function (regexp) { - var cookies = document.cookie.split(';'), - cookieNames = []; - - qq.each(cookies, function (idx, cookie) { - cookie = cookie.trim(); - - var equalsIdx = cookie.indexOf("="); - - if (cookie.match(regexp)) { - cookieNames.push(cookie.substr(0, equalsIdx)); - } - }); - - return cookieNames; -}; - -qq.deleteCookie = function (name) { - qq.setCookie(name, "", -1); -}; - -qq.areCookiesEnabled = function () { - var randNum = Math.random() * 100000, - name = "qqCookieTest:" + randNum; - qq.setCookie(name, 1); - - if (qq.getCookie(name)) { - qq.deleteCookie(name); - return true; - } - return false; -}; - -/** - * Not recommended for use outside of Fine Uploader since this falls back to an unchecked eval if JSON.parse is not - * implemented. For a more secure JSON.parse polyfill, use Douglas Crockford's json2.js. - */ -qq.parseJson = function (json) { - /*jshint evil: true*/ - if (typeof JSON.parse === "function") { - return JSON.parse(json); - } else { - return eval("(" + json + ")"); - } -}; - -/** - * A generic module which supports object disposing in dispose() method. - * */ -qq.DisposeSupport = function () { - "use strict"; - var disposers = []; - - return { - /** Run all registered disposers */ - dispose: function () { - var disposer; - do { - disposer = disposers.shift(); - if (disposer) { - disposer(); - } - } - while (disposer); - }, - - /** Attach event handler and register de-attacher as a disposer */ - attach: function () { - var args = arguments; - /*jslint undef:true*/ - this.addDisposer(qq(args[0]).attach.apply(this, Array.prototype.slice.call(arguments, 1))); - }, - - /** Add disposer to the collection */ - addDisposer: function (disposeFunction) { - disposers.push(disposeFunction); - } - }; -}; -qq.UploadButton = function (o) { - this._options = { - element: null, - // if set to true adds multiple attribute to file input - multiple: false, - acceptFiles: null, - // name attribute of file input - name: 'file', - onChange: function (input) { - }, - hoverClass: 'qq-upload-button-hover', - focusClass: 'qq-upload-button-focus' - }; - - qq.extend(this._options, o); - this._disposeSupport = new qq.DisposeSupport(); - - this._element = this._options.element; - - // make button suitable container for input - qq(this._element).css({ - position: 'relative', - overflow: 'hidden', - // Make sure browse button is in the right side - // in Internet Explorer - direction: 'ltr' - }); - - this._input = this._createInput(); -}; - -qq.UploadButton.prototype = { - /* returns file input element */ - getInput: function () { - return this._input; - }, - /* cleans/recreates the file input */ - reset: function () { - if (this._input.parentNode) { - qq(this._input).remove(); - } - - qq(this._element).removeClass(this._options.focusClass); - this._input = this._createInput(); - }, - _createInput: function () { - var input = document.createElement("input"); - - if (this._options.multiple) { - input.setAttribute("multiple", "multiple"); - } - - if (this._options.acceptFiles) input.setAttribute("accept", this._options.acceptFiles); - - input.setAttribute("type", "file"); - input.setAttribute("name", this._options.name); - - qq(input).css({ - position: 'absolute', - // in Opera only 'browse' button - // is clickable and it is located at - // the right side of the input - right: 0, - top: 0, - fontFamily: 'Arial', - // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118 - fontSize: '118px', - margin: 0, - padding: 0, - cursor: 'pointer', - opacity: 0 - }); - - this._element.appendChild(input); - - var self = this; - this._disposeSupport.attach(input, 'change', function () { - self._options.onChange(input); - }); - - this._disposeSupport.attach(input, 'mouseover', function () { - qq(self._element).addClass(self._options.hoverClass); - }); - this._disposeSupport.attach(input, 'mouseout', function () { - qq(self._element).removeClass(self._options.hoverClass); - }); - this._disposeSupport.attach(input, 'focus', function () { - qq(self._element).addClass(self._options.focusClass); - }); - this._disposeSupport.attach(input, 'blur', function () { - qq(self._element).removeClass(self._options.focusClass); - }); - - // IE and Opera, unfortunately have 2 tab stops on file input - // which is unacceptable in our case, disable keyboard access - if (window.attachEvent) { - // it is IE or Opera - input.setAttribute('tabIndex', "-1"); - } - - return input; - } -}; -/** - * Class for uploading files, uploading itself is handled by child classes - */ -/*globals qq*/ -qq.UploadHandler = function (o) { - "use strict"; - - var queue = [], - options, log, dequeue, handlerImpl; - - // Default options, can be overridden by the user - options = { - debug: false, - forceMultipart: true, - paramsInBody: false, - paramsStore: {}, - endpointStore: {}, - maxConnections: 3, // maximum number of concurrent uploads - uuidParamName: 'qquuid', - totalFileSizeParamName: 'qqtotalfilesize', - chunking: { - enabled: false, - partSize: 2000000, //bytes - paramNames: { - partIndex: 'qqpartindex', - partByteOffset: 'qqpartbyteoffset', - chunkSize: 'qqchunksize', - totalParts: 'qqtotalparts', - filename: 'qqfilename' - } - }, - resume: { - enabled: false, - id: null, - cookiesExpireIn: 7, //days - paramNames: { - resuming: "qqresume" - } - }, - log: function (str, level) { - }, - onProgress: function (id, fileName, loaded, total) { - }, - onComplete: function (id, fileName, response, xhr) { - }, - onCancel: function (id, fileName) { - }, - onUpload: function (id, fileName) { - }, - onUploadChunk: function (id, fileName, chunkData) { - }, - onAutoRetry: function (id, fileName, response, xhr) { - }, - onResume: function (id, fileName, chunkData) { - } - - }; - qq.extend(options, o); - - log = options.log; - - /** - * Removes element from queue, starts upload of next - */ - dequeue = function (id) { - var i = qq.indexOf(queue, id), - max = options.maxConnections, - nextId; - - queue.splice(i, 1); - - if (queue.length >= max && i < max) { - nextId = queue[max - 1]; - handlerImpl.upload(nextId); - } - }; - - if (qq.isXhrUploadSupported()) { - handlerImpl = new qq.UploadHandlerXhr(options, dequeue, log); - } - else { - handlerImpl = new qq.UploadHandlerForm(options, dequeue, log); - } - - - return { - /** - * Adds file or file input to the queue - * @returns id - **/ - add: function (file) { - return handlerImpl.add(file); - }, - /** - * Sends the file identified by id - */ - upload: function (id) { - var len = queue.push(id); - - // if too many active uploads, wait... - if (len <= options.maxConnections) { - return handlerImpl.upload(id); - } - }, - retry: function (id) { - var i = qq.indexOf(queue, id); - if (i >= 0) { - return handlerImpl.upload(id, true); - } - else { - return this.upload(id); - } - }, - /** - * Cancels file upload by id - */ - cancel: function (id) { - log('Cancelling ' + id); - options.paramsStore.remove(id); - handlerImpl.cancel(id); - dequeue(id); - }, - /** - * Cancels all uploads - */ - cancelAll: function () { - qq.each(queue, function (idx, fileId) { - this.cancel(fileId); - }); - - queue = []; - }, - /** - * Returns name of the file identified by id - */ - getName: function (id) { - return handlerImpl.getName(id); - }, - /** - * Returns size of the file identified by id - */ - getSize: function (id) { - if (handlerImpl.getSize) { - return handlerImpl.getSize(id); - } - }, - getFile: function (id) { - if (handlerImpl.getFile) { - return handlerImpl.getFile(id); - } - }, - /** - * Returns id of files being uploaded or - * waiting for their turn - */ - getQueue: function () { - return queue; - }, - reset: function () { - log('Resetting upload handler'); - queue = []; - handlerImpl.reset(); - }, - getUuid: function (id) { - return handlerImpl.getUuid(id); - }, - /** - * Determine if the file exists. - */ - isValid: function (id) { - return handlerImpl.isValid(id); - }, - getResumableFilesData: function () { - if (handlerImpl.getResumableFilesData) { - return handlerImpl.getResumableFilesData(); - } - return []; - } - }; -}; -/*globals qq, document, setTimeout*/ -/*jslint white: true*/ -qq.UploadHandlerForm = function (o, uploadCompleteCallback, logCallback) { - "use strict"; - - var options = o, - inputs = [], - uuids = [], - detachLoadEvents = {}, - uploadComplete = uploadCompleteCallback, - log = logCallback, - api; - - function attachLoadEvent(iframe, callback) { - /*jslint eqeq: true*/ - - detachLoadEvents[iframe.id] = qq(iframe).attach('load', function () { - log('Received response for ' + iframe.id); - - // when we remove iframe from dom - // the request stops, but in IE load - // event fires - if (!iframe.parentNode) { - return; - } - - try { - // fixing Opera 10.53 - if (iframe.contentDocument && - iframe.contentDocument.body && - iframe.contentDocument.body.innerHTML == "false") { - // In Opera event is fired second time - // when body.innerHTML changed from false - // to server response approx. after 1 sec - // when we upload file with iframe - return; - } - } - catch (error) { - //IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases - log('Error when attempting to access iframe during handling of upload response (' + error + ")", 'error'); - } - - callback(); - }); - } - - /** - * Returns json object received by iframe from server. - */ - function getIframeContentJson(iframe) { - /*jshint evil: true*/ - - var response; - - //IE may throw an "access is denied" error when attempting to access contentDocument on the iframe in some cases - try { - // iframe.contentWindow.document - for IE<7 - var doc = iframe.contentDocument || iframe.contentWindow.document, - innerHTML = doc.body.innerHTML; - - log("converting iframe's innerHTML to JSON"); - log("innerHTML = " + innerHTML); - //plain text response may be wrapped in
tag - if (innerHTML && innerHTML.match(/^'); - // src="javascript:false;" removes ie6 prompt on https - - iframe.setAttribute('id', id); - - iframe.style.display = 'none'; - document.body.appendChild(iframe); - - return iframe; - } - - /** - * Creates form, that will be submitted to iframe - */ - function createForm(id, iframe) { - var params = options.paramsStore.getParams(id), - protocol = options.demoMode ? "GET" : "POST", - form = qq.toElement(''), - endpoint = options.endpointStore.getEndpoint(id), - url = endpoint; - - params[options.uuidParamName] = uuids[id]; - - if (!options.paramsInBody) { - url = qq.obj2url(params, endpoint); - } - else { - qq.obj2Inputs(params, form); - } - - form.setAttribute('action', url); - form.setAttribute('target', iframe.name); - form.style.display = 'none'; - document.body.appendChild(form); - - return form; - } - - - api = { - add: function (fileInput) { - fileInput.setAttribute('name', options.inputName); - - var id = inputs.push(fileInput) - 1; - uuids[id] = qq.getUniqueId(); - - // remove file input from DOM - if (fileInput.parentNode) { - qq(fileInput).remove(); - } - - return id; - }, - getName: function (id) { - /*jslint regexp: true*/ - - // get input value and remove path to normalize - return inputs[id].value.replace(/.*(\/|\\)/, ""); - }, - isValid: function (id) { - return inputs[id] !== undefined; - }, - reset: function () { - qq.UploadHandler.prototype.reset.apply(this, arguments); - inputs = []; - uuids = []; - detachLoadEvents = {}; - }, - getUuid: function (id) { - return uuids[id]; - }, - cancel: function (id) { - options.onCancel(id, this.getName(id)); - - delete inputs[id]; - delete uuids[id]; - delete detachLoadEvents[id]; - - var iframe = document.getElementById(id); - if (iframe) { - // to cancel request set src to something else - // we use src="javascript:false;" because it doesn't - // trigger ie6 prompt on https - iframe.setAttribute('src', 'java' + String.fromCharCode(115) + 'cript:false;'); //deal with "JSLint: javascript URL" warning, which apparently cannot be turned off - - qq(iframe).remove(); - } - }, - upload: function (id) { - var input = inputs[id], - fileName = api.getName(id), - iframe = createIframe(id), - form = createForm(id, iframe); - - if (!input) { - throw new Error('file with passed id was not added, or already uploaded or cancelled'); - } - - options.onUpload(id, this.getName(id)); - - form.appendChild(input); - - attachLoadEvent(iframe, function () { - log('iframe loaded'); - - var response = getIframeContentJson(iframe); - - // timeout added to fix busy state in FF3.6 - setTimeout(function () { - detachLoadEvents[id](); - delete detachLoadEvents[id]; - qq(iframe).remove(); - }, 1); - - if (!response.success) { - if (options.onAutoRetry(id, fileName, response)) { - return; - } - } - options.onComplete(id, fileName, response); - uploadComplete(id); - }); - - log('Sending upload request for ' + id); - form.submit(); - qq(form).remove(); - - return id; - } - }; - - return api; -}; -/*globals qq, File, XMLHttpRequest, FormData*/ -qq.UploadHandlerXhr = function (o, uploadCompleteCallback, logCallback) { - "use strict"; - - var options = o, - uploadComplete = uploadCompleteCallback, - log = logCallback, - fileState = [], - cookieItemDelimiter = "|", - chunkFiles = options.chunking.enabled && qq.isFileChunkingSupported(), - resumeEnabled = options.resume.enabled && chunkFiles && qq.areCookiesEnabled(), - resumeId = getResumeId(), - multipart = options.forceMultipart || options.paramsInBody, - api; - - - function addChunkingSpecificParams(id, params, chunkData) { - var size = api.getSize(id), - name = api.getName(id); - - params[options.chunking.paramNames.partIndex] = chunkData.part; - params[options.chunking.paramNames.partByteOffset] = chunkData.start; - params[options.chunking.paramNames.chunkSize] = chunkData.end - chunkData.start; - params[options.chunking.paramNames.totalParts] = chunkData.count; - params[options.totalFileSizeParamName] = size; - - - /** - * When a Blob is sent in a multipart request, the filename value in the content-disposition header is either "blob" - * or an empty string. So, we will need to include the actual file name as a param in this case. - */ - if (multipart) { - params[options.chunking.paramNames.filename] = name; - } - } - - function addResumeSpecificParams(params) { - params[options.resume.paramNames.resuming] = true; - } - - function getChunk(file, startByte, endByte) { - if (file.slice) { - return file.slice(startByte, endByte); - } - else if (file.mozSlice) { - return file.mozSlice(startByte, endByte); - } - else if (file.webkitSlice) { - return file.webkitSlice(startByte, endByte); - } - } - - function getChunkData(id, chunkIndex) { - var chunkSize = options.chunking.partSize, - fileSize = api.getSize(id), - file = fileState[id].file, - startBytes = chunkSize * chunkIndex, - endBytes = startBytes + chunkSize >= fileSize ? fileSize : startBytes + chunkSize, - totalChunks = getTotalChunks(id); - - return { - part: chunkIndex, - start: startBytes, - end: endBytes, - count: totalChunks, - blob: getChunk(file, startBytes, endBytes) - }; - } - - function getTotalChunks(id) { - var fileSize = api.getSize(id), - chunkSize = options.chunking.partSize; - - return Math.ceil(fileSize / chunkSize); - } - - function createXhr(id) { - fileState[id].xhr = new XMLHttpRequest(); - return fileState[id].xhr; - } - - function setParamsAndGetEntityToSend(params, xhr, fileOrBlob, id) { - var formData = new FormData(), - protocol = options.demoMode ? "GET" : "POST", - endpoint = options.endpointStore.getEndpoint(id), - url = endpoint, - name = api.getName(id), - size = api.getSize(id); - - params[options.uuidParamName] = fileState[id].uuid; - - if (multipart) { - params[options.totalFileSizeParamName] = size; - } - - //build query string - if (!options.paramsInBody) { - params[options.inputName] = name; - url = qq.obj2url(params, endpoint); - } - - xhr.open(protocol, url, true); - if (multipart) { - if (options.paramsInBody) { - qq.obj2FormData(params, formData); - } - - formData.append(options.inputName, fileOrBlob); - return formData; - } - - return fileOrBlob; - } - - function setHeaders(id, xhr) { - var extraHeaders = options.customHeaders, - name = api.getName(id), - file = fileState[id].file; - - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - xhr.setRequestHeader("Cache-Control", "no-cache"); - - if (!multipart) { - xhr.setRequestHeader("Content-Type", "application/octet-stream"); - //NOTE: return mime type in xhr works on chrome 16.0.9 firefox 11.0a2 - xhr.setRequestHeader("X-Mime-Type", file.type); - } - - qq.each(extraHeaders, function (name, val) { - xhr.setRequestHeader(name, val); - }); - } - - function handleCompletedFile(id, response, xhr) { - var name = api.getName(id), - size = api.getSize(id); - - fileState[id].attemptingResume = false; - - options.onProgress(id, name, size, size); - - options.onComplete(id, name, response, xhr); - delete fileState[id].xhr; - uploadComplete(id); - } - - function uploadNextChunk(id) { - var chunkData = getChunkData(id, fileState[id].remainingChunkIdxs[0]), - xhr = createXhr(id), - size = api.getSize(id), - name = api.getName(id), - toSend, params; - - if (fileState[id].loaded === undefined) { - fileState[id].loaded = 0; - } - - persistChunkData(id, chunkData); - - xhr.onreadystatechange = getReadyStateChangeHandler(id, xhr); - - xhr.upload.onprogress = function (e) { - if (e.lengthComputable) { - if (fileState[id].loaded < size) { - var totalLoaded = e.loaded + fileState[id].loaded; - options.onProgress(id, name, totalLoaded, size); - } - } - }; - - options.onUploadChunk(id, name, getChunkDataForCallback(chunkData)); - - params = options.paramsStore.getParams(id); - addChunkingSpecificParams(id, params, chunkData); - - if (fileState[id].attemptingResume) { - addResumeSpecificParams(params); - } - - toSend = setParamsAndGetEntityToSend(params, xhr, chunkData.blob, id); - setHeaders(id, xhr); - - log('Sending chunked upload request for ' + id + ": bytes " + (chunkData.start + 1) + "-" + chunkData.end + " of " + size); - xhr.send(toSend); - } - - - function handleSuccessfullyCompletedChunk(id, response, xhr) { - var chunkIdx = fileState[id].remainingChunkIdxs.shift(), - chunkData = getChunkData(id, chunkIdx); - - fileState[id].attemptingResume = false; - fileState[id].loaded += chunkData.end - chunkData.start; - - if (fileState[id].remainingChunkIdxs.length > 0) { - uploadNextChunk(id); - } - else { - deletePersistedChunkData(id); - handleCompletedFile(id, response, xhr); - } - } - - function isErrorResponse(xhr, response) { - return xhr.status !== 200 || !response.success || response.reset; - } - - function parseResponse(xhr) { - var response; - - try { - response = qq.parseJson(xhr.responseText); - } - catch (error) { - log('Error when attempting to parse xhr response text (' + error + ')', 'error'); - response = {}; - } - - return response; - } - - function handleResetResponse(id) { - log('Server has ordered chunking effort to be restarted on next attempt for file ID ' + id, 'error'); - - if (resumeEnabled) { - deletePersistedChunkData(id); - } - fileState[id].remainingChunkIdxs = []; - delete fileState[id].loaded; - } - - function handleResetResponseOnResumeAttempt(id) { - fileState[id].attemptingResume = false; - log("Server has declared that it cannot handle resume for file ID " + id + " - starting from the first chunk", 'error'); - api.upload(id, true); - } - - function handleNonResetErrorResponse(id, response, xhr) { - var name = api.getName(id); - - if (options.onAutoRetry(id, name, response, xhr)) { - return; - } - else { - handleCompletedFile(id, response, xhr); - } - } - - function onComplete(id, xhr) { - var response; - - // the request was aborted/cancelled - if (!fileState[id]) { - return; - } - - log("xhr - server response received for " + id); - log("responseText = " + xhr.responseText); - response = parseResponse(xhr); - - if (isErrorResponse(xhr, response)) { - if (response.reset) { - handleResetResponse(id); - } - - if (fileState[id].attemptingResume && response.reset) { - handleResetResponseOnResumeAttempt(id); - } - else { - handleNonResetErrorResponse(id, response, xhr); - } - } - else if (chunkFiles) { - handleSuccessfullyCompletedChunk(id, response, xhr); - } - else { - handleCompletedFile(id, response, xhr); - } - } - - function getChunkDataForCallback(chunkData) { - return { - partIndex: chunkData.part, - startByte: chunkData.start + 1, - endByte: chunkData.end, - totalParts: chunkData.count - }; - } - - function getReadyStateChangeHandler(id, xhr) { - return function () { - if (xhr.readyState === 4) { - onComplete(id, xhr); - } - }; - } - - function persistChunkData(id, chunkData) { - var fileUuid = api.getUuid(id), - cookieName = getChunkDataCookieName(id), - cookieValue = fileUuid + cookieItemDelimiter + chunkData.part, - cookieExpDays = options.resume.cookiesExpireIn; - - qq.setCookie(cookieName, cookieValue, cookieExpDays); - } - - function deletePersistedChunkData(id) { - var cookieName = getChunkDataCookieName(id); - - qq.deleteCookie(cookieName); - } - - function getPersistedChunkData(id) { - var chunkCookieValue = qq.getCookie(getChunkDataCookieName(id)), - delimiterIndex, uuid, partIndex; - - if (chunkCookieValue) { - delimiterIndex = chunkCookieValue.indexOf(cookieItemDelimiter); - uuid = chunkCookieValue.substr(0, delimiterIndex); - partIndex = parseInt(chunkCookieValue.substr(delimiterIndex + 1, chunkCookieValue.length - delimiterIndex), 10); - - return { - uuid: uuid, - part: partIndex - }; - } - } - - function getChunkDataCookieName(id) { - var filename = api.getName(id), - fileSize = api.getSize(id), - maxChunkSize = options.chunking.partSize, - cookieName; - - cookieName = "qqfilechunk" + cookieItemDelimiter + encodeURIComponent(filename) + cookieItemDelimiter + fileSize + cookieItemDelimiter + maxChunkSize; - - if (resumeId !== undefined) { - cookieName += cookieItemDelimiter + resumeId; - } - - return cookieName; - } - - function getResumeId() { - if (options.resume.id !== null && - options.resume.id !== undefined && !qq.isFunction(options.resume.id) && !qq.isObject(options.resume.id)) { - - return options.resume.id; - } - } - - function handleFileChunkingUpload(id, retry) { - var name = api.getName(id), - firstChunkIndex = 0, - persistedChunkInfoForResume, firstChunkDataForResume, currentChunkIndex; - - if (!fileState[id].remainingChunkIdxs || fileState[id].remainingChunkIdxs.length === 0) { - fileState[id].remainingChunkIdxs = []; - - if (resumeEnabled && !retry) { - persistedChunkInfoForResume = getPersistedChunkData(id); - if (persistedChunkInfoForResume) { - firstChunkDataForResume = getChunkData(id, persistedChunkInfoForResume.part); - if (options.onResume(id, name, getChunkDataForCallback(firstChunkDataForResume)) !== false) { - firstChunkIndex = persistedChunkInfoForResume.part; - fileState[id].uuid = persistedChunkInfoForResume.uuid; - fileState[id].loaded = firstChunkDataForResume.start; - fileState[id].attemptingResume = true; - log('Resuming ' + name + " at partition index " + firstChunkIndex); - } - } - } - - for (currentChunkIndex = getTotalChunks(id) - 1; currentChunkIndex >= firstChunkIndex; currentChunkIndex -= 1) { - fileState[id].remainingChunkIdxs.unshift(currentChunkIndex); - } - } - - uploadNextChunk(id); - } - - function handleStandardFileUpload(id) { - var file = fileState[id].file, - name = api.getName(id), - xhr, params, toSend; - - fileState[id].loaded = 0; - - xhr = createXhr(id); - - xhr.upload.onprogress = function (e) { - if (e.lengthComputable) { - fileState[id].loaded = e.loaded; - options.onProgress(id, name, e.loaded, e.total); - } - }; - - xhr.onreadystatechange = getReadyStateChangeHandler(id, xhr); - - params = options.paramsStore.getParams(id); - toSend = setParamsAndGetEntityToSend(params, xhr, file, id); - setHeaders(id, xhr); - - log('Sending upload request for ' + id); - xhr.send(toSend); - } - - - api = { - /** - * Adds file to the queue - * Returns id to use with upload, cancel - **/ - add: function (file) { - if (!(file instanceof File)) { - throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)'); - } - - - var id = fileState.push({file: file}) - 1; - fileState[id].uuid = qq.getUniqueId(); - - return id; - }, - getName: function (id) { - var file = fileState[id].file; - // fix missing name in Safari 4 - //NOTE: fixed missing name firefox 11.0a2 file.fileName is actually undefined - return (file.fileName !== null && file.fileName !== undefined) ? file.fileName : file.name; - }, - getSize: function (id) { - /*jshint eqnull: true*/ - var file = fileState[id].file; - return file.fileSize != null ? file.fileSize : file.size; - }, - getFile: function (id) { - if (fileState[id]) { - return fileState[id].file; - } - }, - /** - * Returns uploaded bytes for file identified by id - */ - getLoaded: function (id) { - return fileState[id].loaded || 0; - }, - isValid: function (id) { - return fileState[id] !== undefined; - }, - reset: function () { - fileState = []; - }, - getUuid: function (id) { - return fileState[id].uuid; - }, - /** - * Sends the file identified by id to the server - */ - upload: function (id, retry) { - var name = this.getName(id); - - options.onUpload(id, name); - - if (chunkFiles) { - handleFileChunkingUpload(id, retry); - } - else { - handleStandardFileUpload(id); - } - }, - cancel: function (id) { - options.onCancel(id, this.getName(id)); - - if (fileState[id].xhr) { - fileState[id].xhr.abort(); - } - - if (resumeEnabled) { - deletePersistedChunkData(id); - } - - delete fileState[id]; - }, - getResumableFilesData: function () { - var matchingCookieNames = [], - resumableFilesData = []; - - if (chunkFiles && resumeEnabled) { - if (resumeId === undefined) { - matchingCookieNames = qq.getCookieNames(new RegExp("^qqfilechunk\\" + cookieItemDelimiter + ".+\\" + - cookieItemDelimiter + "\\d+\\" + cookieItemDelimiter + options.chunking.partSize + "=")); - } - else { - matchingCookieNames = qq.getCookieNames(new RegExp("^qqfilechunk\\" + cookieItemDelimiter + ".+\\" + - cookieItemDelimiter + "\\d+\\" + cookieItemDelimiter + options.chunking.partSize + "\\" + - cookieItemDelimiter + resumeId + "=")); - } - - qq.each(matchingCookieNames, function (idx, cookieName) { - var cookiesNameParts = cookieName.split(cookieItemDelimiter); - var cookieValueParts = qq.getCookie(cookieName).split(cookieItemDelimiter); - - resumableFilesData.push({ - name: decodeURIComponent(cookiesNameParts[1]), - size: cookiesNameParts[2], - uuid: cookieValueParts[0], - partIdx: cookieValueParts[1] - }); - }); - - return resumableFilesData; - } - return []; - } - }; - - return api; -}; -qq.FineUploaderBasic = function (o) { - var that = this; - this._options = { - debug: false, - button: null, - multiple: true, - maxConnections: 3, - disableCancelForFormUploads: false, - autoUpload: true, - request: { - endpoint: '/server/upload', - params: {}, - paramsInBody: false, - customHeaders: {}, - forceMultipart: true, - inputName: 'qqfile', - uuidName: 'qquuid', - totalFileSizeName: 'qqtotalfilesize' - }, - validation: { - allowedExtensions: [], - sizeLimit: 0, - minSizeLimit: 0, - stopOnFirstInvalidFile: true - }, - callbacks: { - onSubmit: function (id, fileName) { - }, - onComplete: function (id, fileName, responseJSON) { - }, - onCancel: function (id, fileName) { - }, - onUpload: function (id, fileName) { - }, - onUploadChunk: function (id, fileName, chunkData) { - }, - onResume: function (id, fileName, chunkData) { - }, - onProgress: function (id, fileName, loaded, total) { - }, - onError: function (id, fileName, reason) { - }, - onAutoRetry: function (id, fileName, attemptNumber) { - }, - onManualRetry: function (id, fileName) { - }, - onValidateBatch: function (fileData) { - }, - onValidate: function (fileData) { - } - }, - messages: { - typeError: "{file} has an invalid extension. Valid extension(s): {extensions}.", - sizeError: "{file} is too large, maximum file size is {sizeLimit}.", - minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.", - emptyError: "{file} is empty, please select files again without it.", - noFilesError: "No files to upload.", - onLeave: "The files are being uploaded, if you leave now the upload will be cancelled." - }, - retry: { - enableAuto: false, - maxAutoAttempts: 3, - autoAttemptDelay: 5, - preventRetryResponseProperty: 'preventRetry' - }, - classes: { - buttonHover: 'qq-upload-button-hover', - buttonFocus: 'qq-upload-button-focus' - }, - chunking: { - enabled: false, - partSize: 2000000, - paramNames: { - partIndex: 'qqpartindex', - partByteOffset: 'qqpartbyteoffset', - chunkSize: 'qqchunksize', - totalFileSize: 'qqtotalfilesize', - totalParts: 'qqtotalparts', - filename: 'qqfilename' - } - }, - resume: { - enabled: false, - id: null, - cookiesExpireIn: 7, //days - paramNames: { - resuming: "qqresume" - } - }, - formatFileName: function (fileName) { - if (fileName.length > 33) { - fileName = fileName.slice(0, 19) + '...' + fileName.slice(-14); - } - return fileName; - }, - text: { - sizeSymbols: ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'] - } - }; - - qq.extend(this._options, o, true); - this._wrapCallbacks(); - this._disposeSupport = new qq.DisposeSupport(); - - // number of files being uploaded - this._filesInProgress = []; - - this._storedFileIds = []; - - this._autoRetries = []; - this._retryTimeouts = []; - this._preventRetries = []; - - this._paramsStore = this._createParamsStore(); - this._endpointStore = this._createEndpointStore(); - - this._handler = this._createUploadHandler(); - - if (this._options.button) { - this._button = this._createUploadButton(this._options.button); - } - - this._preventLeaveInProgress(); -}; - -qq.FineUploaderBasic.prototype = { - log: function (str, level) { - if (this._options.debug && (!level || level === 'info')) { - qq.log('[FineUploader] ' + str); - } - else if (level && level !== 'info') { - qq.log('[FineUploader] ' + str, level); - - } - }, - setParams: function (params, fileId) { - /*jshint eqeqeq: true, eqnull: true*/ - if (fileId == null) { - this._options.request.params = params; - } - else { - this._paramsStore.setParams(params, fileId); - } - }, - setEndpoint: function (endpoint, fileId) { - /*jshint eqeqeq: true, eqnull: true*/ - if (fileId == null) { - this._options.request.endpoint = endpoint; - } - else { - this._endpointStore.setEndpoint(endpoint, fileId); - } - }, - getInProgress: function () { - return this._filesInProgress.length; - }, - uploadStoredFiles: function () { - "use strict"; - var idToUpload; - - while (this._storedFileIds.length) { - idToUpload = this._storedFileIds.shift(); - this._filesInProgress.push(idToUpload); - this._handler.upload(idToUpload); - } - }, - clearStoredFiles: function () { - this._storedFileIds = []; - }, - retry: function (id) { - if (this._onBeforeManualRetry(id)) { - this._handler.retry(id); - return true; - } - else { - return false; - } - }, - cancel: function (fileId) { - this._handler.cancel(fileId); - }, - reset: function () { - this.log("Resetting uploader..."); - this._handler.reset(); - this._filesInProgress = []; - this._storedFileIds = []; - this._autoRetries = []; - this._retryTimeouts = []; - this._preventRetries = []; - this._button.reset(); - this._paramsStore.reset(); - this._endpointStore.reset(); - }, - addFiles: function (filesOrInputs) { - var self = this, - verifiedFilesOrInputs = [], - index, fileOrInput; - - if (filesOrInputs) { - if (!window.FileList || !(filesOrInputs instanceof FileList)) { - filesOrInputs = [].concat(filesOrInputs); - } - - for (index = 0; index < filesOrInputs.length; index += 1) { - fileOrInput = filesOrInputs[index]; - - if (qq.isFileOrInput(fileOrInput)) { - verifiedFilesOrInputs.push(fileOrInput); - } - else { - self.log(fileOrInput + ' is not a File or INPUT element! Ignoring!', 'warn'); - } - } - - this.log('Processing ' + verifiedFilesOrInputs.length + ' files or inputs...'); - this._uploadFileList(verifiedFilesOrInputs); - } - }, - getUuid: function (fileId) { - return this._handler.getUuid(fileId); - }, - getResumableFilesData: function () { - return this._handler.getResumableFilesData(); - }, - getSize: function (fileId) { - return this._handler.getSize(fileId); - }, - getFile: function (fileId) { - return this._handler.getFile(fileId); - }, - _createUploadButton: function (element) { - var self = this; - - var button = new qq.UploadButton({ - element: element, - multiple: this._options.multiple && qq.isXhrUploadSupported(), - acceptFiles: this._options.validation.acceptFiles, - onChange: function (input) { - self._onInputChange(input); - }, - hoverClass: this._options.classes.buttonHover, - focusClass: this._options.classes.buttonFocus - }); - - this._disposeSupport.addDisposer(function () { - button.dispose(); - }); - return button; - }, - _createUploadHandler: function () { - var self = this; - - return new qq.UploadHandler({ - debug: this._options.debug, - forceMultipart: this._options.request.forceMultipart, - maxConnections: this._options.maxConnections, - customHeaders: this._options.request.customHeaders, - inputName: this._options.request.inputName, - uuidParamName: this._options.request.uuidName, - totalFileSizeParamName: this._options.request.totalFileSizeName, - demoMode: this._options.demoMode, - paramsInBody: this._options.request.paramsInBody, - paramsStore: this._paramsStore, - endpointStore: this._endpointStore, - chunking: this._options.chunking, - resume: this._options.resume, - log: function (str, level) { - self.log(str, level); - }, - onProgress: function (id, fileName, loaded, total) { - self._onProgress(id, fileName, loaded, total); - self._options.callbacks.onProgress(id, fileName, loaded, total); - }, - onComplete: function (id, fileName, result, xhr) { - self._onComplete(id, fileName, result, xhr); - self._options.callbacks.onComplete(id, fileName, result); - }, - onCancel: function (id, fileName) { - self._onCancel(id, fileName); - self._options.callbacks.onCancel(id, fileName); - }, - onUpload: function (id, fileName) { - self._onUpload(id, fileName); - self._options.callbacks.onUpload(id, fileName); - }, - onUploadChunk: function (id, fileName, chunkData) { - self._options.callbacks.onUploadChunk(id, fileName, chunkData); - }, - onResume: function (id, fileName, chunkData) { - return self._options.callbacks.onResume(id, fileName, chunkData); - }, - onAutoRetry: function (id, fileName, responseJSON, xhr) { - self._preventRetries[id] = responseJSON[self._options.retry.preventRetryResponseProperty]; - - if (self._shouldAutoRetry(id, fileName, responseJSON)) { - self._maybeParseAndSendUploadError(id, fileName, responseJSON, xhr); - self._options.callbacks.onAutoRetry(id, fileName, self._autoRetries[id] + 1); - self._onBeforeAutoRetry(id, fileName); - - self._retryTimeouts[id] = setTimeout(function () { - self._onAutoRetry(id, fileName, responseJSON) - }, self._options.retry.autoAttemptDelay * 1000); - - return true; - } - else { - return false; - } - } - }); - }, - _preventLeaveInProgress: function () { - var self = this; - - this._disposeSupport.attach(window, 'beforeunload', function (e) { - if (!self._filesInProgress.length) { - return; - } - - var e = e || window.event; - // for ie, ff - e.returnValue = self._options.messages.onLeave; - // for webkit - return self._options.messages.onLeave; - }); - }, - _onSubmit: function (id, fileName) { - if (this._options.autoUpload) { - this._filesInProgress.push(id); - } - }, - _onProgress: function (id, fileName, loaded, total) { - }, - _onComplete: function (id, fileName, result, xhr) { - this._removeFromFilesInProgress(id); - this._maybeParseAndSendUploadError(id, fileName, result, xhr); - }, - _onCancel: function (id, fileName) { - this._removeFromFilesInProgress(id); - - clearTimeout(this._retryTimeouts[id]); - - var storedFileIndex = qq.indexOf(this._storedFileIds, id); - if (!this._options.autoUpload && storedFileIndex >= 0) { - this._storedFileIds.splice(storedFileIndex, 1); - } - }, - _removeFromFilesInProgress: function (id) { - var index = qq.indexOf(this._filesInProgress, id); - if (index >= 0) { - this._filesInProgress.splice(index, 1); - } - }, - _onUpload: function (id, fileName) { - }, - _onInputChange: function (input) { - if (qq.isXhrUploadSupported()) { - this.addFiles(input.files); - } else { - this.addFiles(input); - } - this._button.reset(); - }, - _onBeforeAutoRetry: function (id, fileName) { - this.log("Waiting " + this._options.retry.autoAttemptDelay + " seconds before retrying " + fileName + "..."); - }, - _onAutoRetry: function (id, fileName, responseJSON) { - this.log("Retrying " + fileName + "..."); - this._autoRetries[id]++; - this._handler.retry(id); - }, - _shouldAutoRetry: function (id, fileName, responseJSON) { - if (!this._preventRetries[id] && this._options.retry.enableAuto) { - if (this._autoRetries[id] === undefined) { - this._autoRetries[id] = 0; - } - - return this._autoRetries[id] < this._options.retry.maxAutoAttempts - } - - return false; - }, - //return false if we should not attempt the requested retry - _onBeforeManualRetry: function (id) { - if (this._preventRetries[id]) { - this.log("Retries are forbidden for id " + id, 'warn'); - return false; - } - else if (this._handler.isValid(id)) { - var fileName = this._handler.getName(id); - - if (this._options.callbacks.onManualRetry(id, fileName) === false) { - return false; - } - - this.log("Retrying upload for '" + fileName + "' (id: " + id + ")..."); - this._filesInProgress.push(id); - return true; - } - else { - this.log("'" + id + "' is not a valid file ID", 'error'); - return false; - } - }, - _maybeParseAndSendUploadError: function (id, fileName, response, xhr) { - //assuming no one will actually set the response code to something other than 200 and still set 'success' to true - if (!response.success) { - if (xhr && xhr.status !== 200 && !response.error) { - this._options.callbacks.onError(id, fileName, "XHR returned response code " + xhr.status); - } - else { - var errorReason = response.error ? response.error : "Upload failure reason unknown"; - this._options.callbacks.onError(id, fileName, errorReason); - } - } - }, - _uploadFileList: function (files) { - var validationDescriptors, index, batchInvalid; - - validationDescriptors = this._getValidationDescriptors(files); - batchInvalid = this._options.callbacks.onValidateBatch(validationDescriptors) === false; - - if (!batchInvalid) { - if (files.length > 0) { - for (index = 0; index < files.length; index++) { - if (this._validateFile(files[index])) { - this._uploadFile(files[index]); - } else { - if (this._options.validation.stopOnFirstInvalidFile) { - return; - } - } - } - } - else { - this._error('noFilesError', ""); - } - } - }, - _uploadFile: function (fileContainer) { - var id = this._handler.add(fileContainer); - var fileName = this._handler.getName(id); - - if (this._options.callbacks.onSubmit(id, fileName) !== false) { - this._onSubmit(id, fileName); - if (this._options.autoUpload) { - this._handler.upload(id); - } - else { - this._storeFileForLater(id); - } - } - }, - _storeFileForLater: function (id) { - this._storedFileIds.push(id); - }, - _validateFile: function (file) { - var validationDescriptor, name, size; - - validationDescriptor = this._getValidationDescriptor(file); - name = validationDescriptor.name; - size = validationDescriptor.size; - - if (this._options.callbacks.onValidate(validationDescriptor) === false) { - return false; - } - - if (!this._isAllowedExtension(name)) { - this._error('typeError', name); - return false; - - } - else if (size === 0) { - this._error('emptyError', name); - return false; - - } - else if (size && this._options.validation.sizeLimit && size > this._options.validation.sizeLimit) { - this._error('sizeError', name); - return false; - - } - else if (size && size < this._options.validation.minSizeLimit) { - this._error('minSizeError', name); - return false; - } - - return true; - }, - _error: function (code, fileName) { - var message = this._options.messages[code]; - - function r(name, replacement) { - message = message.replace(name, replacement); - } - - var extensions = this._options.validation.allowedExtensions.join(', ').toLowerCase(); - - r('{file}', this._options.formatFileName(fileName)); - r('{extensions}', extensions); - r('{sizeLimit}', this._formatSize(this._options.validation.sizeLimit)); - r('{minSizeLimit}', this._formatSize(this._options.validation.minSizeLimit)); - - this._options.callbacks.onError(null, fileName, message); - - return message; - }, - _isAllowedExtension: function (fileName) { - var allowed = this._options.validation.allowedExtensions, - valid = false; - - if (!allowed.length) { - return true; - } - - qq.each(allowed, function (idx, allowedExt) { - /*jshint eqeqeq: true, eqnull: true*/ - var extRegex = new RegExp('\\.' + allowedExt + "$", 'i'); - - if (fileName.match(extRegex) != null) { - valid = true; - return false; - } - }); - - return valid; - }, - _formatSize: function (bytes) { - var i = -1; - do { - bytes = bytes / 1024; - i++; - } while (bytes > 99); - - return Math.max(bytes, 0.1).toFixed(1) + this._options.text.sizeSymbols[i]; - }, - _wrapCallbacks: function () { - var self, safeCallback; - - self = this; - - safeCallback = function (name, callback, args) { - try { - return callback.apply(self, args); - } - catch (exception) { - self.log("Caught exception in '" + name + "' callback - " + exception.message, 'error'); - } - } - - for (var prop in this._options.callbacks) { - (function () { - var callbackName, callbackFunc; - callbackName = prop; - callbackFunc = self._options.callbacks[callbackName]; - self._options.callbacks[callbackName] = function () { - return safeCallback(callbackName, callbackFunc, arguments); - } - }()); - } - }, - _parseFileName: function (file) { - var name; - - if (file.value) { - // it is a file input - // get input value and remove path to normalize - name = file.value.replace(/.*(\/|\\)/, ""); - } else { - // fix missing properties in Safari 4 and firefox 11.0a2 - name = (file.fileName !== null && file.fileName !== undefined) ? file.fileName : file.name; - } - - return name; - }, - _parseFileSize: function (file) { - var size; - - if (!file.value) { - // fix missing properties in Safari 4 and firefox 11.0a2 - size = (file.fileSize !== null && file.fileSize !== undefined) ? file.fileSize : file.size; - } - - return size; - }, - _getValidationDescriptor: function (file) { - var name, size, fileDescriptor; - - fileDescriptor = {}; - name = this._parseFileName(file); - size = this._parseFileSize(file); - - fileDescriptor.name = name; - if (size) { - fileDescriptor.size = size; - } - - return fileDescriptor; - }, - _getValidationDescriptors: function (files) { - var self = this, - fileDescriptors = []; - - qq.each(files, function (idx, file) { - fileDescriptors.push(self._getValidationDescriptor(file)); - }); - - return fileDescriptors; - }, - _createParamsStore: function () { - var paramsStore = {}, - self = this; - - return { - setParams: function (params, fileId) { - var paramsCopy = {}; - qq.extend(paramsCopy, params); - paramsStore[fileId] = paramsCopy; - }, - - getParams: function (fileId) { - /*jshint eqeqeq: true, eqnull: true*/ - var paramsCopy = {}; - - if (fileId != null && paramsStore[fileId]) { - qq.extend(paramsCopy, paramsStore[fileId]); - } - else { - qq.extend(paramsCopy, self._options.request.params); - } - - return paramsCopy; - }, - - remove: function (fileId) { - return delete paramsStore[fileId]; - }, - - reset: function () { - paramsStore = {}; - } - }; - }, - _createEndpointStore: function () { - var endpointStore = {}, - self = this; - - return { - setEndpoint: function (endpoint, fileId) { - endpointStore[fileId] = endpoint; - }, - - getEndpoint: function (fileId) { - /*jshint eqeqeq: true, eqnull: true*/ - if (fileId != null && endpointStore[fileId]) { - return endpointStore[fileId]; - } - - return self._options.request.endpoint; - }, - - remove: function (fileId) { - return delete endpointStore[fileId]; - }, - - reset: function () { - endpointStore = {}; - } - }; - } -}; -/*globals qq, document*/ -qq.DragAndDrop = function (o) { - "use strict"; - - var options, dz, dirPending, - droppedFiles = [], - droppedEntriesCount = 0, - droppedEntriesParsedCount = 0, - disposeSupport = new qq.DisposeSupport(); - - options = { - dropArea: null, - extraDropzones: [], - hideDropzones: true, - multiple: true, - classes: { - dropActive: null - }, - callbacks: { - dropProcessing: function (isProcessing, files) { - }, - error: function (code, filename) { - }, - log: function (message, level) { - } - } - }; - - qq.extend(options, o); - - function maybeUploadDroppedFiles() { - if (droppedEntriesCount === droppedEntriesParsedCount && !dirPending) { - options.callbacks.log('Grabbed ' + droppedFiles.length + " files after tree traversal."); - dz.dropDisabled(false); - options.callbacks.dropProcessing(false, droppedFiles); - } - } - - function addDroppedFile(file) { - droppedFiles.push(file); - droppedEntriesParsedCount += 1; - maybeUploadDroppedFiles(); - } - - function traverseFileTree(entry) { - var dirReader, i; - - droppedEntriesCount += 1; - - if (entry.isFile) { - entry.file(function (file) { - addDroppedFile(file); - }); - } - else if (entry.isDirectory) { - dirPending = true; - dirReader = entry.createReader(); - dirReader.readEntries(function (entries) { - droppedEntriesParsedCount += 1; - for (i = 0; i < entries.length; i += 1) { - traverseFileTree(entries[i]); - } - - dirPending = false; - - if (!entries.length) { - maybeUploadDroppedFiles(); - } - }); - } - } - - function handleDataTransfer(dataTransfer) { - var i, items, entry; - - options.callbacks.dropProcessing(true); - dz.dropDisabled(true); - - if (dataTransfer.files.length > 1 && !options.multiple) { - options.callbacks.dropProcessing(false); - options.callbacks.error('tooManyFilesError', ""); - dz.dropDisabled(false); - } - else { - droppedFiles = []; - droppedEntriesCount = 0; - droppedEntriesParsedCount = 0; - - if (qq.isFolderDropSupported(dataTransfer)) { - items = dataTransfer.items; - - for (i = 0; i < items.length; i += 1) { - entry = items[i].webkitGetAsEntry(); - if (entry) { - //due to a bug in Chrome's File System API impl - #149735 - if (entry.isFile) { - droppedFiles.push(items[i].getAsFile()); - if (i === items.length - 1) { - maybeUploadDroppedFiles(); - } - } - - else { - traverseFileTree(entry); - } - } - } - } - else { - options.callbacks.dropProcessing(false, dataTransfer.files); - dz.dropDisabled(false); - } - } - } - - function setupDropzone(dropArea) { - dz = new qq.UploadDropZone({ - element: dropArea, - onEnter: function (e) { - qq(dropArea).addClass(options.classes.dropActive); - e.stopPropagation(); - }, - onLeaveNotDescendants: function (e) { - qq(dropArea).removeClass(options.classes.dropActive); - }, - onDrop: function (e) { - if (options.hideDropzones) { - qq(dropArea).hide(); - } - qq(dropArea).removeClass(options.classes.dropActive); - - handleDataTransfer(e.dataTransfer); - } - }); - - disposeSupport.addDisposer(function () { - dz.dispose(); - }); - - if (options.hideDropzones) { - qq(dropArea).hide(); - } - } - - function isFileDrag(dragEvent) { - var fileDrag; - - qq.each(dragEvent.dataTransfer.types, function (key, val) { - if (val === 'Files') { - fileDrag = true; - return false; - } - }); - - return fileDrag; - } - - function setupDragDrop() { - if (options.dropArea) { - options.extraDropzones.push(options.dropArea); - } - - var i, dropzones = options.extraDropzones; - - for (i = 0; i < dropzones.length; i += 1) { - setupDropzone(dropzones[i]); - } - - // IE <= 9 does not support the File API used for drag+drop uploads - if (options.dropArea && (!qq.ie() || qq.ie10())) { - disposeSupport.attach(document, 'dragenter', function (e) { - if (!dz.dropDisabled() && isFileDrag(e)) { - if (qq(options.dropArea).hasClass(options.classes.dropDisabled)) { - return; - } - - options.dropArea.style.display = 'block'; - for (i = 0; i < dropzones.length; i += 1) { - dropzones[i].style.display = 'block'; - } - } - }); - } - disposeSupport.attach(document, 'dragleave', function (e) { - if (options.hideDropzones && qq.FineUploader.prototype._leaving_document_out(e)) { - for (i = 0; i < dropzones.length; i += 1) { - qq(dropzones[i]).hide(); - } - } - }); - disposeSupport.attach(document, 'drop', function (e) { - if (options.hideDropzones) { - for (i = 0; i < dropzones.length; i += 1) { - qq(dropzones[i]).hide(); - } - } - e.preventDefault(); - }); - } - - return { - setup: function () { - setupDragDrop(); - }, - - setupExtraDropzone: function (element) { - options.extraDropzones.push(element); - setupDropzone(element); - }, - - removeExtraDropzone: function (element) { - var i, dzs = options.extraDropzones; - for (i in dzs) { - if (dzs[i] === element) { - return dzs.splice(i, 1); - } - } - }, - - dispose: function () { - disposeSupport.dispose(); - dz.dispose(); - } - }; -}; - - -qq.UploadDropZone = function (o) { - "use strict"; - - var options, element, preventDrop, dropOutsideDisabled, disposeSupport = new qq.DisposeSupport(); - - options = { - element: null, - onEnter: function (e) { - }, - onLeave: function (e) { - }, - // is not fired when leaving element by hovering descendants - onLeaveNotDescendants: function (e) { - }, - onDrop: function (e) { - } - }; - - qq.extend(options, o); - element = options.element; - - function dragover_should_be_canceled() { - return qq.safari() || (qq.firefox() && qq.windows()); - } - - function disableDropOutside(e) { - // run only once for all instances - if (!dropOutsideDisabled) { - - // for these cases we need to catch onDrop to reset dropArea - if (dragover_should_be_canceled) { - disposeSupport.attach(document, 'dragover', function (e) { - e.preventDefault(); - }); - } else { - disposeSupport.attach(document, 'dragover', function (e) { - if (e.dataTransfer) { - e.dataTransfer.dropEffect = 'none'; - e.preventDefault(); - } - }); - } - - dropOutsideDisabled = true; - } - } - - function isValidFileDrag(e) { - // e.dataTransfer currently causing IE errors - // IE9 does NOT support file API, so drag-and-drop is not possible - if (qq.ie() && !qq.ie10()) { - return false; - } - - var effectTest, dt = e.dataTransfer, - // do not check dt.types.contains in webkit, because it crashes safari 4 - isSafari = qq.safari(); - - // dt.effectAllowed is none in Safari 5 - // dt.types.contains check is for firefox - effectTest = qq.ie10() ? true : dt.effectAllowed !== 'none'; - return dt && effectTest && (dt.files || (!isSafari && dt.types.contains && dt.types.contains('Files'))); - } - - function isOrSetDropDisabled(isDisabled) { - if (isDisabled !== undefined) { - preventDrop = isDisabled; - } - return preventDrop; - } - - function attachEvents() { - disposeSupport.attach(element, 'dragover', function (e) { - if (!isValidFileDrag(e)) { - return; - } - - var effect = qq.ie() ? null : e.dataTransfer.effectAllowed; - if (effect === 'move' || effect === 'linkMove') { - e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed) - } else { - e.dataTransfer.dropEffect = 'copy'; // for Chrome - } - - e.stopPropagation(); - e.preventDefault(); - }); - - disposeSupport.attach(element, 'dragenter', function (e) { - if (!isOrSetDropDisabled()) { - if (!isValidFileDrag(e)) { - return; - } - options.onEnter(e); - } - }); - - disposeSupport.attach(element, 'dragleave', function (e) { - if (!isValidFileDrag(e)) { - return; - } - - options.onLeave(e); - - var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); - // do not fire when moving a mouse over a descendant - if (qq(this).contains(relatedTarget)) { - return; - } - - options.onLeaveNotDescendants(e); - }); - - disposeSupport.attach(element, 'drop', function (e) { - if (!isOrSetDropDisabled()) { - if (!isValidFileDrag(e)) { - return; - } - - e.preventDefault(); - options.onDrop(e); - } - }); - } - - disableDropOutside(); - attachEvents(); - - return { - dropDisabled: function (isDisabled) { - return isOrSetDropDisabled(isDisabled); - }, - - dispose: function () { - disposeSupport.dispose(); - } - }; -}; -/** - * Class that creates upload widget with drag-and-drop and file list - * @inherits qq.FineUploaderBasic - */ -qq.FineUploader = function (o) { - // call parent constructor - qq.FineUploaderBasic.apply(this, arguments); - - // additional options - qq.extend(this._options, { - element: null, - listElement: null, - dragAndDrop: { - extraDropzones: [], - hideDropzones: true, - disableDefaultDropzone: false - }, - text: { - uploadButton: 'Upload a file', - cancelButton: 'Cancel', - retryButton: 'Retry', - failUpload: 'Upload failed', - dragZone: 'Drop files here to upload', - dropProcessing: 'Processing dropped files...', - formatProgress: "{percent}% of {total_size}", - waitingForResponse: "Processing..." - }, - template: '