Commit 8c448b56136e6710bdc84e140bbd4eb3150f1dfc

Authored by Administrator
1 parent ad6304d6

add yii jquery

Showing 31 changed files with 5160 additions and 40 deletions   Show diff stats
common/modules/file/FileUploadAsset.php 0 → 100755
  1 +<?php
  2 +/**
  3 + * @link http://www.yiiframework.com/
  4 + * @copyright Copyright (c) 2008 Yii Software LLC
  5 + * @license http://www.yiiframework.com/license/
  6 + */
  7 +
  8 +
  9 +namespace common\modules\file;
  10 +
  11 +use yii\helpers\Url;
  12 +use yii\web\AssetBundle;
  13 +
  14 +/**
  15 + * Asset bundle for the Twitter bootstrap javascript files.
  16 + *
  17 + * @author Qiang Xue <qiang.xue@gmail.com>
  18 + * @since 2.0
  19 + */
  20 +class FileUploadAsset extends AssetBundle
  21 +{
  22 +
  23 +
  24 + /**
  25 + * @inheritdoc
  26 + */
  27 + public function init()
  28 + {
  29 + parent::init();
  30 + $this->sourcePath = __DIR__.'/assets';
  31 + }
  32 +
  33 + public $css = [
  34 + 'css/jquery.fileupload.css'
  35 + ];
  36 +
  37 + public $js = [
  38 + 'js/vendor/jquery.ui.widget.js',
  39 + 'js/jquery.iframe-transport.js',
  40 + 'js/jquery.fileupload.js'
  41 + ];
  42 +}
... ...
common/modules/file/assets/css/jquery.fileupload-noscript.css 0 → 100644
  1 +@charset "UTF-8";
  2 +/*
  3 + * jQuery File Upload Plugin NoScript CSS
  4 + * https://github.com/blueimp/jQuery-File-Upload
  5 + *
  6 + * Copyright 2013, Sebastian Tschan
  7 + * https://blueimp.net
  8 + *
  9 + * Licensed under the MIT license:
  10 + * http://www.opensource.org/licenses/MIT
  11 + */
  12 +
  13 +.fileinput-button input {
  14 + position: static;
  15 + opacity: 1;
  16 + filter: none;
  17 + font-size: inherit;
  18 + direction: inherit;
  19 +}
  20 +.fileinput-button span {
  21 + display: none;
  22 +}
... ...
common/modules/file/assets/css/jquery.fileupload-ui-noscript.css 0 → 100644
  1 +@charset "UTF-8";
  2 +/*
  3 + * jQuery File Upload UI Plugin NoScript CSS
  4 + * https://github.com/blueimp/jQuery-File-Upload
  5 + *
  6 + * Copyright 2012, Sebastian Tschan
  7 + * https://blueimp.net
  8 + *
  9 + * Licensed under the MIT license:
  10 + * http://www.opensource.org/licenses/MIT
  11 + */
  12 +
  13 +.fileinput-button i,
  14 +.fileupload-buttonbar .delete,
  15 +.fileupload-buttonbar .toggle {
  16 + display: none;
  17 +}
... ...
common/modules/file/assets/css/jquery.fileupload-ui.css 0 → 100644
  1 +@charset "UTF-8";
  2 +/*
  3 + * jQuery File Upload UI Plugin CSS
  4 + * https://github.com/blueimp/jQuery-File-Upload
  5 + *
  6 + * Copyright 2010, Sebastian Tschan
  7 + * https://blueimp.net
  8 + *
  9 + * Licensed under the MIT license:
  10 + * http://www.opensource.org/licenses/MIT
  11 + */
  12 +
  13 +.fileupload-buttonbar .btn,
  14 +.fileupload-buttonbar .toggle {
  15 + margin-bottom: 5px;
  16 +}
  17 +.progress-animated .progress-bar,
  18 +.progress-animated .bar {
  19 + background: url("../img/progressbar.gif") !important;
  20 + filter: none;
  21 +}
  22 +.fileupload-process {
  23 + float: right;
  24 + display: none;
  25 +}
  26 +.fileupload-processing .fileupload-process,
  27 +.files .processing .preview {
  28 + display: block;
  29 + width: 32px;
  30 + height: 32px;
  31 + background: url("../img/loading.gif") center no-repeat;
  32 + background-size: contain;
  33 +}
  34 +.files audio,
  35 +.files video {
  36 + max-width: 300px;
  37 +}
  38 +
  39 +@media (max-width: 767px) {
  40 + .fileupload-buttonbar .toggle,
  41 + .files .toggle,
  42 + .files .btn span {
  43 + display: none;
  44 + }
  45 + .files .name {
  46 + width: 80px;
  47 + word-wrap: break-word;
  48 + }
  49 + .files audio,
  50 + .files video {
  51 + max-width: 80px;
  52 + }
  53 + .files img,
  54 + .files canvas {
  55 + max-width: 100%;
  56 + }
  57 +}
... ...
common/modules/file/assets/css/jquery.fileupload.css 0 → 100644
  1 +@charset "UTF-8";
  2 +/*
  3 + * jQuery File Upload Plugin CSS
  4 + * https://github.com/blueimp/jQuery-File-Upload
  5 + *
  6 + * Copyright 2013, Sebastian Tschan
  7 + * https://blueimp.net
  8 + *
  9 + * Licensed under the MIT license:
  10 + * http://www.opensource.org/licenses/MIT
  11 + */
  12 +
  13 +.fileinput-button {
  14 + position: relative;
  15 + overflow: hidden;
  16 + display: inline-block;
  17 +}
  18 +.fileinput-button input {
  19 + position: absolute;
  20 + top: 0;
  21 + right: 0;
  22 + margin: 0;
  23 + opacity: 0;
  24 + -ms-filter: 'alpha(opacity=0)';
  25 + font-size: 200px;
  26 + direction: ltr;
  27 + cursor: pointer;
  28 +}
  29 +
  30 +/* Fixes for IE < 8 */
  31 +@media screen\9 {
  32 + .fileinput-button input {
  33 + filter: alpha(opacity=0);
  34 + font-size: 100%;
  35 + height: 100%;
  36 + }
  37 +}
... ...
common/modules/file/assets/img/loading.gif 0 → 100644

3.81 KB

common/modules/file/assets/img/progressbar.gif 0 → 100644

3.25 KB

common/modules/file/assets/js/cors/jquery.postmessage-transport.js 0 → 100644
  1 +/*
  2 + * jQuery postMessage Transport Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2011, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* global define, require, window, document */
  13 +
  14 +(function (factory) {
  15 + 'use strict';
  16 + if (typeof define === 'function' && define.amd) {
  17 + // Register as an anonymous AMD module:
  18 + define(['jquery'], factory);
  19 + } else if (typeof exports === 'object') {
  20 + // Node/CommonJS:
  21 + factory(require('jquery'));
  22 + } else {
  23 + // Browser globals:
  24 + factory(window.jQuery);
  25 + }
  26 +}(function ($) {
  27 + 'use strict';
  28 +
  29 + var counter = 0,
  30 + names = [
  31 + 'accepts',
  32 + 'cache',
  33 + 'contents',
  34 + 'contentType',
  35 + 'crossDomain',
  36 + 'data',
  37 + 'dataType',
  38 + 'headers',
  39 + 'ifModified',
  40 + 'mimeType',
  41 + 'password',
  42 + 'processData',
  43 + 'timeout',
  44 + 'traditional',
  45 + 'type',
  46 + 'url',
  47 + 'username'
  48 + ],
  49 + convert = function (p) {
  50 + return p;
  51 + };
  52 +
  53 + $.ajaxSetup({
  54 + converters: {
  55 + 'postmessage text': convert,
  56 + 'postmessage json': convert,
  57 + 'postmessage html': convert
  58 + }
  59 + });
  60 +
  61 + $.ajaxTransport('postmessage', function (options) {
  62 + if (options.postMessage && window.postMessage) {
  63 + var iframe,
  64 + loc = $('<a>').prop('href', options.postMessage)[0],
  65 + target = loc.protocol + '//' + loc.host,
  66 + xhrUpload = options.xhr().upload;
  67 + return {
  68 + send: function (_, completeCallback) {
  69 + counter += 1;
  70 + var message = {
  71 + id: 'postmessage-transport-' + counter
  72 + },
  73 + eventName = 'message.' + message.id;
  74 + iframe = $(
  75 + '<iframe style="display:none;" src="' +
  76 + options.postMessage + '" name="' +
  77 + message.id + '"></iframe>'
  78 + ).bind('load', function () {
  79 + $.each(names, function (i, name) {
  80 + message[name] = options[name];
  81 + });
  82 + message.dataType = message.dataType.replace('postmessage ', '');
  83 + $(window).bind(eventName, function (e) {
  84 + e = e.originalEvent;
  85 + var data = e.data,
  86 + ev;
  87 + if (e.origin === target && data.id === message.id) {
  88 + if (data.type === 'progress') {
  89 + ev = document.createEvent('Event');
  90 + ev.initEvent(data.type, false, true);
  91 + $.extend(ev, data);
  92 + xhrUpload.dispatchEvent(ev);
  93 + } else {
  94 + completeCallback(
  95 + data.status,
  96 + data.statusText,
  97 + {postmessage: data.result},
  98 + data.headers
  99 + );
  100 + iframe.remove();
  101 + $(window).unbind(eventName);
  102 + }
  103 + }
  104 + });
  105 + iframe[0].contentWindow.postMessage(
  106 + message,
  107 + target
  108 + );
  109 + }).appendTo(document.body);
  110 + },
  111 + abort: function () {
  112 + if (iframe) {
  113 + iframe.remove();
  114 + }
  115 + }
  116 + };
  117 + }
  118 + });
  119 +
  120 +}));
... ...
common/modules/file/assets/js/cors/jquery.xdr-transport.js 0 → 100644
  1 +/*
  2 + * jQuery XDomainRequest Transport Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2011, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + *
  11 + * Based on Julian Aubourg's ajaxHooks xdr.js:
  12 + * https://github.com/jaubourg/ajaxHooks/
  13 + */
  14 +
  15 +/* global define, require, window, XDomainRequest */
  16 +
  17 +(function (factory) {
  18 + 'use strict';
  19 + if (typeof define === 'function' && define.amd) {
  20 + // Register as an anonymous AMD module:
  21 + define(['jquery'], factory);
  22 + } else if (typeof exports === 'object') {
  23 + // Node/CommonJS:
  24 + factory(require('jquery'));
  25 + } else {
  26 + // Browser globals:
  27 + factory(window.jQuery);
  28 + }
  29 +}(function ($) {
  30 + 'use strict';
  31 + if (window.XDomainRequest && !$.support.cors) {
  32 + $.ajaxTransport(function (s) {
  33 + if (s.crossDomain && s.async) {
  34 + if (s.timeout) {
  35 + s.xdrTimeout = s.timeout;
  36 + delete s.timeout;
  37 + }
  38 + var xdr;
  39 + return {
  40 + send: function (headers, completeCallback) {
  41 + var addParamChar = /\?/.test(s.url) ? '&' : '?';
  42 + function callback(status, statusText, responses, responseHeaders) {
  43 + xdr.onload = xdr.onerror = xdr.ontimeout = $.noop;
  44 + xdr = null;
  45 + completeCallback(status, statusText, responses, responseHeaders);
  46 + }
  47 + xdr = new XDomainRequest();
  48 + // XDomainRequest only supports GET and POST:
  49 + if (s.type === 'DELETE') {
  50 + s.url = s.url + addParamChar + '_method=DELETE';
  51 + s.type = 'POST';
  52 + } else if (s.type === 'PUT') {
  53 + s.url = s.url + addParamChar + '_method=PUT';
  54 + s.type = 'POST';
  55 + } else if (s.type === 'PATCH') {
  56 + s.url = s.url + addParamChar + '_method=PATCH';
  57 + s.type = 'POST';
  58 + }
  59 + xdr.open(s.type, s.url);
  60 + xdr.onload = function () {
  61 + callback(
  62 + 200,
  63 + 'OK',
  64 + {text: xdr.responseText},
  65 + 'Content-Type: ' + xdr.contentType
  66 + );
  67 + };
  68 + xdr.onerror = function () {
  69 + callback(404, 'Not Found');
  70 + };
  71 + if (s.xdrTimeout) {
  72 + xdr.ontimeout = function () {
  73 + callback(0, 'timeout');
  74 + };
  75 + xdr.timeout = s.xdrTimeout;
  76 + }
  77 + xdr.send((s.hasContent && s.data) || null);
  78 + },
  79 + abort: function () {
  80 + if (xdr) {
  81 + xdr.onerror = $.noop();
  82 + xdr.abort();
  83 + }
  84 + }
  85 + };
  86 + }
  87 + });
  88 + }
  89 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-angular.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload AngularJS Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, angular */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'angular',
  22 + './jquery.fileupload-image',
  23 + './jquery.fileupload-audio',
  24 + './jquery.fileupload-video',
  25 + './jquery.fileupload-validate'
  26 + ], factory);
  27 + } else {
  28 + factory();
  29 + }
  30 +}(function () {
  31 + 'use strict';
  32 +
  33 + angular.module('blueimp.fileupload', [])
  34 +
  35 + // The fileUpload service provides configuration options
  36 + // for the fileUpload directive and default handlers for
  37 + // File Upload events:
  38 + .provider('fileUpload', function () {
  39 + var scopeEvalAsync = function (expression) {
  40 + var scope = angular.element(this)
  41 + .fileupload('option', 'scope');
  42 + // Schedule a new $digest cycle if not already inside of one
  43 + // and evaluate the given expression:
  44 + scope.$evalAsync(expression);
  45 + },
  46 + addFileMethods = function (scope, data) {
  47 + var files = data.files,
  48 + file = files[0];
  49 + angular.forEach(files, function (file, index) {
  50 + file._index = index;
  51 + file.$state = function () {
  52 + return data.state();
  53 + };
  54 + file.$processing = function () {
  55 + return data.processing();
  56 + };
  57 + file.$progress = function () {
  58 + return data.progress();
  59 + };
  60 + file.$response = function () {
  61 + return data.response();
  62 + };
  63 + });
  64 + file.$submit = function () {
  65 + if (!file.error) {
  66 + return data.submit();
  67 + }
  68 + };
  69 + file.$cancel = function () {
  70 + return data.abort();
  71 + };
  72 + },
  73 + $config;
  74 + $config = this.defaults = {
  75 + handleResponse: function (e, data) {
  76 + var files = data.result && data.result.files;
  77 + if (files) {
  78 + data.scope.replace(data.files, files);
  79 + } else if (data.errorThrown ||
  80 + data.textStatus === 'error') {
  81 + data.files[0].error = data.errorThrown ||
  82 + data.textStatus;
  83 + }
  84 + },
  85 + add: function (e, data) {
  86 + if (e.isDefaultPrevented()) {
  87 + return false;
  88 + }
  89 + var scope = data.scope,
  90 + filesCopy = [];
  91 + angular.forEach(data.files, function (file) {
  92 + filesCopy.push(file);
  93 + });
  94 + scope.$parent.$applyAsync(function () {
  95 + addFileMethods(scope, data);
  96 + var method = scope.option('prependFiles') ?
  97 + 'unshift' : 'push';
  98 + Array.prototype[method].apply(scope.queue, data.files);
  99 + });
  100 + data.process(function () {
  101 + return scope.process(data);
  102 + }).always(function () {
  103 + scope.$parent.$applyAsync(function () {
  104 + addFileMethods(scope, data);
  105 + scope.replace(filesCopy, data.files);
  106 + });
  107 + }).then(function () {
  108 + if ((scope.option('autoUpload') ||
  109 + data.autoUpload) &&
  110 + data.autoUpload !== false) {
  111 + data.submit();
  112 + }
  113 + });
  114 + },
  115 + done: function (e, data) {
  116 + if (e.isDefaultPrevented()) {
  117 + return false;
  118 + }
  119 + var that = this;
  120 + data.scope.$apply(function () {
  121 + data.handleResponse.call(that, e, data);
  122 + });
  123 + },
  124 + fail: function (e, data) {
  125 + if (e.isDefaultPrevented()) {
  126 + return false;
  127 + }
  128 + var that = this,
  129 + scope = data.scope;
  130 + if (data.errorThrown === 'abort') {
  131 + scope.clear(data.files);
  132 + return;
  133 + }
  134 + scope.$apply(function () {
  135 + data.handleResponse.call(that, e, data);
  136 + });
  137 + },
  138 + stop: scopeEvalAsync,
  139 + processstart: scopeEvalAsync,
  140 + processstop: scopeEvalAsync,
  141 + getNumberOfFiles: function () {
  142 + var scope = this.scope;
  143 + return scope.queue.length - scope.processing();
  144 + },
  145 + dataType: 'json',
  146 + autoUpload: false
  147 + };
  148 + this.$get = [
  149 + function () {
  150 + return {
  151 + defaults: $config
  152 + };
  153 + }
  154 + ];
  155 + })
  156 +
  157 + // Format byte numbers to readable presentations:
  158 + .provider('formatFileSizeFilter', function () {
  159 + var $config = {
  160 + // Byte units following the IEC format
  161 + // http://en.wikipedia.org/wiki/Kilobyte
  162 + units: [
  163 + {size: 1000000000, suffix: ' GB'},
  164 + {size: 1000000, suffix: ' MB'},
  165 + {size: 1000, suffix: ' KB'}
  166 + ]
  167 + };
  168 + this.defaults = $config;
  169 + this.$get = function () {
  170 + return function (bytes) {
  171 + if (!angular.isNumber(bytes)) {
  172 + return '';
  173 + }
  174 + var unit = true,
  175 + i = 0,
  176 + prefix,
  177 + suffix;
  178 + while (unit) {
  179 + unit = $config.units[i];
  180 + prefix = unit.prefix || '';
  181 + suffix = unit.suffix || '';
  182 + if (i === $config.units.length - 1 || bytes >= unit.size) {
  183 + return prefix + (bytes / unit.size).toFixed(2) + suffix;
  184 + }
  185 + i += 1;
  186 + }
  187 + };
  188 + };
  189 + })
  190 +
  191 + // The FileUploadController initializes the fileupload widget and
  192 + // provides scope methods to control the File Upload functionality:
  193 + .controller('FileUploadController', [
  194 + '$scope', '$element', '$attrs', '$window', 'fileUpload',
  195 + function ($scope, $element, $attrs, $window, fileUpload) {
  196 + var uploadMethods = {
  197 + progress: function () {
  198 + return $element.fileupload('progress');
  199 + },
  200 + active: function () {
  201 + return $element.fileupload('active');
  202 + },
  203 + option: function (option, data) {
  204 + if (arguments.length === 1) {
  205 + return $element.fileupload('option', option);
  206 + }
  207 + $element.fileupload('option', option, data);
  208 + },
  209 + add: function (data) {
  210 + return $element.fileupload('add', data);
  211 + },
  212 + send: function (data) {
  213 + return $element.fileupload('send', data);
  214 + },
  215 + process: function (data) {
  216 + return $element.fileupload('process', data);
  217 + },
  218 + processing: function (data) {
  219 + return $element.fileupload('processing', data);
  220 + }
  221 + };
  222 + $scope.disabled = !$window.jQuery.support.fileInput;
  223 + $scope.queue = $scope.queue || [];
  224 + $scope.clear = function (files) {
  225 + var queue = this.queue,
  226 + i = queue.length,
  227 + file = files,
  228 + length = 1;
  229 + if (angular.isArray(files)) {
  230 + file = files[0];
  231 + length = files.length;
  232 + }
  233 + while (i) {
  234 + i -= 1;
  235 + if (queue[i] === file) {
  236 + return queue.splice(i, length);
  237 + }
  238 + }
  239 + };
  240 + $scope.replace = function (oldFiles, newFiles) {
  241 + var queue = this.queue,
  242 + file = oldFiles[0],
  243 + i,
  244 + j;
  245 + for (i = 0; i < queue.length; i += 1) {
  246 + if (queue[i] === file) {
  247 + for (j = 0; j < newFiles.length; j += 1) {
  248 + queue[i + j] = newFiles[j];
  249 + }
  250 + return;
  251 + }
  252 + }
  253 + };
  254 + $scope.applyOnQueue = function (method) {
  255 + var list = this.queue.slice(0),
  256 + i,
  257 + file;
  258 + for (i = 0; i < list.length; i += 1) {
  259 + file = list[i];
  260 + if (file[method]) {
  261 + file[method]();
  262 + }
  263 + }
  264 + };
  265 + $scope.submit = function () {
  266 + this.applyOnQueue('$submit');
  267 + };
  268 + $scope.cancel = function () {
  269 + this.applyOnQueue('$cancel');
  270 + };
  271 + // Add upload methods to the scope:
  272 + angular.extend($scope, uploadMethods);
  273 + // The fileupload widget will initialize with
  274 + // the options provided via "data-"-parameters,
  275 + // as well as those given via options object:
  276 + $element.fileupload(angular.extend(
  277 + {scope: $scope},
  278 + fileUpload.defaults
  279 + )).on('fileuploadadd', function (e, data) {
  280 + data.scope = $scope;
  281 + }).on('fileuploadfail', function (e, data) {
  282 + if (data.errorThrown === 'abort') {
  283 + return;
  284 + }
  285 + if (data.dataType &&
  286 + data.dataType.indexOf('json') === data.dataType.length - 4) {
  287 + try {
  288 + data.result = angular.fromJson(data.jqXHR.responseText);
  289 + } catch (ignore) {}
  290 + }
  291 + }).on([
  292 + 'fileuploadadd',
  293 + 'fileuploadsubmit',
  294 + 'fileuploadsend',
  295 + 'fileuploaddone',
  296 + 'fileuploadfail',
  297 + 'fileuploadalways',
  298 + 'fileuploadprogress',
  299 + 'fileuploadprogressall',
  300 + 'fileuploadstart',
  301 + 'fileuploadstop',
  302 + 'fileuploadchange',
  303 + 'fileuploadpaste',
  304 + 'fileuploaddrop',
  305 + 'fileuploaddragover',
  306 + 'fileuploadchunksend',
  307 + 'fileuploadchunkdone',
  308 + 'fileuploadchunkfail',
  309 + 'fileuploadchunkalways',
  310 + 'fileuploadprocessstart',
  311 + 'fileuploadprocess',
  312 + 'fileuploadprocessdone',
  313 + 'fileuploadprocessfail',
  314 + 'fileuploadprocessalways',
  315 + 'fileuploadprocessstop'
  316 + ].join(' '), function (e, data) {
  317 + $scope.$parent.$applyAsync(function () {
  318 + if ($scope.$emit(e.type, data).defaultPrevented) {
  319 + e.preventDefault();
  320 + }
  321 + });
  322 + }).on('remove', function () {
  323 + // Remove upload methods from the scope,
  324 + // when the widget is removed:
  325 + var method;
  326 + for (method in uploadMethods) {
  327 + if (uploadMethods.hasOwnProperty(method)) {
  328 + delete $scope[method];
  329 + }
  330 + }
  331 + });
  332 + // Observe option changes:
  333 + $scope.$watch(
  334 + $attrs.fileUpload,
  335 + function (newOptions) {
  336 + if (newOptions) {
  337 + $element.fileupload('option', newOptions);
  338 + }
  339 + }
  340 + );
  341 + }
  342 + ])
  343 +
  344 + // Provide File Upload progress feedback:
  345 + .controller('FileUploadProgressController', [
  346 + '$scope', '$attrs', '$parse',
  347 + function ($scope, $attrs, $parse) {
  348 + var fn = $parse($attrs.fileUploadProgress),
  349 + update = function () {
  350 + var progress = fn($scope);
  351 + if (!progress || !progress.total) {
  352 + return;
  353 + }
  354 + $scope.num = Math.floor(
  355 + progress.loaded / progress.total * 100
  356 + );
  357 + };
  358 + update();
  359 + $scope.$watch(
  360 + $attrs.fileUploadProgress + '.loaded',
  361 + function (newValue, oldValue) {
  362 + if (newValue !== oldValue) {
  363 + update();
  364 + }
  365 + }
  366 + );
  367 + }
  368 + ])
  369 +
  370 + // Display File Upload previews:
  371 + .controller('FileUploadPreviewController', [
  372 + '$scope', '$element', '$attrs',
  373 + function ($scope, $element, $attrs) {
  374 + $scope.$watch(
  375 + $attrs.fileUploadPreview + '.preview',
  376 + function (preview) {
  377 + $element.empty();
  378 + if (preview) {
  379 + $element.append(preview);
  380 + }
  381 + }
  382 + );
  383 + }
  384 + ])
  385 +
  386 + .directive('fileUpload', function () {
  387 + return {
  388 + controller: 'FileUploadController',
  389 + scope: true
  390 + };
  391 + })
  392 +
  393 + .directive('fileUploadProgress', function () {
  394 + return {
  395 + controller: 'FileUploadProgressController',
  396 + scope: true
  397 + };
  398 + })
  399 +
  400 + .directive('fileUploadPreview', function () {
  401 + return {
  402 + controller: 'FileUploadPreviewController'
  403 + };
  404 + })
  405 +
  406 + // Enhance the HTML5 download attribute to
  407 + // allow drag&drop of files to the desktop:
  408 + .directive('download', function () {
  409 + return function (scope, elm) {
  410 + elm.on('dragstart', function (e) {
  411 + try {
  412 + e.originalEvent.dataTransfer.setData(
  413 + 'DownloadURL',
  414 + [
  415 + 'application/octet-stream',
  416 + elm.prop('download'),
  417 + elm.prop('href')
  418 + ].join(':')
  419 + );
  420 + } catch (ignore) {}
  421 + });
  422 + };
  423 + });
  424 +
  425 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-audio.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Audio Preview Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window, document */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'load-image',
  22 + './jquery.fileupload-process'
  23 + ], factory);
  24 + } else if (typeof exports === 'object') {
  25 + // Node/CommonJS:
  26 + factory(
  27 + require('jquery'),
  28 + require('load-image')
  29 + );
  30 + } else {
  31 + // Browser globals:
  32 + factory(
  33 + window.jQuery,
  34 + window.loadImage
  35 + );
  36 + }
  37 +}(function ($, loadImage) {
  38 + 'use strict';
  39 +
  40 + // Prepend to the default processQueue:
  41 + $.blueimp.fileupload.prototype.options.processQueue.unshift(
  42 + {
  43 + action: 'loadAudio',
  44 + // Use the action as prefix for the "@" options:
  45 + prefix: true,
  46 + fileTypes: '@',
  47 + maxFileSize: '@',
  48 + disabled: '@disableAudioPreview'
  49 + },
  50 + {
  51 + action: 'setAudio',
  52 + name: '@audioPreviewName',
  53 + disabled: '@disableAudioPreview'
  54 + }
  55 + );
  56 +
  57 + // The File Upload Audio Preview plugin extends the fileupload widget
  58 + // with audio preview functionality:
  59 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  60 +
  61 + options: {
  62 + // The regular expression for the types of audio files to load,
  63 + // matched against the file type:
  64 + loadAudioFileTypes: /^audio\/.*$/
  65 + },
  66 +
  67 + _audioElement: document.createElement('audio'),
  68 +
  69 + processActions: {
  70 +
  71 + // Loads the audio file given via data.files and data.index
  72 + // as audio element if the browser supports playing it.
  73 + // Accepts the options fileTypes (regular expression)
  74 + // and maxFileSize (integer) to limit the files to load:
  75 + loadAudio: function (data, options) {
  76 + if (options.disabled) {
  77 + return data;
  78 + }
  79 + var file = data.files[data.index],
  80 + url,
  81 + audio;
  82 + if (this._audioElement.canPlayType &&
  83 + this._audioElement.canPlayType(file.type) &&
  84 + ($.type(options.maxFileSize) !== 'number' ||
  85 + file.size <= options.maxFileSize) &&
  86 + (!options.fileTypes ||
  87 + options.fileTypes.test(file.type))) {
  88 + url = loadImage.createObjectURL(file);
  89 + if (url) {
  90 + audio = this._audioElement.cloneNode(false);
  91 + audio.src = url;
  92 + audio.controls = true;
  93 + data.audio = audio;
  94 + return data;
  95 + }
  96 + }
  97 + return data;
  98 + },
  99 +
  100 + // Sets the audio element as a property of the file object:
  101 + setAudio: function (data, options) {
  102 + if (data.audio && !options.disabled) {
  103 + data.files[data.index][options.name || 'preview'] = data.audio;
  104 + }
  105 + return data;
  106 + }
  107 +
  108 + }
  109 +
  110 + });
  111 +
  112 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-image.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Image Preview & Resize Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window, Blob */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'load-image',
  22 + 'load-image-meta',
  23 + 'load-image-exif',
  24 + 'load-image-ios',
  25 + 'canvas-to-blob',
  26 + './jquery.fileupload-process'
  27 + ], factory);
  28 + } else if (typeof exports === 'object') {
  29 + // Node/CommonJS:
  30 + factory(
  31 + require('jquery'),
  32 + require('load-image')
  33 + );
  34 + } else {
  35 + // Browser globals:
  36 + factory(
  37 + window.jQuery,
  38 + window.loadImage
  39 + );
  40 + }
  41 +}(function ($, loadImage) {
  42 + 'use strict';
  43 +
  44 + // Prepend to the default processQueue:
  45 + $.blueimp.fileupload.prototype.options.processQueue.unshift(
  46 + {
  47 + action: 'loadImageMetaData',
  48 + disableImageHead: '@',
  49 + disableExif: '@',
  50 + disableExifThumbnail: '@',
  51 + disableExifSub: '@',
  52 + disableExifGps: '@',
  53 + disabled: '@disableImageMetaDataLoad'
  54 + },
  55 + {
  56 + action: 'loadImage',
  57 + // Use the action as prefix for the "@" options:
  58 + prefix: true,
  59 + fileTypes: '@',
  60 + maxFileSize: '@',
  61 + noRevoke: '@',
  62 + disabled: '@disableImageLoad'
  63 + },
  64 + {
  65 + action: 'resizeImage',
  66 + // Use "image" as prefix for the "@" options:
  67 + prefix: 'image',
  68 + maxWidth: '@',
  69 + maxHeight: '@',
  70 + minWidth: '@',
  71 + minHeight: '@',
  72 + crop: '@',
  73 + orientation: '@',
  74 + forceResize: '@',
  75 + disabled: '@disableImageResize'
  76 + },
  77 + {
  78 + action: 'saveImage',
  79 + quality: '@imageQuality',
  80 + type: '@imageType',
  81 + disabled: '@disableImageResize'
  82 + },
  83 + {
  84 + action: 'saveImageMetaData',
  85 + disabled: '@disableImageMetaDataSave'
  86 + },
  87 + {
  88 + action: 'resizeImage',
  89 + // Use "preview" as prefix for the "@" options:
  90 + prefix: 'preview',
  91 + maxWidth: '@',
  92 + maxHeight: '@',
  93 + minWidth: '@',
  94 + minHeight: '@',
  95 + crop: '@',
  96 + orientation: '@',
  97 + thumbnail: '@',
  98 + canvas: '@',
  99 + disabled: '@disableImagePreview'
  100 + },
  101 + {
  102 + action: 'setImage',
  103 + name: '@imagePreviewName',
  104 + disabled: '@disableImagePreview'
  105 + },
  106 + {
  107 + action: 'deleteImageReferences',
  108 + disabled: '@disableImageReferencesDeletion'
  109 + }
  110 + );
  111 +
  112 + // The File Upload Resize plugin extends the fileupload widget
  113 + // with image resize functionality:
  114 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  115 +
  116 + options: {
  117 + // The regular expression for the types of images to load:
  118 + // matched against the file type:
  119 + loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
  120 + // The maximum file size of images to load:
  121 + loadImageMaxFileSize: 10000000, // 10MB
  122 + // The maximum width of resized images:
  123 + imageMaxWidth: 1920,
  124 + // The maximum height of resized images:
  125 + imageMaxHeight: 1080,
  126 + // Defines the image orientation (1-8) or takes the orientation
  127 + // value from Exif data if set to true:
  128 + imageOrientation: false,
  129 + // Define if resized images should be cropped or only scaled:
  130 + imageCrop: false,
  131 + // Disable the resize image functionality by default:
  132 + disableImageResize: true,
  133 + // The maximum width of the preview images:
  134 + previewMaxWidth: 80,
  135 + // The maximum height of the preview images:
  136 + previewMaxHeight: 80,
  137 + // Defines the preview orientation (1-8) or takes the orientation
  138 + // value from Exif data if set to true:
  139 + previewOrientation: true,
  140 + // Create the preview using the Exif data thumbnail:
  141 + previewThumbnail: true,
  142 + // Define if preview images should be cropped or only scaled:
  143 + previewCrop: false,
  144 + // Define if preview images should be resized as canvas elements:
  145 + previewCanvas: true
  146 + },
  147 +
  148 + processActions: {
  149 +
  150 + // Loads the image given via data.files and data.index
  151 + // as img element, if the browser supports the File API.
  152 + // Accepts the options fileTypes (regular expression)
  153 + // and maxFileSize (integer) to limit the files to load:
  154 + loadImage: function (data, options) {
  155 + if (options.disabled) {
  156 + return data;
  157 + }
  158 + var that = this,
  159 + file = data.files[data.index],
  160 + dfd = $.Deferred();
  161 + if (($.type(options.maxFileSize) === 'number' &&
  162 + file.size > options.maxFileSize) ||
  163 + (options.fileTypes &&
  164 + !options.fileTypes.test(file.type)) ||
  165 + !loadImage(
  166 + file,
  167 + function (img) {
  168 + if (img.src) {
  169 + data.img = img;
  170 + }
  171 + dfd.resolveWith(that, [data]);
  172 + },
  173 + options
  174 + )) {
  175 + return data;
  176 + }
  177 + return dfd.promise();
  178 + },
  179 +
  180 + // Resizes the image given as data.canvas or data.img
  181 + // and updates data.canvas or data.img with the resized image.
  182 + // Also stores the resized image as preview property.
  183 + // Accepts the options maxWidth, maxHeight, minWidth,
  184 + // minHeight, canvas and crop:
  185 + resizeImage: function (data, options) {
  186 + if (options.disabled || !(data.canvas || data.img)) {
  187 + return data;
  188 + }
  189 + options = $.extend({canvas: true}, options);
  190 + var that = this,
  191 + dfd = $.Deferred(),
  192 + img = (options.canvas && data.canvas) || data.img,
  193 + resolve = function (newImg) {
  194 + if (newImg && (newImg.width !== img.width ||
  195 + newImg.height !== img.height ||
  196 + options.forceResize)) {
  197 + data[newImg.getContext ? 'canvas' : 'img'] = newImg;
  198 + }
  199 + data.preview = newImg;
  200 + dfd.resolveWith(that, [data]);
  201 + },
  202 + thumbnail;
  203 + if (data.exif) {
  204 + if (options.orientation === true) {
  205 + options.orientation = data.exif.get('Orientation');
  206 + }
  207 + if (options.thumbnail) {
  208 + thumbnail = data.exif.get('Thumbnail');
  209 + if (thumbnail) {
  210 + loadImage(thumbnail, resolve, options);
  211 + return dfd.promise();
  212 + }
  213 + }
  214 + // Prevent orienting the same image twice:
  215 + if (data.orientation) {
  216 + delete options.orientation;
  217 + } else {
  218 + data.orientation = options.orientation;
  219 + }
  220 + }
  221 + if (img) {
  222 + resolve(loadImage.scale(img, options));
  223 + return dfd.promise();
  224 + }
  225 + return data;
  226 + },
  227 +
  228 + // Saves the processed image given as data.canvas
  229 + // inplace at data.index of data.files:
  230 + saveImage: function (data, options) {
  231 + if (!data.canvas || options.disabled) {
  232 + return data;
  233 + }
  234 + var that = this,
  235 + file = data.files[data.index],
  236 + dfd = $.Deferred();
  237 + if (data.canvas.toBlob) {
  238 + data.canvas.toBlob(
  239 + function (blob) {
  240 + if (!blob.name) {
  241 + if (file.type === blob.type) {
  242 + blob.name = file.name;
  243 + } else if (file.name) {
  244 + blob.name = file.name.replace(
  245 + /\.\w+$/,
  246 + '.' + blob.type.substr(6)
  247 + );
  248 + }
  249 + }
  250 + // Don't restore invalid meta data:
  251 + if (file.type !== blob.type) {
  252 + delete data.imageHead;
  253 + }
  254 + // Store the created blob at the position
  255 + // of the original file in the files list:
  256 + data.files[data.index] = blob;
  257 + dfd.resolveWith(that, [data]);
  258 + },
  259 + options.type || file.type,
  260 + options.quality
  261 + );
  262 + } else {
  263 + return data;
  264 + }
  265 + return dfd.promise();
  266 + },
  267 +
  268 + loadImageMetaData: function (data, options) {
  269 + if (options.disabled) {
  270 + return data;
  271 + }
  272 + var that = this,
  273 + dfd = $.Deferred();
  274 + loadImage.parseMetaData(data.files[data.index], function (result) {
  275 + $.extend(data, result);
  276 + dfd.resolveWith(that, [data]);
  277 + }, options);
  278 + return dfd.promise();
  279 + },
  280 +
  281 + saveImageMetaData: function (data, options) {
  282 + if (!(data.imageHead && data.canvas &&
  283 + data.canvas.toBlob && !options.disabled)) {
  284 + return data;
  285 + }
  286 + var file = data.files[data.index],
  287 + blob = new Blob([
  288 + data.imageHead,
  289 + // Resized images always have a head size of 20 bytes,
  290 + // including the JPEG marker and a minimal JFIF header:
  291 + this._blobSlice.call(file, 20)
  292 + ], {type: file.type});
  293 + blob.name = file.name;
  294 + data.files[data.index] = blob;
  295 + return data;
  296 + },
  297 +
  298 + // Sets the resized version of the image as a property of the
  299 + // file object, must be called after "saveImage":
  300 + setImage: function (data, options) {
  301 + if (data.preview && !options.disabled) {
  302 + data.files[data.index][options.name || 'preview'] = data.preview;
  303 + }
  304 + return data;
  305 + },
  306 +
  307 + deleteImageReferences: function (data, options) {
  308 + if (!options.disabled) {
  309 + delete data.img;
  310 + delete data.canvas;
  311 + delete data.preview;
  312 + delete data.imageHead;
  313 + }
  314 + return data;
  315 + }
  316 +
  317 + }
  318 +
  319 + });
  320 +
  321 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-jquery-ui.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload jQuery UI Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define(['jquery', './jquery.fileupload-ui'], factory);
  20 + } else if (typeof exports === 'object') {
  21 + // Node/CommonJS:
  22 + factory(require('jquery'));
  23 + } else {
  24 + // Browser globals:
  25 + factory(window.jQuery);
  26 + }
  27 +}(function ($) {
  28 + 'use strict';
  29 +
  30 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  31 +
  32 + options: {
  33 + processdone: function (e, data) {
  34 + data.context.find('.start').button('enable');
  35 + },
  36 + progress: function (e, data) {
  37 + if (data.context) {
  38 + data.context.find('.progress').progressbar(
  39 + 'option',
  40 + 'value',
  41 + parseInt(data.loaded / data.total * 100, 10)
  42 + );
  43 + }
  44 + },
  45 + progressall: function (e, data) {
  46 + var $this = $(this);
  47 + $this.find('.fileupload-progress')
  48 + .find('.progress').progressbar(
  49 + 'option',
  50 + 'value',
  51 + parseInt(data.loaded / data.total * 100, 10)
  52 + ).end()
  53 + .find('.progress-extended').each(function () {
  54 + $(this).html(
  55 + ($this.data('blueimp-fileupload') ||
  56 + $this.data('fileupload'))
  57 + ._renderExtendedProgress(data)
  58 + );
  59 + });
  60 + }
  61 + },
  62 +
  63 + _renderUpload: function (func, files) {
  64 + var node = this._super(func, files),
  65 + showIconText = $(window).width() > 480;
  66 + node.find('.progress').empty().progressbar();
  67 + node.find('.start').button({
  68 + icons: {primary: 'ui-icon-circle-arrow-e'},
  69 + text: showIconText
  70 + });
  71 + node.find('.cancel').button({
  72 + icons: {primary: 'ui-icon-cancel'},
  73 + text: showIconText
  74 + });
  75 + if (node.hasClass('fade')) {
  76 + node.hide();
  77 + }
  78 + return node;
  79 + },
  80 +
  81 + _renderDownload: function (func, files) {
  82 + var node = this._super(func, files),
  83 + showIconText = $(window).width() > 480;
  84 + node.find('.delete').button({
  85 + icons: {primary: 'ui-icon-trash'},
  86 + text: showIconText
  87 + });
  88 + if (node.hasClass('fade')) {
  89 + node.hide();
  90 + }
  91 + return node;
  92 + },
  93 +
  94 + _startHandler: function (e) {
  95 + $(e.currentTarget).button('disable');
  96 + this._super(e);
  97 + },
  98 +
  99 + _transition: function (node) {
  100 + var deferred = $.Deferred();
  101 + if (node.hasClass('fade')) {
  102 + node.fadeToggle(
  103 + this.options.transitionDuration,
  104 + this.options.transitionEasing,
  105 + function () {
  106 + deferred.resolveWith(node);
  107 + }
  108 + );
  109 + } else {
  110 + deferred.resolveWith(node);
  111 + }
  112 + return deferred;
  113 + },
  114 +
  115 + _create: function () {
  116 + this._super();
  117 + this.element
  118 + .find('.fileupload-buttonbar')
  119 + .find('.fileinput-button').each(function () {
  120 + var input = $(this).find('input:file').detach();
  121 + $(this)
  122 + .button({icons: {primary: 'ui-icon-plusthick'}})
  123 + .append(input);
  124 + })
  125 + .end().find('.start')
  126 + .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
  127 + .end().find('.cancel')
  128 + .button({icons: {primary: 'ui-icon-cancel'}})
  129 + .end().find('.delete')
  130 + .button({icons: {primary: 'ui-icon-trash'}})
  131 + .end().find('.progress').progressbar();
  132 + },
  133 +
  134 + _destroy: function () {
  135 + this.element
  136 + .find('.fileupload-buttonbar')
  137 + .find('.fileinput-button').each(function () {
  138 + var input = $(this).find('input:file').detach();
  139 + $(this)
  140 + .button('destroy')
  141 + .append(input);
  142 + })
  143 + .end().find('.start')
  144 + .button('destroy')
  145 + .end().find('.cancel')
  146 + .button('destroy')
  147 + .end().find('.delete')
  148 + .button('destroy')
  149 + .end().find('.progress').progressbar('destroy');
  150 + this._super();
  151 + }
  152 +
  153 + });
  154 +
  155 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-process.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Processing Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2012, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + './jquery.fileupload'
  22 + ], factory);
  23 + } else if (typeof exports === 'object') {
  24 + // Node/CommonJS:
  25 + factory(require('jquery'));
  26 + } else {
  27 + // Browser globals:
  28 + factory(
  29 + window.jQuery
  30 + );
  31 + }
  32 +}(function ($) {
  33 + 'use strict';
  34 +
  35 + var originalAdd = $.blueimp.fileupload.prototype.options.add;
  36 +
  37 + // The File Upload Processing plugin extends the fileupload widget
  38 + // with file processing functionality:
  39 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  40 +
  41 + options: {
  42 + // The list of processing actions:
  43 + processQueue: [
  44 + /*
  45 + {
  46 + action: 'log',
  47 + type: 'debug'
  48 + }
  49 + */
  50 + ],
  51 + add: function (e, data) {
  52 + var $this = $(this);
  53 + data.process(function () {
  54 + return $this.fileupload('process', data);
  55 + });
  56 + originalAdd.call(this, e, data);
  57 + }
  58 + },
  59 +
  60 + processActions: {
  61 + /*
  62 + log: function (data, options) {
  63 + console[options.type](
  64 + 'Processing "' + data.files[data.index].name + '"'
  65 + );
  66 + }
  67 + */
  68 + },
  69 +
  70 + _processFile: function (data, originalData) {
  71 + var that = this,
  72 + dfd = $.Deferred().resolveWith(that, [data]),
  73 + chain = dfd.promise();
  74 + this._trigger('process', null, data);
  75 + $.each(data.processQueue, function (i, settings) {
  76 + var func = function (data) {
  77 + if (originalData.errorThrown) {
  78 + return $.Deferred()
  79 + .rejectWith(that, [originalData]).promise();
  80 + }
  81 + return that.processActions[settings.action].call(
  82 + that,
  83 + data,
  84 + settings
  85 + );
  86 + };
  87 + chain = chain.pipe(func, settings.always && func);
  88 + });
  89 + chain
  90 + .done(function () {
  91 + that._trigger('processdone', null, data);
  92 + that._trigger('processalways', null, data);
  93 + })
  94 + .fail(function () {
  95 + that._trigger('processfail', null, data);
  96 + that._trigger('processalways', null, data);
  97 + });
  98 + return chain;
  99 + },
  100 +
  101 + // Replaces the settings of each processQueue item that
  102 + // are strings starting with an "@", using the remaining
  103 + // substring as key for the option map,
  104 + // e.g. "@autoUpload" is replaced with options.autoUpload:
  105 + _transformProcessQueue: function (options) {
  106 + var processQueue = [];
  107 + $.each(options.processQueue, function () {
  108 + var settings = {},
  109 + action = this.action,
  110 + prefix = this.prefix === true ? action : this.prefix;
  111 + $.each(this, function (key, value) {
  112 + if ($.type(value) === 'string' &&
  113 + value.charAt(0) === '@') {
  114 + settings[key] = options[
  115 + value.slice(1) || (prefix ? prefix +
  116 + key.charAt(0).toUpperCase() + key.slice(1) : key)
  117 + ];
  118 + } else {
  119 + settings[key] = value;
  120 + }
  121 +
  122 + });
  123 + processQueue.push(settings);
  124 + });
  125 + options.processQueue = processQueue;
  126 + },
  127 +
  128 + // Returns the number of files currently in the processsing queue:
  129 + processing: function () {
  130 + return this._processing;
  131 + },
  132 +
  133 + // Processes the files given as files property of the data parameter,
  134 + // returns a Promise object that allows to bind callbacks:
  135 + process: function (data) {
  136 + var that = this,
  137 + options = $.extend({}, this.options, data);
  138 + if (options.processQueue && options.processQueue.length) {
  139 + this._transformProcessQueue(options);
  140 + if (this._processing === 0) {
  141 + this._trigger('processstart');
  142 + }
  143 + $.each(data.files, function (index) {
  144 + var opts = index ? $.extend({}, options) : options,
  145 + func = function () {
  146 + if (data.errorThrown) {
  147 + return $.Deferred()
  148 + .rejectWith(that, [data]).promise();
  149 + }
  150 + return that._processFile(opts, data);
  151 + };
  152 + opts.index = index;
  153 + that._processing += 1;
  154 + that._processingQueue = that._processingQueue.pipe(func, func)
  155 + .always(function () {
  156 + that._processing -= 1;
  157 + if (that._processing === 0) {
  158 + that._trigger('processstop');
  159 + }
  160 + });
  161 + });
  162 + }
  163 + return this._processingQueue;
  164 + },
  165 +
  166 + _create: function () {
  167 + this._super();
  168 + this._processing = 0;
  169 + this._processingQueue = $.Deferred().resolveWith(this)
  170 + .promise();
  171 + }
  172 +
  173 + });
  174 +
  175 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-ui.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload User Interface Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2010, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'tmpl',
  22 + './jquery.fileupload-image',
  23 + './jquery.fileupload-audio',
  24 + './jquery.fileupload-video',
  25 + './jquery.fileupload-validate'
  26 + ], factory);
  27 + } else if (typeof exports === 'object') {
  28 + // Node/CommonJS:
  29 + factory(
  30 + require('jquery'),
  31 + require('tmpl')
  32 + );
  33 + } else {
  34 + // Browser globals:
  35 + factory(
  36 + window.jQuery,
  37 + window.tmpl
  38 + );
  39 + }
  40 +}(function ($, tmpl) {
  41 + 'use strict';
  42 +
  43 + $.blueimp.fileupload.prototype._specialOptions.push(
  44 + 'filesContainer',
  45 + 'uploadTemplateId',
  46 + 'downloadTemplateId'
  47 + );
  48 +
  49 + // The UI version extends the file upload widget
  50 + // and adds complete user interface interaction:
  51 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  52 +
  53 + options: {
  54 + // By default, files added to the widget are uploaded as soon
  55 + // as the user clicks on the start buttons. To enable automatic
  56 + // uploads, set the following option to true:
  57 + autoUpload: false,
  58 + // The ID of the upload template:
  59 + uploadTemplateId: 'template-upload',
  60 + // The ID of the download template:
  61 + downloadTemplateId: 'template-download',
  62 + // The container for the list of files. If undefined, it is set to
  63 + // an element with class "files" inside of the widget element:
  64 + filesContainer: undefined,
  65 + // By default, files are appended to the files container.
  66 + // Set the following option to true, to prepend files instead:
  67 + prependFiles: false,
  68 + // The expected data type of the upload response, sets the dataType
  69 + // option of the $.ajax upload requests:
  70 + dataType: 'json',
  71 +
  72 + // Error and info messages:
  73 + messages: {
  74 + unknownError: 'Unknown error'
  75 + },
  76 +
  77 + // Function returning the current number of files,
  78 + // used by the maxNumberOfFiles validation:
  79 + getNumberOfFiles: function () {
  80 + return this.filesContainer.children()
  81 + .not('.processing').length;
  82 + },
  83 +
  84 + // Callback to retrieve the list of files from the server response:
  85 + getFilesFromResponse: function (data) {
  86 + if (data.result && $.isArray(data.result.files)) {
  87 + return data.result.files;
  88 + }
  89 + return [];
  90 + },
  91 +
  92 + // The add callback is invoked as soon as files are added to the fileupload
  93 + // widget (via file input selection, drag & drop or add API call).
  94 + // See the basic file upload widget for more information:
  95 + add: function (e, data) {
  96 + if (e.isDefaultPrevented()) {
  97 + return false;
  98 + }
  99 + var $this = $(this),
  100 + that = $this.data('blueimp-fileupload') ||
  101 + $this.data('fileupload'),
  102 + options = that.options;
  103 + data.context = that._renderUpload(data.files)
  104 + .data('data', data)
  105 + .addClass('processing');
  106 + options.filesContainer[
  107 + options.prependFiles ? 'prepend' : 'append'
  108 + ](data.context);
  109 + that._forceReflow(data.context);
  110 + that._transition(data.context);
  111 + data.process(function () {
  112 + return $this.fileupload('process', data);
  113 + }).always(function () {
  114 + data.context.each(function (index) {
  115 + $(this).find('.size').text(
  116 + that._formatFileSize(data.files[index].size)
  117 + );
  118 + }).removeClass('processing');
  119 + that._renderPreviews(data);
  120 + }).done(function () {
  121 + data.context.find('.start').prop('disabled', false);
  122 + if ((that._trigger('added', e, data) !== false) &&
  123 + (options.autoUpload || data.autoUpload) &&
  124 + data.autoUpload !== false) {
  125 + data.submit();
  126 + }
  127 + }).fail(function () {
  128 + if (data.files.error) {
  129 + data.context.each(function (index) {
  130 + var error = data.files[index].error;
  131 + if (error) {
  132 + $(this).find('.error').text(error);
  133 + }
  134 + });
  135 + }
  136 + });
  137 + },
  138 + // Callback for the start of each file upload request:
  139 + send: function (e, data) {
  140 + if (e.isDefaultPrevented()) {
  141 + return false;
  142 + }
  143 + var that = $(this).data('blueimp-fileupload') ||
  144 + $(this).data('fileupload');
  145 + if (data.context && data.dataType &&
  146 + data.dataType.substr(0, 6) === 'iframe') {
  147 + // Iframe Transport does not support progress events.
  148 + // In lack of an indeterminate progress bar, we set
  149 + // the progress to 100%, showing the full animated bar:
  150 + data.context
  151 + .find('.progress').addClass(
  152 + !$.support.transition && 'progress-animated'
  153 + )
  154 + .attr('aria-valuenow', 100)
  155 + .children().first().css(
  156 + 'width',
  157 + '100%'
  158 + );
  159 + }
  160 + return that._trigger('sent', e, data);
  161 + },
  162 + // Callback for successful uploads:
  163 + done: function (e, data) {
  164 + if (e.isDefaultPrevented()) {
  165 + return false;
  166 + }
  167 + var that = $(this).data('blueimp-fileupload') ||
  168 + $(this).data('fileupload'),
  169 + getFilesFromResponse = data.getFilesFromResponse ||
  170 + that.options.getFilesFromResponse,
  171 + files = getFilesFromResponse(data),
  172 + template,
  173 + deferred;
  174 + if (data.context) {
  175 + data.context.each(function (index) {
  176 + var file = files[index] ||
  177 + {error: 'Empty file upload result'};
  178 + deferred = that._addFinishedDeferreds();
  179 + that._transition($(this)).done(
  180 + function () {
  181 + var node = $(this);
  182 + template = that._renderDownload([file])
  183 + .replaceAll(node);
  184 + that._forceReflow(template);
  185 + that._transition(template).done(
  186 + function () {
  187 + data.context = $(this);
  188 + that._trigger('completed', e, data);
  189 + that._trigger('finished', e, data);
  190 + deferred.resolve();
  191 + }
  192 + );
  193 + }
  194 + );
  195 + });
  196 + } else {
  197 + template = that._renderDownload(files)[
  198 + that.options.prependFiles ? 'prependTo' : 'appendTo'
  199 + ](that.options.filesContainer);
  200 + that._forceReflow(template);
  201 + deferred = that._addFinishedDeferreds();
  202 + that._transition(template).done(
  203 + function () {
  204 + data.context = $(this);
  205 + that._trigger('completed', e, data);
  206 + that._trigger('finished', e, data);
  207 + deferred.resolve();
  208 + }
  209 + );
  210 + }
  211 + },
  212 + // Callback for failed (abort or error) uploads:
  213 + fail: function (e, data) {
  214 + if (e.isDefaultPrevented()) {
  215 + return false;
  216 + }
  217 + var that = $(this).data('blueimp-fileupload') ||
  218 + $(this).data('fileupload'),
  219 + template,
  220 + deferred;
  221 + if (data.context) {
  222 + data.context.each(function (index) {
  223 + if (data.errorThrown !== 'abort') {
  224 + var file = data.files[index];
  225 + file.error = file.error || data.errorThrown ||
  226 + data.i18n('unknownError');
  227 + deferred = that._addFinishedDeferreds();
  228 + that._transition($(this)).done(
  229 + function () {
  230 + var node = $(this);
  231 + template = that._renderDownload([file])
  232 + .replaceAll(node);
  233 + that._forceReflow(template);
  234 + that._transition(template).done(
  235 + function () {
  236 + data.context = $(this);
  237 + that._trigger('failed', e, data);
  238 + that._trigger('finished', e, data);
  239 + deferred.resolve();
  240 + }
  241 + );
  242 + }
  243 + );
  244 + } else {
  245 + deferred = that._addFinishedDeferreds();
  246 + that._transition($(this)).done(
  247 + function () {
  248 + $(this).remove();
  249 + that._trigger('failed', e, data);
  250 + that._trigger('finished', e, data);
  251 + deferred.resolve();
  252 + }
  253 + );
  254 + }
  255 + });
  256 + } else if (data.errorThrown !== 'abort') {
  257 + data.context = that._renderUpload(data.files)[
  258 + that.options.prependFiles ? 'prependTo' : 'appendTo'
  259 + ](that.options.filesContainer)
  260 + .data('data', data);
  261 + that._forceReflow(data.context);
  262 + deferred = that._addFinishedDeferreds();
  263 + that._transition(data.context).done(
  264 + function () {
  265 + data.context = $(this);
  266 + that._trigger('failed', e, data);
  267 + that._trigger('finished', e, data);
  268 + deferred.resolve();
  269 + }
  270 + );
  271 + } else {
  272 + that._trigger('failed', e, data);
  273 + that._trigger('finished', e, data);
  274 + that._addFinishedDeferreds().resolve();
  275 + }
  276 + },
  277 + // Callback for upload progress events:
  278 + progress: function (e, data) {
  279 + if (e.isDefaultPrevented()) {
  280 + return false;
  281 + }
  282 + var progress = Math.floor(data.loaded / data.total * 100);
  283 + if (data.context) {
  284 + data.context.each(function () {
  285 + $(this).find('.progress')
  286 + .attr('aria-valuenow', progress)
  287 + .children().first().css(
  288 + 'width',
  289 + progress + '%'
  290 + );
  291 + });
  292 + }
  293 + },
  294 + // Callback for global upload progress events:
  295 + progressall: function (e, data) {
  296 + if (e.isDefaultPrevented()) {
  297 + return false;
  298 + }
  299 + var $this = $(this),
  300 + progress = Math.floor(data.loaded / data.total * 100),
  301 + globalProgressNode = $this.find('.fileupload-progress'),
  302 + extendedProgressNode = globalProgressNode
  303 + .find('.progress-extended');
  304 + if (extendedProgressNode.length) {
  305 + extendedProgressNode.html(
  306 + ($this.data('blueimp-fileupload') || $this.data('fileupload'))
  307 + ._renderExtendedProgress(data)
  308 + );
  309 + }
  310 + globalProgressNode
  311 + .find('.progress')
  312 + .attr('aria-valuenow', progress)
  313 + .children().first().css(
  314 + 'width',
  315 + progress + '%'
  316 + );
  317 + },
  318 + // Callback for uploads start, equivalent to the global ajaxStart event:
  319 + start: function (e) {
  320 + if (e.isDefaultPrevented()) {
  321 + return false;
  322 + }
  323 + var that = $(this).data('blueimp-fileupload') ||
  324 + $(this).data('fileupload');
  325 + that._resetFinishedDeferreds();
  326 + that._transition($(this).find('.fileupload-progress')).done(
  327 + function () {
  328 + that._trigger('started', e);
  329 + }
  330 + );
  331 + },
  332 + // Callback for uploads stop, equivalent to the global ajaxStop event:
  333 + stop: function (e) {
  334 + if (e.isDefaultPrevented()) {
  335 + return false;
  336 + }
  337 + var that = $(this).data('blueimp-fileupload') ||
  338 + $(this).data('fileupload'),
  339 + deferred = that._addFinishedDeferreds();
  340 + $.when.apply($, that._getFinishedDeferreds())
  341 + .done(function () {
  342 + that._trigger('stopped', e);
  343 + });
  344 + that._transition($(this).find('.fileupload-progress')).done(
  345 + function () {
  346 + $(this).find('.progress')
  347 + .attr('aria-valuenow', '0')
  348 + .children().first().css('width', '0%');
  349 + $(this).find('.progress-extended').html('&nbsp;');
  350 + deferred.resolve();
  351 + }
  352 + );
  353 + },
  354 + processstart: function (e) {
  355 + if (e.isDefaultPrevented()) {
  356 + return false;
  357 + }
  358 + $(this).addClass('fileupload-processing');
  359 + },
  360 + processstop: function (e) {
  361 + if (e.isDefaultPrevented()) {
  362 + return false;
  363 + }
  364 + $(this).removeClass('fileupload-processing');
  365 + },
  366 + // Callback for file deletion:
  367 + destroy: function (e, data) {
  368 + if (e.isDefaultPrevented()) {
  369 + return false;
  370 + }
  371 + var that = $(this).data('blueimp-fileupload') ||
  372 + $(this).data('fileupload'),
  373 + removeNode = function () {
  374 + that._transition(data.context).done(
  375 + function () {
  376 + $(this).remove();
  377 + that._trigger('destroyed', e, data);
  378 + }
  379 + );
  380 + };
  381 + if (data.url) {
  382 + data.dataType = data.dataType || that.options.dataType;
  383 + $.ajax(data).done(removeNode).fail(function () {
  384 + that._trigger('destroyfailed', e, data);
  385 + });
  386 + } else {
  387 + removeNode();
  388 + }
  389 + }
  390 + },
  391 +
  392 + _resetFinishedDeferreds: function () {
  393 + this._finishedUploads = [];
  394 + },
  395 +
  396 + _addFinishedDeferreds: function (deferred) {
  397 + if (!deferred) {
  398 + deferred = $.Deferred();
  399 + }
  400 + this._finishedUploads.push(deferred);
  401 + return deferred;
  402 + },
  403 +
  404 + _getFinishedDeferreds: function () {
  405 + return this._finishedUploads;
  406 + },
  407 +
  408 + // Link handler, that allows to download files
  409 + // by drag & drop of the links to the desktop:
  410 + _enableDragToDesktop: function () {
  411 + var link = $(this),
  412 + url = link.prop('href'),
  413 + name = link.prop('download'),
  414 + type = 'application/octet-stream';
  415 + link.bind('dragstart', function (e) {
  416 + try {
  417 + e.originalEvent.dataTransfer.setData(
  418 + 'DownloadURL',
  419 + [type, name, url].join(':')
  420 + );
  421 + } catch (ignore) {}
  422 + });
  423 + },
  424 +
  425 + _formatFileSize: function (bytes) {
  426 + if (typeof bytes !== 'number') {
  427 + return '';
  428 + }
  429 + if (bytes >= 1000000000) {
  430 + return (bytes / 1000000000).toFixed(2) + ' GB';
  431 + }
  432 + if (bytes >= 1000000) {
  433 + return (bytes / 1000000).toFixed(2) + ' MB';
  434 + }
  435 + return (bytes / 1000).toFixed(2) + ' KB';
  436 + },
  437 +
  438 + _formatBitrate: function (bits) {
  439 + if (typeof bits !== 'number') {
  440 + return '';
  441 + }
  442 + if (bits >= 1000000000) {
  443 + return (bits / 1000000000).toFixed(2) + ' Gbit/s';
  444 + }
  445 + if (bits >= 1000000) {
  446 + return (bits / 1000000).toFixed(2) + ' Mbit/s';
  447 + }
  448 + if (bits >= 1000) {
  449 + return (bits / 1000).toFixed(2) + ' kbit/s';
  450 + }
  451 + return bits.toFixed(2) + ' bit/s';
  452 + },
  453 +
  454 + _formatTime: function (seconds) {
  455 + var date = new Date(seconds * 1000),
  456 + days = Math.floor(seconds / 86400);
  457 + days = days ? days + 'd ' : '';
  458 + return days +
  459 + ('0' + date.getUTCHours()).slice(-2) + ':' +
  460 + ('0' + date.getUTCMinutes()).slice(-2) + ':' +
  461 + ('0' + date.getUTCSeconds()).slice(-2);
  462 + },
  463 +
  464 + _formatPercentage: function (floatValue) {
  465 + return (floatValue * 100).toFixed(2) + ' %';
  466 + },
  467 +
  468 + _renderExtendedProgress: function (data) {
  469 + return this._formatBitrate(data.bitrate) + ' | ' +
  470 + this._formatTime(
  471 + (data.total - data.loaded) * 8 / data.bitrate
  472 + ) + ' | ' +
  473 + this._formatPercentage(
  474 + data.loaded / data.total
  475 + ) + ' | ' +
  476 + this._formatFileSize(data.loaded) + ' / ' +
  477 + this._formatFileSize(data.total);
  478 + },
  479 +
  480 + _renderTemplate: function (func, files) {
  481 + if (!func) {
  482 + return $();
  483 + }
  484 + var result = func({
  485 + files: files,
  486 + formatFileSize: this._formatFileSize,
  487 + options: this.options
  488 + });
  489 + if (result instanceof $) {
  490 + return result;
  491 + }
  492 + return $(this.options.templatesContainer).html(result).children();
  493 + },
  494 +
  495 + _renderPreviews: function (data) {
  496 + data.context.find('.preview').each(function (index, elm) {
  497 + $(elm).append(data.files[index].preview);
  498 + });
  499 + },
  500 +
  501 + _renderUpload: function (files) {
  502 + return this._renderTemplate(
  503 + this.options.uploadTemplate,
  504 + files
  505 + );
  506 + },
  507 +
  508 + _renderDownload: function (files) {
  509 + return this._renderTemplate(
  510 + this.options.downloadTemplate,
  511 + files
  512 + ).find('a[download]').each(this._enableDragToDesktop).end();
  513 + },
  514 +
  515 + _startHandler: function (e) {
  516 + e.preventDefault();
  517 + var button = $(e.currentTarget),
  518 + template = button.closest('.template-upload'),
  519 + data = template.data('data');
  520 + button.prop('disabled', true);
  521 + if (data && data.submit) {
  522 + data.submit();
  523 + }
  524 + },
  525 +
  526 + _cancelHandler: function (e) {
  527 + e.preventDefault();
  528 + var template = $(e.currentTarget)
  529 + .closest('.template-upload,.template-download'),
  530 + data = template.data('data') || {};
  531 + data.context = data.context || template;
  532 + if (data.abort) {
  533 + data.abort();
  534 + } else {
  535 + data.errorThrown = 'abort';
  536 + this._trigger('fail', e, data);
  537 + }
  538 + },
  539 +
  540 + _deleteHandler: function (e) {
  541 + e.preventDefault();
  542 + var button = $(e.currentTarget);
  543 + this._trigger('destroy', e, $.extend({
  544 + context: button.closest('.template-download'),
  545 + type: 'DELETE'
  546 + }, button.data()));
  547 + },
  548 +
  549 + _forceReflow: function (node) {
  550 + return $.support.transition && node.length &&
  551 + node[0].offsetWidth;
  552 + },
  553 +
  554 + _transition: function (node) {
  555 + var dfd = $.Deferred();
  556 + if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
  557 + node.bind(
  558 + $.support.transition.end,
  559 + function (e) {
  560 + // Make sure we don't respond to other transitions events
  561 + // in the container element, e.g. from button elements:
  562 + if (e.target === node[0]) {
  563 + node.unbind($.support.transition.end);
  564 + dfd.resolveWith(node);
  565 + }
  566 + }
  567 + ).toggleClass('in');
  568 + } else {
  569 + node.toggleClass('in');
  570 + dfd.resolveWith(node);
  571 + }
  572 + return dfd;
  573 + },
  574 +
  575 + _initButtonBarEventHandlers: function () {
  576 + var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
  577 + filesList = this.options.filesContainer;
  578 + this._on(fileUploadButtonBar.find('.start'), {
  579 + click: function (e) {
  580 + e.preventDefault();
  581 + filesList.find('.start').click();
  582 + }
  583 + });
  584 + this._on(fileUploadButtonBar.find('.cancel'), {
  585 + click: function (e) {
  586 + e.preventDefault();
  587 + filesList.find('.cancel').click();
  588 + }
  589 + });
  590 + this._on(fileUploadButtonBar.find('.delete'), {
  591 + click: function (e) {
  592 + e.preventDefault();
  593 + filesList.find('.toggle:checked')
  594 + .closest('.template-download')
  595 + .find('.delete').click();
  596 + fileUploadButtonBar.find('.toggle')
  597 + .prop('checked', false);
  598 + }
  599 + });
  600 + this._on(fileUploadButtonBar.find('.toggle'), {
  601 + change: function (e) {
  602 + filesList.find('.toggle').prop(
  603 + 'checked',
  604 + $(e.currentTarget).is(':checked')
  605 + );
  606 + }
  607 + });
  608 + },
  609 +
  610 + _destroyButtonBarEventHandlers: function () {
  611 + this._off(
  612 + this.element.find('.fileupload-buttonbar')
  613 + .find('.start, .cancel, .delete'),
  614 + 'click'
  615 + );
  616 + this._off(
  617 + this.element.find('.fileupload-buttonbar .toggle'),
  618 + 'change.'
  619 + );
  620 + },
  621 +
  622 + _initEventHandlers: function () {
  623 + this._super();
  624 + this._on(this.options.filesContainer, {
  625 + 'click .start': this._startHandler,
  626 + 'click .cancel': this._cancelHandler,
  627 + 'click .delete': this._deleteHandler
  628 + });
  629 + this._initButtonBarEventHandlers();
  630 + },
  631 +
  632 + _destroyEventHandlers: function () {
  633 + this._destroyButtonBarEventHandlers();
  634 + this._off(this.options.filesContainer, 'click');
  635 + this._super();
  636 + },
  637 +
  638 + _enableFileInputButton: function () {
  639 + this.element.find('.fileinput-button input')
  640 + .prop('disabled', false)
  641 + .parent().removeClass('disabled');
  642 + },
  643 +
  644 + _disableFileInputButton: function () {
  645 + this.element.find('.fileinput-button input')
  646 + .prop('disabled', true)
  647 + .parent().addClass('disabled');
  648 + },
  649 +
  650 + _initTemplates: function () {
  651 + var options = this.options;
  652 + options.templatesContainer = this.document[0].createElement(
  653 + options.filesContainer.prop('nodeName')
  654 + );
  655 + if (tmpl) {
  656 + if (options.uploadTemplateId) {
  657 + options.uploadTemplate = tmpl(options.uploadTemplateId);
  658 + }
  659 + if (options.downloadTemplateId) {
  660 + options.downloadTemplate = tmpl(options.downloadTemplateId);
  661 + }
  662 + }
  663 + },
  664 +
  665 + _initFilesContainer: function () {
  666 + var options = this.options;
  667 + if (options.filesContainer === undefined) {
  668 + options.filesContainer = this.element.find('.files');
  669 + } else if (!(options.filesContainer instanceof $)) {
  670 + options.filesContainer = $(options.filesContainer);
  671 + }
  672 + },
  673 +
  674 + _initSpecialOptions: function () {
  675 + this._super();
  676 + this._initFilesContainer();
  677 + this._initTemplates();
  678 + },
  679 +
  680 + _create: function () {
  681 + this._super();
  682 + this._resetFinishedDeferreds();
  683 + if (!$.support.fileInput) {
  684 + this._disableFileInputButton();
  685 + }
  686 + },
  687 +
  688 + enable: function () {
  689 + var wasDisabled = false;
  690 + if (this.options.disabled) {
  691 + wasDisabled = true;
  692 + }
  693 + this._super();
  694 + if (wasDisabled) {
  695 + this.element.find('input, button').prop('disabled', false);
  696 + this._enableFileInputButton();
  697 + }
  698 + },
  699 +
  700 + disable: function () {
  701 + if (!this.options.disabled) {
  702 + this.element.find('input, button').prop('disabled', true);
  703 + this._disableFileInputButton();
  704 + }
  705 + this._super();
  706 + }
  707 +
  708 + });
  709 +
  710 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-validate.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Validation Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* global define, require, window */
  13 +
  14 +(function (factory) {
  15 + 'use strict';
  16 + if (typeof define === 'function' && define.amd) {
  17 + // Register as an anonymous AMD module:
  18 + define([
  19 + 'jquery',
  20 + './jquery.fileupload-process'
  21 + ], factory);
  22 + } else if (typeof exports === 'object') {
  23 + // Node/CommonJS:
  24 + factory(require('jquery'));
  25 + } else {
  26 + // Browser globals:
  27 + factory(
  28 + window.jQuery
  29 + );
  30 + }
  31 +}(function ($) {
  32 + 'use strict';
  33 +
  34 + // Append to the default processQueue:
  35 + $.blueimp.fileupload.prototype.options.processQueue.push(
  36 + {
  37 + action: 'validate',
  38 + // Always trigger this action,
  39 + // even if the previous action was rejected:
  40 + always: true,
  41 + // Options taken from the global options map:
  42 + acceptFileTypes: '@',
  43 + maxFileSize: '@',
  44 + minFileSize: '@',
  45 + maxNumberOfFiles: '@',
  46 + disabled: '@disableValidation'
  47 + }
  48 + );
  49 +
  50 + // The File Upload Validation plugin extends the fileupload widget
  51 + // with file validation functionality:
  52 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  53 +
  54 + options: {
  55 + /*
  56 + // The regular expression for allowed file types, matches
  57 + // against either file type or file name:
  58 + acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
  59 + // The maximum allowed file size in bytes:
  60 + maxFileSize: 10000000, // 10 MB
  61 + // The minimum allowed file size in bytes:
  62 + minFileSize: undefined, // No minimal file size
  63 + // The limit of files to be uploaded:
  64 + maxNumberOfFiles: 10,
  65 + */
  66 +
  67 + // Function returning the current number of files,
  68 + // has to be overriden for maxNumberOfFiles validation:
  69 + getNumberOfFiles: $.noop,
  70 +
  71 + // Error and info messages:
  72 + messages: {
  73 + maxNumberOfFiles: 'Maximum number of files exceeded',
  74 + acceptFileTypes: 'File type not allowed',
  75 + maxFileSize: 'File is too large',
  76 + minFileSize: 'File is too small'
  77 + }
  78 + },
  79 +
  80 + processActions: {
  81 +
  82 + validate: function (data, options) {
  83 + if (options.disabled) {
  84 + return data;
  85 + }
  86 + var dfd = $.Deferred(),
  87 + settings = this.options,
  88 + file = data.files[data.index],
  89 + fileSize;
  90 + if (options.minFileSize || options.maxFileSize) {
  91 + fileSize = file.size;
  92 + }
  93 + if ($.type(options.maxNumberOfFiles) === 'number' &&
  94 + (settings.getNumberOfFiles() || 0) + data.files.length >
  95 + options.maxNumberOfFiles) {
  96 + file.error = settings.i18n('maxNumberOfFiles');
  97 + } else if (options.acceptFileTypes &&
  98 + !(options.acceptFileTypes.test(file.type) ||
  99 + options.acceptFileTypes.test(file.name))) {
  100 + file.error = settings.i18n('acceptFileTypes');
  101 + } else if (fileSize > options.maxFileSize) {
  102 + file.error = settings.i18n('maxFileSize');
  103 + } else if ($.type(fileSize) === 'number' &&
  104 + fileSize < options.minFileSize) {
  105 + file.error = settings.i18n('minFileSize');
  106 + } else {
  107 + delete file.error;
  108 + }
  109 + if (file.error || data.files.error) {
  110 + data.files.error = true;
  111 + dfd.rejectWith(this, [data]);
  112 + } else {
  113 + dfd.resolveWith(this, [data]);
  114 + }
  115 + return dfd.promise();
  116 + }
  117 +
  118 + }
  119 +
  120 + });
  121 +
  122 +}));
... ...
common/modules/file/assets/js/jquery.fileupload-video.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Video Preview Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2013, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window, document */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'load-image',
  22 + './jquery.fileupload-process'
  23 + ], factory);
  24 + } else if (typeof exports === 'object') {
  25 + // Node/CommonJS:
  26 + factory(
  27 + require('jquery'),
  28 + require('load-image')
  29 + );
  30 + } else {
  31 + // Browser globals:
  32 + factory(
  33 + window.jQuery,
  34 + window.loadImage
  35 + );
  36 + }
  37 +}(function ($, loadImage) {
  38 + 'use strict';
  39 +
  40 + // Prepend to the default processQueue:
  41 + $.blueimp.fileupload.prototype.options.processQueue.unshift(
  42 + {
  43 + action: 'loadVideo',
  44 + // Use the action as prefix for the "@" options:
  45 + prefix: true,
  46 + fileTypes: '@',
  47 + maxFileSize: '@',
  48 + disabled: '@disableVideoPreview'
  49 + },
  50 + {
  51 + action: 'setVideo',
  52 + name: '@videoPreviewName',
  53 + disabled: '@disableVideoPreview'
  54 + }
  55 + );
  56 +
  57 + // The File Upload Video Preview plugin extends the fileupload widget
  58 + // with video preview functionality:
  59 + $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  60 +
  61 + options: {
  62 + // The regular expression for the types of video files to load,
  63 + // matched against the file type:
  64 + loadVideoFileTypes: /^video\/.*$/
  65 + },
  66 +
  67 + _videoElement: document.createElement('video'),
  68 +
  69 + processActions: {
  70 +
  71 + // Loads the video file given via data.files and data.index
  72 + // as video element if the browser supports playing it.
  73 + // Accepts the options fileTypes (regular expression)
  74 + // and maxFileSize (integer) to limit the files to load:
  75 + loadVideo: function (data, options) {
  76 + if (options.disabled) {
  77 + return data;
  78 + }
  79 + var file = data.files[data.index],
  80 + url,
  81 + video;
  82 + if (this._videoElement.canPlayType &&
  83 + this._videoElement.canPlayType(file.type) &&
  84 + ($.type(options.maxFileSize) !== 'number' ||
  85 + file.size <= options.maxFileSize) &&
  86 + (!options.fileTypes ||
  87 + options.fileTypes.test(file.type))) {
  88 + url = loadImage.createObjectURL(file);
  89 + if (url) {
  90 + video = this._videoElement.cloneNode(false);
  91 + video.src = url;
  92 + video.controls = true;
  93 + data.video = video;
  94 + return data;
  95 + }
  96 + }
  97 + return data;
  98 + },
  99 +
  100 + // Sets the video element as a property of the file object:
  101 + setVideo: function (data, options) {
  102 + if (data.video && !options.disabled) {
  103 + data.files[data.index][options.name || 'preview'] = data.video;
  104 + }
  105 + return data;
  106 + }
  107 +
  108 + }
  109 +
  110 + });
  111 +
  112 +}));
... ...
common/modules/file/assets/js/jquery.fileupload.js 0 → 100644
  1 +/*
  2 + * jQuery File Upload Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2010, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* jshint nomen:false */
  13 +/* global define, require, window, document, location, Blob, FormData */
  14 +
  15 +(function (factory) {
  16 + 'use strict';
  17 + if (typeof define === 'function' && define.amd) {
  18 + // Register as an anonymous AMD module:
  19 + define([
  20 + 'jquery',
  21 + 'jquery.ui.widget'
  22 + ], factory);
  23 + } else if (typeof exports === 'object') {
  24 + // Node/CommonJS:
  25 + factory(
  26 + require('jquery'),
  27 + require('./vendor/jquery.ui.widget')
  28 + );
  29 + } else {
  30 + // Browser globals:
  31 + factory(window.jQuery);
  32 + }
  33 +}(function ($) {
  34 + 'use strict';
  35 +
  36 + // Detect file input support, based on
  37 + // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
  38 + $.support.fileInput = !(new RegExp(
  39 + // Handle devices which give false positives for the feature detection:
  40 + '(Android (1\\.[0156]|2\\.[01]))' +
  41 + '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
  42 + '|(w(eb)?OSBrowser)|(webOS)' +
  43 + '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
  44 + ).test(window.navigator.userAgent) ||
  45 + // Feature detection for all other devices:
  46 + $('<input type="file">').prop('disabled'));
  47 +
  48 + // The FileReader API is not actually used, but works as feature detection,
  49 + // as some Safari versions (5?) support XHR file uploads via the FormData API,
  50 + // but not non-multipart XHR file uploads.
  51 + // window.XMLHttpRequestUpload is not available on IE10, so we check for
  52 + // window.ProgressEvent instead to detect XHR2 file upload capability:
  53 + $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
  54 + $.support.xhrFormDataFileUpload = !!window.FormData;
  55 +
  56 + // Detect support for Blob slicing (required for chunked uploads):
  57 + $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
  58 + Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
  59 +
  60 + // Helper function to create drag handlers for dragover/dragenter/dragleave:
  61 + function getDragHandler(type) {
  62 + var isDragOver = type === 'dragover';
  63 + return function (e) {
  64 + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
  65 + var dataTransfer = e.dataTransfer;
  66 + if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
  67 + this._trigger(
  68 + type,
  69 + $.Event(type, {delegatedEvent: e})
  70 + ) !== false) {
  71 + e.preventDefault();
  72 + if (isDragOver) {
  73 + dataTransfer.dropEffect = 'copy';
  74 + }
  75 + }
  76 + };
  77 + }
  78 +
  79 + // The fileupload widget listens for change events on file input fields defined
  80 + // via fileInput setting and paste or drop events of the given dropZone.
  81 + // In addition to the default jQuery Widget methods, the fileupload widget
  82 + // exposes the "add" and "send" methods, to add or directly send files using
  83 + // the fileupload API.
  84 + // By default, files added via file input selection, paste, drag & drop or
  85 + // "add" method are uploaded immediately, but it is possible to override
  86 + // the "add" callback option to queue file uploads.
  87 + $.widget('blueimp.fileupload', {
  88 +
  89 + options: {
  90 + // The drop target element(s), by the default the complete document.
  91 + // Set to null to disable drag & drop support:
  92 + dropZone: $(document),
  93 + // The paste target element(s), by the default undefined.
  94 + // Set to a DOM node or jQuery object to enable file pasting:
  95 + pasteZone: undefined,
  96 + // The file input field(s), that are listened to for change events.
  97 + // If undefined, it is set to the file input fields inside
  98 + // of the widget element on plugin initialization.
  99 + // Set to null to disable the change listener.
  100 + fileInput: undefined,
  101 + // By default, the file input field is replaced with a clone after
  102 + // each input field change event. This is required for iframe transport
  103 + // queues and allows change events to be fired for the same file
  104 + // selection, but can be disabled by setting the following option to false:
  105 + replaceFileInput: true,
  106 + // The parameter name for the file form data (the request argument name).
  107 + // If undefined or empty, the name property of the file input field is
  108 + // used, or "files[]" if the file input name property is also empty,
  109 + // can be a string or an array of strings:
  110 + paramName: undefined,
  111 + // By default, each file of a selection is uploaded using an individual
  112 + // request for XHR type uploads. Set to false to upload file
  113 + // selections in one request each:
  114 + singleFileUploads: true,
  115 + // To limit the number of files uploaded with one XHR request,
  116 + // set the following option to an integer greater than 0:
  117 + limitMultiFileUploads: undefined,
  118 + // The following option limits the number of files uploaded with one
  119 + // XHR request to keep the request size under or equal to the defined
  120 + // limit in bytes:
  121 + limitMultiFileUploadSize: undefined,
  122 + // Multipart file uploads add a number of bytes to each uploaded file,
  123 + // therefore the following option adds an overhead for each file used
  124 + // in the limitMultiFileUploadSize configuration:
  125 + limitMultiFileUploadSizeOverhead: 512,
  126 + // Set the following option to true to issue all file upload requests
  127 + // in a sequential order:
  128 + sequentialUploads: false,
  129 + // To limit the number of concurrent uploads,
  130 + // set the following option to an integer greater than 0:
  131 + limitConcurrentUploads: undefined,
  132 + // Set the following option to true to force iframe transport uploads:
  133 + forceIframeTransport: false,
  134 + // Set the following option to the location of a redirect url on the
  135 + // origin server, for cross-domain iframe transport uploads:
  136 + redirect: undefined,
  137 + // The parameter name for the redirect url, sent as part of the form
  138 + // data and set to 'redirect' if this option is empty:
  139 + redirectParamName: undefined,
  140 + // Set the following option to the location of a postMessage window,
  141 + // to enable postMessage transport uploads:
  142 + postMessage: undefined,
  143 + // By default, XHR file uploads are sent as multipart/form-data.
  144 + // The iframe transport is always using multipart/form-data.
  145 + // Set to false to enable non-multipart XHR uploads:
  146 + multipart: true,
  147 + // To upload large files in smaller chunks, set the following option
  148 + // to a preferred maximum chunk size. If set to 0, null or undefined,
  149 + // or the browser does not support the required Blob API, files will
  150 + // be uploaded as a whole.
  151 + maxChunkSize: undefined,
  152 + // When a non-multipart upload or a chunked multipart upload has been
  153 + // aborted, this option can be used to resume the upload by setting
  154 + // it to the size of the already uploaded bytes. This option is most
  155 + // useful when modifying the options object inside of the "add" or
  156 + // "send" callbacks, as the options are cloned for each file upload.
  157 + uploadedBytes: undefined,
  158 + // By default, failed (abort or error) file uploads are removed from the
  159 + // global progress calculation. Set the following option to false to
  160 + // prevent recalculating the global progress data:
  161 + recalculateProgress: true,
  162 + // Interval in milliseconds to calculate and trigger progress events:
  163 + progressInterval: 100,
  164 + // Interval in milliseconds to calculate progress bitrate:
  165 + bitrateInterval: 500,
  166 + // By default, uploads are started automatically when adding files:
  167 + autoUpload: true,
  168 +
  169 + // Error and info messages:
  170 + messages: {
  171 + uploadedBytes: 'Uploaded bytes exceed file size'
  172 + },
  173 +
  174 + // Translation function, gets the message key to be translated
  175 + // and an object with context specific data as arguments:
  176 + i18n: function (message, context) {
  177 + message = this.messages[message] || message.toString();
  178 + if (context) {
  179 + $.each(context, function (key, value) {
  180 + message = message.replace('{' + key + '}', value);
  181 + });
  182 + }
  183 + return message;
  184 + },
  185 +
  186 + // Additional form data to be sent along with the file uploads can be set
  187 + // using this option, which accepts an array of objects with name and
  188 + // value properties, a function returning such an array, a FormData
  189 + // object (for XHR file uploads), or a simple object.
  190 + // The form of the first fileInput is given as parameter to the function:
  191 + formData: function (form) {
  192 + return form.serializeArray();
  193 + },
  194 +
  195 + // The add callback is invoked as soon as files are added to the fileupload
  196 + // widget (via file input selection, drag & drop, paste or add API call).
  197 + // If the singleFileUploads option is enabled, this callback will be
  198 + // called once for each file in the selection for XHR file uploads, else
  199 + // once for each file selection.
  200 + //
  201 + // The upload starts when the submit method is invoked on the data parameter.
  202 + // The data object contains a files property holding the added files
  203 + // and allows you to override plugin options as well as define ajax settings.
  204 + //
  205 + // Listeners for this callback can also be bound the following way:
  206 + // .bind('fileuploadadd', func);
  207 + //
  208 + // data.submit() returns a Promise object and allows to attach additional
  209 + // handlers using jQuery's Deferred callbacks:
  210 + // data.submit().done(func).fail(func).always(func);
  211 + add: function (e, data) {
  212 + if (e.isDefaultPrevented()) {
  213 + return false;
  214 + }
  215 + if (data.autoUpload || (data.autoUpload !== false &&
  216 + $(this).fileupload('option', 'autoUpload'))) {
  217 + data.process().done(function () {
  218 + data.submit();
  219 + });
  220 + }
  221 + },
  222 +
  223 + // Other callbacks:
  224 +
  225 + // Callback for the submit event of each file upload:
  226 + // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
  227 +
  228 + // Callback for the start of each file upload request:
  229 + // send: function (e, data) {}, // .bind('fileuploadsend', func);
  230 +
  231 + // Callback for successful uploads:
  232 + // done: function (e, data) {}, // .bind('fileuploaddone', func);
  233 +
  234 + // Callback for failed (abort or error) uploads:
  235 + // fail: function (e, data) {}, // .bind('fileuploadfail', func);
  236 +
  237 + // Callback for completed (success, abort or error) requests:
  238 + // always: function (e, data) {}, // .bind('fileuploadalways', func);
  239 +
  240 + // Callback for upload progress events:
  241 + // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
  242 +
  243 + // Callback for global upload progress events:
  244 + // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
  245 +
  246 + // Callback for uploads start, equivalent to the global ajaxStart event:
  247 + // start: function (e) {}, // .bind('fileuploadstart', func);
  248 +
  249 + // Callback for uploads stop, equivalent to the global ajaxStop event:
  250 + // stop: function (e) {}, // .bind('fileuploadstop', func);
  251 +
  252 + // Callback for change events of the fileInput(s):
  253 + // change: function (e, data) {}, // .bind('fileuploadchange', func);
  254 +
  255 + // Callback for paste events to the pasteZone(s):
  256 + // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
  257 +
  258 + // Callback for drop events of the dropZone(s):
  259 + // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
  260 +
  261 + // Callback for dragover events of the dropZone(s):
  262 + // dragover: function (e) {}, // .bind('fileuploaddragover', func);
  263 +
  264 + // Callback for the start of each chunk upload request:
  265 + // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);
  266 +
  267 + // Callback for successful chunk uploads:
  268 + // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);
  269 +
  270 + // Callback for failed (abort or error) chunk uploads:
  271 + // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);
  272 +
  273 + // Callback for completed (success, abort or error) chunk upload requests:
  274 + // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);
  275 +
  276 + // The plugin options are used as settings object for the ajax calls.
  277 + // The following are jQuery ajax settings required for the file uploads:
  278 + processData: false,
  279 + contentType: false,
  280 + cache: false,
  281 + timeout: 0
  282 + },
  283 +
  284 + // A list of options that require reinitializing event listeners and/or
  285 + // special initialization code:
  286 + _specialOptions: [
  287 + 'fileInput',
  288 + 'dropZone',
  289 + 'pasteZone',
  290 + 'multipart',
  291 + 'forceIframeTransport'
  292 + ],
  293 +
  294 + _blobSlice: $.support.blobSlice && function () {
  295 + var slice = this.slice || this.webkitSlice || this.mozSlice;
  296 + return slice.apply(this, arguments);
  297 + },
  298 +
  299 + _BitrateTimer: function () {
  300 + this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
  301 + this.loaded = 0;
  302 + this.bitrate = 0;
  303 + this.getBitrate = function (now, loaded, interval) {
  304 + var timeDiff = now - this.timestamp;
  305 + if (!this.bitrate || !interval || timeDiff > interval) {
  306 + this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
  307 + this.loaded = loaded;
  308 + this.timestamp = now;
  309 + }
  310 + return this.bitrate;
  311 + };
  312 + },
  313 +
  314 + _isXHRUpload: function (options) {
  315 + return !options.forceIframeTransport &&
  316 + ((!options.multipart && $.support.xhrFileUpload) ||
  317 + $.support.xhrFormDataFileUpload);
  318 + },
  319 +
  320 + _getFormData: function (options) {
  321 + var formData;
  322 + if ($.type(options.formData) === 'function') {
  323 + return options.formData(options.form);
  324 + }
  325 + if ($.isArray(options.formData)) {
  326 + return options.formData;
  327 + }
  328 + if ($.type(options.formData) === 'object') {
  329 + formData = [];
  330 + $.each(options.formData, function (name, value) {
  331 + formData.push({name: name, value: value});
  332 + });
  333 + return formData;
  334 + }
  335 + return [];
  336 + },
  337 +
  338 + _getTotal: function (files) {
  339 + var total = 0;
  340 + $.each(files, function (index, file) {
  341 + total += file.size || 1;
  342 + });
  343 + return total;
  344 + },
  345 +
  346 + _initProgressObject: function (obj) {
  347 + var progress = {
  348 + loaded: 0,
  349 + total: 0,
  350 + bitrate: 0
  351 + };
  352 + if (obj._progress) {
  353 + $.extend(obj._progress, progress);
  354 + } else {
  355 + obj._progress = progress;
  356 + }
  357 + },
  358 +
  359 + _initResponseObject: function (obj) {
  360 + var prop;
  361 + if (obj._response) {
  362 + for (prop in obj._response) {
  363 + if (obj._response.hasOwnProperty(prop)) {
  364 + delete obj._response[prop];
  365 + }
  366 + }
  367 + } else {
  368 + obj._response = {};
  369 + }
  370 + },
  371 +
  372 + _onProgress: function (e, data) {
  373 + if (e.lengthComputable) {
  374 + var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
  375 + loaded;
  376 + if (data._time && data.progressInterval &&
  377 + (now - data._time < data.progressInterval) &&
  378 + e.loaded !== e.total) {
  379 + return;
  380 + }
  381 + data._time = now;
  382 + loaded = Math.floor(
  383 + e.loaded / e.total * (data.chunkSize || data._progress.total)
  384 + ) + (data.uploadedBytes || 0);
  385 + // Add the difference from the previously loaded state
  386 + // to the global loaded counter:
  387 + this._progress.loaded += (loaded - data._progress.loaded);
  388 + this._progress.bitrate = this._bitrateTimer.getBitrate(
  389 + now,
  390 + this._progress.loaded,
  391 + data.bitrateInterval
  392 + );
  393 + data._progress.loaded = data.loaded = loaded;
  394 + data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
  395 + now,
  396 + loaded,
  397 + data.bitrateInterval
  398 + );
  399 + // Trigger a custom progress event with a total data property set
  400 + // to the file size(s) of the current upload and a loaded data
  401 + // property calculated accordingly:
  402 + this._trigger(
  403 + 'progress',
  404 + $.Event('progress', {delegatedEvent: e}),
  405 + data
  406 + );
  407 + // Trigger a global progress event for all current file uploads,
  408 + // including ajax calls queued for sequential file uploads:
  409 + this._trigger(
  410 + 'progressall',
  411 + $.Event('progressall', {delegatedEvent: e}),
  412 + this._progress
  413 + );
  414 + }
  415 + },
  416 +
  417 + _initProgressListener: function (options) {
  418 + var that = this,
  419 + xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
  420 + // Accesss to the native XHR object is required to add event listeners
  421 + // for the upload progress event:
  422 + if (xhr.upload) {
  423 + $(xhr.upload).bind('progress', function (e) {
  424 + var oe = e.originalEvent;
  425 + // Make sure the progress event properties get copied over:
  426 + e.lengthComputable = oe.lengthComputable;
  427 + e.loaded = oe.loaded;
  428 + e.total = oe.total;
  429 + that._onProgress(e, options);
  430 + });
  431 + options.xhr = function () {
  432 + return xhr;
  433 + };
  434 + }
  435 + },
  436 +
  437 + _isInstanceOf: function (type, obj) {
  438 + // Cross-frame instanceof check
  439 + return Object.prototype.toString.call(obj) === '[object ' + type + ']';
  440 + },
  441 +
  442 + _initXHRData: function (options) {
  443 + var that = this,
  444 + formData,
  445 + file = options.files[0],
  446 + // Ignore non-multipart setting if not supported:
  447 + multipart = options.multipart || !$.support.xhrFileUpload,
  448 + paramName = $.type(options.paramName) === 'array' ?
  449 + options.paramName[0] : options.paramName;
  450 + options.headers = $.extend({}, options.headers);
  451 + if (options.contentRange) {
  452 + options.headers['Content-Range'] = options.contentRange;
  453 + }
  454 + if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
  455 + options.headers['Content-Disposition'] = 'attachment; filename="' +
  456 + encodeURI(file.name) + '"';
  457 + }
  458 + if (!multipart) {
  459 + options.contentType = file.type || 'application/octet-stream';
  460 + options.data = options.blob || file;
  461 + } else if ($.support.xhrFormDataFileUpload) {
  462 + if (options.postMessage) {
  463 + // window.postMessage does not allow sending FormData
  464 + // objects, so we just add the File/Blob objects to
  465 + // the formData array and let the postMessage window
  466 + // create the FormData object out of this array:
  467 + formData = this._getFormData(options);
  468 + if (options.blob) {
  469 + formData.push({
  470 + name: paramName,
  471 + value: options.blob
  472 + });
  473 + } else {
  474 + $.each(options.files, function (index, file) {
  475 + formData.push({
  476 + name: ($.type(options.paramName) === 'array' &&
  477 + options.paramName[index]) || paramName,
  478 + value: file
  479 + });
  480 + });
  481 + }
  482 + } else {
  483 + if (that._isInstanceOf('FormData', options.formData)) {
  484 + formData = options.formData;
  485 + } else {
  486 + formData = new FormData();
  487 + $.each(this._getFormData(options), function (index, field) {
  488 + formData.append(field.name, field.value);
  489 + });
  490 + }
  491 + if (options.blob) {
  492 + formData.append(paramName, options.blob, file.name);
  493 + } else {
  494 + $.each(options.files, function (index, file) {
  495 + // This check allows the tests to run with
  496 + // dummy objects:
  497 + if (that._isInstanceOf('File', file) ||
  498 + that._isInstanceOf('Blob', file)) {
  499 + formData.append(
  500 + ($.type(options.paramName) === 'array' &&
  501 + options.paramName[index]) || paramName,
  502 + file,
  503 + file.uploadName || file.name
  504 + );
  505 + }
  506 + });
  507 + }
  508 + }
  509 + options.data = formData;
  510 + }
  511 + // Blob reference is not needed anymore, free memory:
  512 + options.blob = null;
  513 + },
  514 +
  515 + _initIframeSettings: function (options) {
  516 + var targetHost = $('<a></a>').prop('href', options.url).prop('host');
  517 + // Setting the dataType to iframe enables the iframe transport:
  518 + options.dataType = 'iframe ' + (options.dataType || '');
  519 + // The iframe transport accepts a serialized array as form data:
  520 + options.formData = this._getFormData(options);
  521 + // Add redirect url to form data on cross-domain uploads:
  522 + if (options.redirect && targetHost && targetHost !== location.host) {
  523 + options.formData.push({
  524 + name: options.redirectParamName || 'redirect',
  525 + value: options.redirect
  526 + });
  527 + }
  528 + },
  529 +
  530 + _initDataSettings: function (options) {
  531 + if (this._isXHRUpload(options)) {
  532 + if (!this._chunkedUpload(options, true)) {
  533 + if (!options.data) {
  534 + this._initXHRData(options);
  535 + }
  536 + this._initProgressListener(options);
  537 + }
  538 + if (options.postMessage) {
  539 + // Setting the dataType to postmessage enables the
  540 + // postMessage transport:
  541 + options.dataType = 'postmessage ' + (options.dataType || '');
  542 + }
  543 + } else {
  544 + this._initIframeSettings(options);
  545 + }
  546 + },
  547 +
  548 + _getParamName: function (options) {
  549 + var fileInput = $(options.fileInput),
  550 + paramName = options.paramName;
  551 + if (!paramName) {
  552 + paramName = [];
  553 + fileInput.each(function () {
  554 + var input = $(this),
  555 + name = input.prop('name') || 'files[]',
  556 + i = (input.prop('files') || [1]).length;
  557 + while (i) {
  558 + paramName.push(name);
  559 + i -= 1;
  560 + }
  561 + });
  562 + if (!paramName.length) {
  563 + paramName = [fileInput.prop('name') || 'files[]'];
  564 + }
  565 + } else if (!$.isArray(paramName)) {
  566 + paramName = [paramName];
  567 + }
  568 + return paramName;
  569 + },
  570 +
  571 + _initFormSettings: function (options) {
  572 + // Retrieve missing options from the input field and the
  573 + // associated form, if available:
  574 + if (!options.form || !options.form.length) {
  575 + options.form = $(options.fileInput.prop('form'));
  576 + // If the given file input doesn't have an associated form,
  577 + // use the default widget file input's form:
  578 + if (!options.form.length) {
  579 + options.form = $(this.options.fileInput.prop('form'));
  580 + }
  581 + }
  582 + options.paramName = this._getParamName(options);
  583 + if (!options.url) {
  584 + options.url = options.form.prop('action') || location.href;
  585 + }
  586 + // The HTTP request method must be "POST" or "PUT":
  587 + options.type = (options.type ||
  588 + ($.type(options.form.prop('method')) === 'string' &&
  589 + options.form.prop('method')) || ''
  590 + ).toUpperCase();
  591 + if (options.type !== 'POST' && options.type !== 'PUT' &&
  592 + options.type !== 'PATCH') {
  593 + options.type = 'POST';
  594 + }
  595 + if (!options.formAcceptCharset) {
  596 + options.formAcceptCharset = options.form.attr('accept-charset');
  597 + }
  598 + },
  599 +
  600 + _getAJAXSettings: function (data) {
  601 + var options = $.extend({}, this.options, data);
  602 + this._initFormSettings(options);
  603 + this._initDataSettings(options);
  604 + return options;
  605 + },
  606 +
  607 + // jQuery 1.6 doesn't provide .state(),
  608 + // while jQuery 1.8+ removed .isRejected() and .isResolved():
  609 + _getDeferredState: function (deferred) {
  610 + if (deferred.state) {
  611 + return deferred.state();
  612 + }
  613 + if (deferred.isResolved()) {
  614 + return 'resolved';
  615 + }
  616 + if (deferred.isRejected()) {
  617 + return 'rejected';
  618 + }
  619 + return 'pending';
  620 + },
  621 +
  622 + // Maps jqXHR callbacks to the equivalent
  623 + // methods of the given Promise object:
  624 + _enhancePromise: function (promise) {
  625 + promise.success = promise.done;
  626 + promise.error = promise.fail;
  627 + promise.complete = promise.always;
  628 + return promise;
  629 + },
  630 +
  631 + // Creates and returns a Promise object enhanced with
  632 + // the jqXHR methods abort, success, error and complete:
  633 + _getXHRPromise: function (resolveOrReject, context, args) {
  634 + var dfd = $.Deferred(),
  635 + promise = dfd.promise();
  636 + context = context || this.options.context || promise;
  637 + if (resolveOrReject === true) {
  638 + dfd.resolveWith(context, args);
  639 + } else if (resolveOrReject === false) {
  640 + dfd.rejectWith(context, args);
  641 + }
  642 + promise.abort = dfd.promise;
  643 + return this._enhancePromise(promise);
  644 + },
  645 +
  646 + // Adds convenience methods to the data callback argument:
  647 + _addConvenienceMethods: function (e, data) {
  648 + var that = this,
  649 + getPromise = function (args) {
  650 + return $.Deferred().resolveWith(that, args).promise();
  651 + };
  652 + data.process = function (resolveFunc, rejectFunc) {
  653 + if (resolveFunc || rejectFunc) {
  654 + data._processQueue = this._processQueue =
  655 + (this._processQueue || getPromise([this])).pipe(
  656 + function () {
  657 + if (data.errorThrown) {
  658 + return $.Deferred()
  659 + .rejectWith(that, [data]).promise();
  660 + }
  661 + return getPromise(arguments);
  662 + }
  663 + ).pipe(resolveFunc, rejectFunc);
  664 + }
  665 + return this._processQueue || getPromise([this]);
  666 + };
  667 + data.submit = function () {
  668 + if (this.state() !== 'pending') {
  669 + data.jqXHR = this.jqXHR =
  670 + (that._trigger(
  671 + 'submit',
  672 + $.Event('submit', {delegatedEvent: e}),
  673 + this
  674 + ) !== false) && that._onSend(e, this);
  675 + }
  676 + return this.jqXHR || that._getXHRPromise();
  677 + };
  678 + data.abort = function () {
  679 + if (this.jqXHR) {
  680 + return this.jqXHR.abort();
  681 + }
  682 + this.errorThrown = 'abort';
  683 + that._trigger('fail', null, this);
  684 + return that._getXHRPromise(false);
  685 + };
  686 + data.state = function () {
  687 + if (this.jqXHR) {
  688 + return that._getDeferredState(this.jqXHR);
  689 + }
  690 + if (this._processQueue) {
  691 + return that._getDeferredState(this._processQueue);
  692 + }
  693 + };
  694 + data.processing = function () {
  695 + return !this.jqXHR && this._processQueue && that
  696 + ._getDeferredState(this._processQueue) === 'pending';
  697 + };
  698 + data.progress = function () {
  699 + return this._progress;
  700 + };
  701 + data.response = function () {
  702 + return this._response;
  703 + };
  704 + },
  705 +
  706 + // Parses the Range header from the server response
  707 + // and returns the uploaded bytes:
  708 + _getUploadedBytes: function (jqXHR) {
  709 + var range = jqXHR.getResponseHeader('Range'),
  710 + parts = range && range.split('-'),
  711 + upperBytesPos = parts && parts.length > 1 &&
  712 + parseInt(parts[1], 10);
  713 + return upperBytesPos && upperBytesPos + 1;
  714 + },
  715 +
  716 + // Uploads a file in multiple, sequential requests
  717 + // by splitting the file up in multiple blob chunks.
  718 + // If the second parameter is true, only tests if the file
  719 + // should be uploaded in chunks, but does not invoke any
  720 + // upload requests:
  721 + _chunkedUpload: function (options, testOnly) {
  722 + options.uploadedBytes = options.uploadedBytes || 0;
  723 + var that = this,
  724 + file = options.files[0],
  725 + fs = file.size,
  726 + ub = options.uploadedBytes,
  727 + mcs = options.maxChunkSize || fs,
  728 + slice = this._blobSlice,
  729 + dfd = $.Deferred(),
  730 + promise = dfd.promise(),
  731 + jqXHR,
  732 + upload;
  733 + if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
  734 + options.data) {
  735 + return false;
  736 + }
  737 + if (testOnly) {
  738 + return true;
  739 + }
  740 + if (ub >= fs) {
  741 + file.error = options.i18n('uploadedBytes');
  742 + return this._getXHRPromise(
  743 + false,
  744 + options.context,
  745 + [null, 'error', file.error]
  746 + );
  747 + }
  748 + // The chunk upload method:
  749 + upload = function () {
  750 + // Clone the options object for each chunk upload:
  751 + var o = $.extend({}, options),
  752 + currentLoaded = o._progress.loaded;
  753 + o.blob = slice.call(
  754 + file,
  755 + ub,
  756 + ub + mcs,
  757 + file.type
  758 + );
  759 + // Store the current chunk size, as the blob itself
  760 + // will be dereferenced after data processing:
  761 + o.chunkSize = o.blob.size;
  762 + // Expose the chunk bytes position range:
  763 + o.contentRange = 'bytes ' + ub + '-' +
  764 + (ub + o.chunkSize - 1) + '/' + fs;
  765 + // Process the upload data (the blob and potential form data):
  766 + that._initXHRData(o);
  767 + // Add progress listeners for this chunk upload:
  768 + that._initProgressListener(o);
  769 + jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
  770 + that._getXHRPromise(false, o.context))
  771 + .done(function (result, textStatus, jqXHR) {
  772 + ub = that._getUploadedBytes(jqXHR) ||
  773 + (ub + o.chunkSize);
  774 + // Create a progress event if no final progress event
  775 + // with loaded equaling total has been triggered
  776 + // for this chunk:
  777 + if (currentLoaded + o.chunkSize - o._progress.loaded) {
  778 + that._onProgress($.Event('progress', {
  779 + lengthComputable: true,
  780 + loaded: ub - o.uploadedBytes,
  781 + total: ub - o.uploadedBytes
  782 + }), o);
  783 + }
  784 + options.uploadedBytes = o.uploadedBytes = ub;
  785 + o.result = result;
  786 + o.textStatus = textStatus;
  787 + o.jqXHR = jqXHR;
  788 + that._trigger('chunkdone', null, o);
  789 + that._trigger('chunkalways', null, o);
  790 + if (ub < fs) {
  791 + // File upload not yet complete,
  792 + // continue with the next chunk:
  793 + upload();
  794 + } else {
  795 + dfd.resolveWith(
  796 + o.context,
  797 + [result, textStatus, jqXHR]
  798 + );
  799 + }
  800 + })
  801 + .fail(function (jqXHR, textStatus, errorThrown) {
  802 + o.jqXHR = jqXHR;
  803 + o.textStatus = textStatus;
  804 + o.errorThrown = errorThrown;
  805 + that._trigger('chunkfail', null, o);
  806 + that._trigger('chunkalways', null, o);
  807 + dfd.rejectWith(
  808 + o.context,
  809 + [jqXHR, textStatus, errorThrown]
  810 + );
  811 + });
  812 + };
  813 + this._enhancePromise(promise);
  814 + promise.abort = function () {
  815 + return jqXHR.abort();
  816 + };
  817 + upload();
  818 + return promise;
  819 + },
  820 +
  821 + _beforeSend: function (e, data) {
  822 + if (this._active === 0) {
  823 + // the start callback is triggered when an upload starts
  824 + // and no other uploads are currently running,
  825 + // equivalent to the global ajaxStart event:
  826 + this._trigger('start');
  827 + // Set timer for global bitrate progress calculation:
  828 + this._bitrateTimer = new this._BitrateTimer();
  829 + // Reset the global progress values:
  830 + this._progress.loaded = this._progress.total = 0;
  831 + this._progress.bitrate = 0;
  832 + }
  833 + // Make sure the container objects for the .response() and
  834 + // .progress() methods on the data object are available
  835 + // and reset to their initial state:
  836 + this._initResponseObject(data);
  837 + this._initProgressObject(data);
  838 + data._progress.loaded = data.loaded = data.uploadedBytes || 0;
  839 + data._progress.total = data.total = this._getTotal(data.files) || 1;
  840 + data._progress.bitrate = data.bitrate = 0;
  841 + this._active += 1;
  842 + // Initialize the global progress values:
  843 + this._progress.loaded += data.loaded;
  844 + this._progress.total += data.total;
  845 + },
  846 +
  847 + _onDone: function (result, textStatus, jqXHR, options) {
  848 + var total = options._progress.total,
  849 + response = options._response;
  850 + if (options._progress.loaded < total) {
  851 + // Create a progress event if no final progress event
  852 + // with loaded equaling total has been triggered:
  853 + this._onProgress($.Event('progress', {
  854 + lengthComputable: true,
  855 + loaded: total,
  856 + total: total
  857 + }), options);
  858 + }
  859 + response.result = options.result = result;
  860 + response.textStatus = options.textStatus = textStatus;
  861 + response.jqXHR = options.jqXHR = jqXHR;
  862 + this._trigger('done', null, options);
  863 + },
  864 +
  865 + _onFail: function (jqXHR, textStatus, errorThrown, options) {
  866 + var response = options._response;
  867 + if (options.recalculateProgress) {
  868 + // Remove the failed (error or abort) file upload from
  869 + // the global progress calculation:
  870 + this._progress.loaded -= options._progress.loaded;
  871 + this._progress.total -= options._progress.total;
  872 + }
  873 + response.jqXHR = options.jqXHR = jqXHR;
  874 + response.textStatus = options.textStatus = textStatus;
  875 + response.errorThrown = options.errorThrown = errorThrown;
  876 + this._trigger('fail', null, options);
  877 + },
  878 +
  879 + _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
  880 + // jqXHRorResult, textStatus and jqXHRorError are added to the
  881 + // options object via done and fail callbacks
  882 + this._trigger('always', null, options);
  883 + },
  884 +
  885 + _onSend: function (e, data) {
  886 + if (!data.submit) {
  887 + this._addConvenienceMethods(e, data);
  888 + }
  889 + var that = this,
  890 + jqXHR,
  891 + aborted,
  892 + slot,
  893 + pipe,
  894 + options = that._getAJAXSettings(data),
  895 + send = function () {
  896 + that._sending += 1;
  897 + // Set timer for bitrate progress calculation:
  898 + options._bitrateTimer = new that._BitrateTimer();
  899 + jqXHR = jqXHR || (
  900 + ((aborted || that._trigger(
  901 + 'send',
  902 + $.Event('send', {delegatedEvent: e}),
  903 + options
  904 + ) === false) &&
  905 + that._getXHRPromise(false, options.context, aborted)) ||
  906 + that._chunkedUpload(options) || $.ajax(options)
  907 + ).done(function (result, textStatus, jqXHR) {
  908 + that._onDone(result, textStatus, jqXHR, options);
  909 + }).fail(function (jqXHR, textStatus, errorThrown) {
  910 + that._onFail(jqXHR, textStatus, errorThrown, options);
  911 + }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
  912 + that._onAlways(
  913 + jqXHRorResult,
  914 + textStatus,
  915 + jqXHRorError,
  916 + options
  917 + );
  918 + that._sending -= 1;
  919 + that._active -= 1;
  920 + if (options.limitConcurrentUploads &&
  921 + options.limitConcurrentUploads > that._sending) {
  922 + // Start the next queued upload,
  923 + // that has not been aborted:
  924 + var nextSlot = that._slots.shift();
  925 + while (nextSlot) {
  926 + if (that._getDeferredState(nextSlot) === 'pending') {
  927 + nextSlot.resolve();
  928 + break;
  929 + }
  930 + nextSlot = that._slots.shift();
  931 + }
  932 + }
  933 + if (that._active === 0) {
  934 + // The stop callback is triggered when all uploads have
  935 + // been completed, equivalent to the global ajaxStop event:
  936 + that._trigger('stop');
  937 + }
  938 + });
  939 + return jqXHR;
  940 + };
  941 + this._beforeSend(e, options);
  942 + if (this.options.sequentialUploads ||
  943 + (this.options.limitConcurrentUploads &&
  944 + this.options.limitConcurrentUploads <= this._sending)) {
  945 + if (this.options.limitConcurrentUploads > 1) {
  946 + slot = $.Deferred();
  947 + this._slots.push(slot);
  948 + pipe = slot.pipe(send);
  949 + } else {
  950 + this._sequence = this._sequence.pipe(send, send);
  951 + pipe = this._sequence;
  952 + }
  953 + // Return the piped Promise object, enhanced with an abort method,
  954 + // which is delegated to the jqXHR object of the current upload,
  955 + // and jqXHR callbacks mapped to the equivalent Promise methods:
  956 + pipe.abort = function () {
  957 + aborted = [undefined, 'abort', 'abort'];
  958 + if (!jqXHR) {
  959 + if (slot) {
  960 + slot.rejectWith(options.context, aborted);
  961 + }
  962 + return send();
  963 + }
  964 + return jqXHR.abort();
  965 + };
  966 + return this._enhancePromise(pipe);
  967 + }
  968 + return send();
  969 + },
  970 +
  971 + _onAdd: function (e, data) {
  972 + var that = this,
  973 + result = true,
  974 + options = $.extend({}, this.options, data),
  975 + files = data.files,
  976 + filesLength = files.length,
  977 + limit = options.limitMultiFileUploads,
  978 + limitSize = options.limitMultiFileUploadSize,
  979 + overhead = options.limitMultiFileUploadSizeOverhead,
  980 + batchSize = 0,
  981 + paramName = this._getParamName(options),
  982 + paramNameSet,
  983 + paramNameSlice,
  984 + fileSet,
  985 + i,
  986 + j = 0;
  987 + if (!filesLength) {
  988 + return false;
  989 + }
  990 + if (limitSize && files[0].size === undefined) {
  991 + limitSize = undefined;
  992 + }
  993 + if (!(options.singleFileUploads || limit || limitSize) ||
  994 + !this._isXHRUpload(options)) {
  995 + fileSet = [files];
  996 + paramNameSet = [paramName];
  997 + } else if (!(options.singleFileUploads || limitSize) && limit) {
  998 + fileSet = [];
  999 + paramNameSet = [];
  1000 + for (i = 0; i < filesLength; i += limit) {
  1001 + fileSet.push(files.slice(i, i + limit));
  1002 + paramNameSlice = paramName.slice(i, i + limit);
  1003 + if (!paramNameSlice.length) {
  1004 + paramNameSlice = paramName;
  1005 + }
  1006 + paramNameSet.push(paramNameSlice);
  1007 + }
  1008 + } else if (!options.singleFileUploads && limitSize) {
  1009 + fileSet = [];
  1010 + paramNameSet = [];
  1011 + for (i = 0; i < filesLength; i = i + 1) {
  1012 + batchSize += files[i].size + overhead;
  1013 + if (i + 1 === filesLength ||
  1014 + ((batchSize + files[i + 1].size + overhead) > limitSize) ||
  1015 + (limit && i + 1 - j >= limit)) {
  1016 + fileSet.push(files.slice(j, i + 1));
  1017 + paramNameSlice = paramName.slice(j, i + 1);
  1018 + if (!paramNameSlice.length) {
  1019 + paramNameSlice = paramName;
  1020 + }
  1021 + paramNameSet.push(paramNameSlice);
  1022 + j = i + 1;
  1023 + batchSize = 0;
  1024 + }
  1025 + }
  1026 + } else {
  1027 + paramNameSet = paramName;
  1028 + }
  1029 + data.originalFiles = files;
  1030 + $.each(fileSet || files, function (index, element) {
  1031 + var newData = $.extend({}, data);
  1032 + newData.files = fileSet ? element : [element];
  1033 + newData.paramName = paramNameSet[index];
  1034 + that._initResponseObject(newData);
  1035 + that._initProgressObject(newData);
  1036 + that._addConvenienceMethods(e, newData);
  1037 + result = that._trigger(
  1038 + 'add',
  1039 + $.Event('add', {delegatedEvent: e}),
  1040 + newData
  1041 + );
  1042 + return result;
  1043 + });
  1044 + return result;
  1045 + },
  1046 +
  1047 + _replaceFileInput: function (data) {
  1048 + var input = data.fileInput,
  1049 + inputClone = input.clone(true),
  1050 + restoreFocus = input.is(document.activeElement);
  1051 + // Add a reference for the new cloned file input to the data argument:
  1052 + data.fileInputClone = inputClone;
  1053 + $('<form></form>').append(inputClone)[0].reset();
  1054 + // Detaching allows to insert the fileInput on another form
  1055 + // without loosing the file input value:
  1056 + input.after(inputClone).detach();
  1057 + // If the fileInput had focus before it was detached,
  1058 + // restore focus to the inputClone.
  1059 + if (restoreFocus) {
  1060 + inputClone.focus();
  1061 + }
  1062 + // Avoid memory leaks with the detached file input:
  1063 + $.cleanData(input.unbind('remove'));
  1064 + // Replace the original file input element in the fileInput
  1065 + // elements set with the clone, which has been copied including
  1066 + // event handlers:
  1067 + this.options.fileInput = this.options.fileInput.map(function (i, el) {
  1068 + if (el === input[0]) {
  1069 + return inputClone[0];
  1070 + }
  1071 + return el;
  1072 + });
  1073 + // If the widget has been initialized on the file input itself,
  1074 + // override this.element with the file input clone:
  1075 + if (input[0] === this.element[0]) {
  1076 + this.element = inputClone;
  1077 + }
  1078 + },
  1079 +
  1080 + _handleFileTreeEntry: function (entry, path) {
  1081 + var that = this,
  1082 + dfd = $.Deferred(),
  1083 + errorHandler = function (e) {
  1084 + if (e && !e.entry) {
  1085 + e.entry = entry;
  1086 + }
  1087 + // Since $.when returns immediately if one
  1088 + // Deferred is rejected, we use resolve instead.
  1089 + // This allows valid files and invalid items
  1090 + // to be returned together in one set:
  1091 + dfd.resolve([e]);
  1092 + },
  1093 + successHandler = function (entries) {
  1094 + that._handleFileTreeEntries(
  1095 + entries,
  1096 + path + entry.name + '/'
  1097 + ).done(function (files) {
  1098 + dfd.resolve(files);
  1099 + }).fail(errorHandler);
  1100 + },
  1101 + readEntries = function () {
  1102 + dirReader.readEntries(function (results) {
  1103 + if (!results.length) {
  1104 + successHandler(entries);
  1105 + } else {
  1106 + entries = entries.concat(results);
  1107 + readEntries();
  1108 + }
  1109 + }, errorHandler);
  1110 + },
  1111 + dirReader, entries = [];
  1112 + path = path || '';
  1113 + if (entry.isFile) {
  1114 + if (entry._file) {
  1115 + // Workaround for Chrome bug #149735
  1116 + entry._file.relativePath = path;
  1117 + dfd.resolve(entry._file);
  1118 + } else {
  1119 + entry.file(function (file) {
  1120 + file.relativePath = path;
  1121 + dfd.resolve(file);
  1122 + }, errorHandler);
  1123 + }
  1124 + } else if (entry.isDirectory) {
  1125 + dirReader = entry.createReader();
  1126 + readEntries();
  1127 + } else {
  1128 + // Return an empy list for file system items
  1129 + // other than files or directories:
  1130 + dfd.resolve([]);
  1131 + }
  1132 + return dfd.promise();
  1133 + },
  1134 +
  1135 + _handleFileTreeEntries: function (entries, path) {
  1136 + var that = this;
  1137 + return $.when.apply(
  1138 + $,
  1139 + $.map(entries, function (entry) {
  1140 + return that._handleFileTreeEntry(entry, path);
  1141 + })
  1142 + ).pipe(function () {
  1143 + return Array.prototype.concat.apply(
  1144 + [],
  1145 + arguments
  1146 + );
  1147 + });
  1148 + },
  1149 +
  1150 + _getDroppedFiles: function (dataTransfer) {
  1151 + dataTransfer = dataTransfer || {};
  1152 + var items = dataTransfer.items;
  1153 + if (items && items.length && (items[0].webkitGetAsEntry ||
  1154 + items[0].getAsEntry)) {
  1155 + return this._handleFileTreeEntries(
  1156 + $.map(items, function (item) {
  1157 + var entry;
  1158 + if (item.webkitGetAsEntry) {
  1159 + entry = item.webkitGetAsEntry();
  1160 + if (entry) {
  1161 + // Workaround for Chrome bug #149735:
  1162 + entry._file = item.getAsFile();
  1163 + }
  1164 + return entry;
  1165 + }
  1166 + return item.getAsEntry();
  1167 + })
  1168 + );
  1169 + }
  1170 + return $.Deferred().resolve(
  1171 + $.makeArray(dataTransfer.files)
  1172 + ).promise();
  1173 + },
  1174 +
  1175 + _getSingleFileInputFiles: function (fileInput) {
  1176 + fileInput = $(fileInput);
  1177 + var entries = fileInput.prop('webkitEntries') ||
  1178 + fileInput.prop('entries'),
  1179 + files,
  1180 + value;
  1181 + if (entries && entries.length) {
  1182 + return this._handleFileTreeEntries(entries);
  1183 + }
  1184 + files = $.makeArray(fileInput.prop('files'));
  1185 + if (!files.length) {
  1186 + value = fileInput.prop('value');
  1187 + if (!value) {
  1188 + return $.Deferred().resolve([]).promise();
  1189 + }
  1190 + // If the files property is not available, the browser does not
  1191 + // support the File API and we add a pseudo File object with
  1192 + // the input value as name with path information removed:
  1193 + files = [{name: value.replace(/^.*\\/, '')}];
  1194 + } else if (files[0].name === undefined && files[0].fileName) {
  1195 + // File normalization for Safari 4 and Firefox 3:
  1196 + $.each(files, function (index, file) {
  1197 + file.name = file.fileName;
  1198 + file.size = file.fileSize;
  1199 + });
  1200 + }
  1201 + return $.Deferred().resolve(files).promise();
  1202 + },
  1203 +
  1204 + _getFileInputFiles: function (fileInput) {
  1205 + if (!(fileInput instanceof $) || fileInput.length === 1) {
  1206 + return this._getSingleFileInputFiles(fileInput);
  1207 + }
  1208 + return $.when.apply(
  1209 + $,
  1210 + $.map(fileInput, this._getSingleFileInputFiles)
  1211 + ).pipe(function () {
  1212 + return Array.prototype.concat.apply(
  1213 + [],
  1214 + arguments
  1215 + );
  1216 + });
  1217 + },
  1218 +
  1219 + _onChange: function (e) {
  1220 + var that = this,
  1221 + data = {
  1222 + fileInput: $(e.target),
  1223 + form: $(e.target.form)
  1224 + };
  1225 + this._getFileInputFiles(data.fileInput).always(function (files) {
  1226 + data.files = files;
  1227 + if (that.options.replaceFileInput) {
  1228 + that._replaceFileInput(data);
  1229 + }
  1230 + if (that._trigger(
  1231 + 'change',
  1232 + $.Event('change', {delegatedEvent: e}),
  1233 + data
  1234 + ) !== false) {
  1235 + that._onAdd(e, data);
  1236 + }
  1237 + });
  1238 + },
  1239 +
  1240 + _onPaste: function (e) {
  1241 + var items = e.originalEvent && e.originalEvent.clipboardData &&
  1242 + e.originalEvent.clipboardData.items,
  1243 + data = {files: []};
  1244 + if (items && items.length) {
  1245 + $.each(items, function (index, item) {
  1246 + var file = item.getAsFile && item.getAsFile();
  1247 + if (file) {
  1248 + data.files.push(file);
  1249 + }
  1250 + });
  1251 + if (this._trigger(
  1252 + 'paste',
  1253 + $.Event('paste', {delegatedEvent: e}),
  1254 + data
  1255 + ) !== false) {
  1256 + this._onAdd(e, data);
  1257 + }
  1258 + }
  1259 + },
  1260 +
  1261 + _onDrop: function (e) {
  1262 + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
  1263 + var that = this,
  1264 + dataTransfer = e.dataTransfer,
  1265 + data = {};
  1266 + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
  1267 + e.preventDefault();
  1268 + this._getDroppedFiles(dataTransfer).always(function (files) {
  1269 + data.files = files;
  1270 + if (that._trigger(
  1271 + 'drop',
  1272 + $.Event('drop', {delegatedEvent: e}),
  1273 + data
  1274 + ) !== false) {
  1275 + that._onAdd(e, data);
  1276 + }
  1277 + });
  1278 + }
  1279 + },
  1280 +
  1281 + _onDragOver: getDragHandler('dragover'),
  1282 +
  1283 + _onDragEnter: getDragHandler('dragenter'),
  1284 +
  1285 + _onDragLeave: getDragHandler('dragleave'),
  1286 +
  1287 + _initEventHandlers: function () {
  1288 + if (this._isXHRUpload(this.options)) {
  1289 + this._on(this.options.dropZone, {
  1290 + dragover: this._onDragOver,
  1291 + drop: this._onDrop,
  1292 + // event.preventDefault() on dragenter is required for IE10+:
  1293 + dragenter: this._onDragEnter,
  1294 + // dragleave is not required, but added for completeness:
  1295 + dragleave: this._onDragLeave
  1296 + });
  1297 + this._on(this.options.pasteZone, {
  1298 + paste: this._onPaste
  1299 + });
  1300 + }
  1301 + if ($.support.fileInput) {
  1302 + this._on(this.options.fileInput, {
  1303 + change: this._onChange
  1304 + });
  1305 + }
  1306 + },
  1307 +
  1308 + _destroyEventHandlers: function () {
  1309 + this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
  1310 + this._off(this.options.pasteZone, 'paste');
  1311 + this._off(this.options.fileInput, 'change');
  1312 + },
  1313 +
  1314 + _setOption: function (key, value) {
  1315 + var reinit = $.inArray(key, this._specialOptions) !== -1;
  1316 + if (reinit) {
  1317 + this._destroyEventHandlers();
  1318 + }
  1319 + this._super(key, value);
  1320 + if (reinit) {
  1321 + this._initSpecialOptions();
  1322 + this._initEventHandlers();
  1323 + }
  1324 + },
  1325 +
  1326 + _initSpecialOptions: function () {
  1327 + var options = this.options;
  1328 + if (options.fileInput === undefined) {
  1329 + options.fileInput = this.element.is('input[type="file"]') ?
  1330 + this.element : this.element.find('input[type="file"]');
  1331 + } else if (!(options.fileInput instanceof $)) {
  1332 + options.fileInput = $(options.fileInput);
  1333 + }
  1334 + if (!(options.dropZone instanceof $)) {
  1335 + options.dropZone = $(options.dropZone);
  1336 + }
  1337 + if (!(options.pasteZone instanceof $)) {
  1338 + options.pasteZone = $(options.pasteZone);
  1339 + }
  1340 + },
  1341 +
  1342 + _getRegExp: function (str) {
  1343 + var parts = str.split('/'),
  1344 + modifiers = parts.pop();
  1345 + parts.shift();
  1346 + return new RegExp(parts.join('/'), modifiers);
  1347 + },
  1348 +
  1349 + _isRegExpOption: function (key, value) {
  1350 + return key !== 'url' && $.type(value) === 'string' &&
  1351 + /^\/.*\/[igm]{0,3}$/.test(value);
  1352 + },
  1353 +
  1354 + _initDataAttributes: function () {
  1355 + var that = this,
  1356 + options = this.options,
  1357 + data = this.element.data();
  1358 + // Initialize options set via HTML5 data-attributes:
  1359 + $.each(
  1360 + this.element[0].attributes,
  1361 + function (index, attr) {
  1362 + var key = attr.name.toLowerCase(),
  1363 + value;
  1364 + if (/^data-/.test(key)) {
  1365 + // Convert hyphen-ated key to camelCase:
  1366 + key = key.slice(5).replace(/-[a-z]/g, function (str) {
  1367 + return str.charAt(1).toUpperCase();
  1368 + });
  1369 + value = data[key];
  1370 + if (that._isRegExpOption(key, value)) {
  1371 + value = that._getRegExp(value);
  1372 + }
  1373 + options[key] = value;
  1374 + }
  1375 + }
  1376 + );
  1377 + },
  1378 +
  1379 + _create: function () {
  1380 + this._initDataAttributes();
  1381 + this._initSpecialOptions();
  1382 + this._slots = [];
  1383 + this._sequence = this._getXHRPromise(true);
  1384 + this._sending = this._active = 0;
  1385 + this._initProgressObject(this);
  1386 + this._initEventHandlers();
  1387 + },
  1388 +
  1389 + // This method is exposed to the widget API and allows to query
  1390 + // the number of active uploads:
  1391 + active: function () {
  1392 + return this._active;
  1393 + },
  1394 +
  1395 + // This method is exposed to the widget API and allows to query
  1396 + // the widget upload progress.
  1397 + // It returns an object with loaded, total and bitrate properties
  1398 + // for the running uploads:
  1399 + progress: function () {
  1400 + return this._progress;
  1401 + },
  1402 +
  1403 + // This method is exposed to the widget API and allows adding files
  1404 + // using the fileupload API. The data parameter accepts an object which
  1405 + // must have a files property and can contain additional options:
  1406 + // .fileupload('add', {files: filesList});
  1407 + add: function (data) {
  1408 + var that = this;
  1409 + if (!data || this.options.disabled) {
  1410 + return;
  1411 + }
  1412 + if (data.fileInput && !data.files) {
  1413 + this._getFileInputFiles(data.fileInput).always(function (files) {
  1414 + data.files = files;
  1415 + that._onAdd(null, data);
  1416 + });
  1417 + } else {
  1418 + data.files = $.makeArray(data.files);
  1419 + this._onAdd(null, data);
  1420 + }
  1421 + },
  1422 +
  1423 + // This method is exposed to the widget API and allows sending files
  1424 + // using the fileupload API. The data parameter accepts an object which
  1425 + // must have a files or fileInput property and can contain additional options:
  1426 + // .fileupload('send', {files: filesList});
  1427 + // The method returns a Promise object for the file upload call.
  1428 + send: function (data) {
  1429 + if (data && !this.options.disabled) {
  1430 + if (data.fileInput && !data.files) {
  1431 + var that = this,
  1432 + dfd = $.Deferred(),
  1433 + promise = dfd.promise(),
  1434 + jqXHR,
  1435 + aborted;
  1436 + promise.abort = function () {
  1437 + aborted = true;
  1438 + if (jqXHR) {
  1439 + return jqXHR.abort();
  1440 + }
  1441 + dfd.reject(null, 'abort', 'abort');
  1442 + return promise;
  1443 + };
  1444 + this._getFileInputFiles(data.fileInput).always(
  1445 + function (files) {
  1446 + if (aborted) {
  1447 + return;
  1448 + }
  1449 + if (!files.length) {
  1450 + dfd.reject();
  1451 + return;
  1452 + }
  1453 + data.files = files;
  1454 + jqXHR = that._onSend(null, data);
  1455 + jqXHR.then(
  1456 + function (result, textStatus, jqXHR) {
  1457 + dfd.resolve(result, textStatus, jqXHR);
  1458 + },
  1459 + function (jqXHR, textStatus, errorThrown) {
  1460 + dfd.reject(jqXHR, textStatus, errorThrown);
  1461 + }
  1462 + );
  1463 + }
  1464 + );
  1465 + return this._enhancePromise(promise);
  1466 + }
  1467 + data.files = $.makeArray(data.files);
  1468 + if (data.files.length) {
  1469 + return this._onSend(null, data);
  1470 + }
  1471 + }
  1472 + return this._getXHRPromise(false, data && data.context);
  1473 + }
  1474 +
  1475 + });
  1476 +
  1477 +}));
... ...
common/modules/file/assets/js/jquery.iframe-transport.js 0 → 100644
  1 +/*
  2 + * jQuery Iframe Transport Plugin
  3 + * https://github.com/blueimp/jQuery-File-Upload
  4 + *
  5 + * Copyright 2011, Sebastian Tschan
  6 + * https://blueimp.net
  7 + *
  8 + * Licensed under the MIT license:
  9 + * http://www.opensource.org/licenses/MIT
  10 + */
  11 +
  12 +/* global define, require, window, document */
  13 +
  14 +(function (factory) {
  15 + 'use strict';
  16 + if (typeof define === 'function' && define.amd) {
  17 + // Register as an anonymous AMD module:
  18 + define(['jquery'], factory);
  19 + } else if (typeof exports === 'object') {
  20 + // Node/CommonJS:
  21 + factory(require('jquery'));
  22 + } else {
  23 + // Browser globals:
  24 + factory(window.jQuery);
  25 + }
  26 +}(function ($) {
  27 + 'use strict';
  28 +
  29 + // Helper variable to create unique names for the transport iframes:
  30 + var counter = 0;
  31 +
  32 + // The iframe transport accepts four additional options:
  33 + // options.fileInput: a jQuery collection of file input fields
  34 + // options.paramName: the parameter name for the file form data,
  35 + // overrides the name property of the file input field(s),
  36 + // can be a string or an array of strings.
  37 + // options.formData: an array of objects with name and value properties,
  38 + // equivalent to the return data of .serializeArray(), e.g.:
  39 + // [{name: 'a', value: 1}, {name: 'b', value: 2}]
  40 + // options.initialIframeSrc: the URL of the initial iframe src,
  41 + // by default set to "javascript:false;"
  42 + $.ajaxTransport('iframe', function (options) {
  43 + if (options.async) {
  44 + // javascript:false as initial iframe src
  45 + // prevents warning popups on HTTPS in IE6:
  46 + /*jshint scripturl: true */
  47 + var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
  48 + /*jshint scripturl: false */
  49 + form,
  50 + iframe,
  51 + addParamChar;
  52 + return {
  53 + send: function (_, completeCallback) {
  54 + form = $('<form style="display:none;"></form>');
  55 + form.attr('accept-charset', options.formAcceptCharset);
  56 + addParamChar = /\?/.test(options.url) ? '&' : '?';
  57 + // XDomainRequest only supports GET and POST:
  58 + if (options.type === 'DELETE') {
  59 + options.url = options.url + addParamChar + '_method=DELETE';
  60 + options.type = 'POST';
  61 + } else if (options.type === 'PUT') {
  62 + options.url = options.url + addParamChar + '_method=PUT';
  63 + options.type = 'POST';
  64 + } else if (options.type === 'PATCH') {
  65 + options.url = options.url + addParamChar + '_method=PATCH';
  66 + options.type = 'POST';
  67 + }
  68 + // IE versions below IE8 cannot set the name property of
  69 + // elements that have already been added to the DOM,
  70 + // so we set the name along with the iframe HTML markup:
  71 + counter += 1;
  72 + iframe = $(
  73 + '<iframe src="' + initialIframeSrc +
  74 + '" name="iframe-transport-' + counter + '"></iframe>'
  75 + ).bind('load', function () {
  76 + var fileInputClones,
  77 + paramNames = $.isArray(options.paramName) ?
  78 + options.paramName : [options.paramName];
  79 + iframe
  80 + .unbind('load')
  81 + .bind('load', function () {
  82 + var response;
  83 + // Wrap in a try/catch block to catch exceptions thrown
  84 + // when trying to access cross-domain iframe contents:
  85 + try {
  86 + response = iframe.contents();
  87 + // Google Chrome and Firefox do not throw an
  88 + // exception when calling iframe.contents() on
  89 + // cross-domain requests, so we unify the response:
  90 + if (!response.length || !response[0].firstChild) {
  91 + throw new Error();
  92 + }
  93 + } catch (e) {
  94 + response = undefined;
  95 + }
  96 + // The complete callback returns the
  97 + // iframe content document as response object:
  98 + completeCallback(
  99 + 200,
  100 + 'success',
  101 + {'iframe': response}
  102 + );
  103 + // Fix for IE endless progress bar activity bug
  104 + // (happens on form submits to iframe targets):
  105 + $('<iframe src="' + initialIframeSrc + '"></iframe>')
  106 + .appendTo(form);
  107 + window.setTimeout(function () {
  108 + // Removing the form in a setTimeout call
  109 + // allows Chrome's developer tools to display
  110 + // the response result
  111 + form.remove();
  112 + }, 0);
  113 + });
  114 + form
  115 + .prop('target', iframe.prop('name'))
  116 + .prop('action', options.url)
  117 + .prop('method', options.type);
  118 + if (options.formData) {
  119 + $.each(options.formData, function (index, field) {
  120 + $('<input type="hidden"/>')
  121 + .prop('name', field.name)
  122 + .val(field.value)
  123 + .appendTo(form);
  124 + });
  125 + }
  126 + if (options.fileInput && options.fileInput.length &&
  127 + options.type === 'POST') {
  128 + fileInputClones = options.fileInput.clone();
  129 + // Insert a clone for each file input field:
  130 + options.fileInput.after(function (index) {
  131 + return fileInputClones[index];
  132 + });
  133 + if (options.paramName) {
  134 + options.fileInput.each(function (index) {
  135 + $(this).prop(
  136 + 'name',
  137 + paramNames[index] || options.paramName
  138 + );
  139 + });
  140 + }
  141 + // Appending the file input fields to the hidden form
  142 + // removes them from their original location:
  143 + form
  144 + .append(options.fileInput)
  145 + .prop('enctype', 'multipart/form-data')
  146 + // enctype must be set as encoding for IE:
  147 + .prop('encoding', 'multipart/form-data');
  148 + // Remove the HTML5 form attribute from the input(s):
  149 + options.fileInput.removeAttr('form');
  150 + }
  151 + form.submit();
  152 + // Insert the file input fields at their original location
  153 + // by replacing the clones with the originals:
  154 + if (fileInputClones && fileInputClones.length) {
  155 + options.fileInput.each(function (index, input) {
  156 + var clone = $(fileInputClones[index]);
  157 + // Restore the original name and form properties:
  158 + $(input)
  159 + .prop('name', clone.prop('name'))
  160 + .attr('form', clone.attr('form'));
  161 + clone.replaceWith(input);
  162 + });
  163 + }
  164 + });
  165 + form.append(iframe).appendTo(document.body);
  166 + },
  167 + abort: function () {
  168 + if (iframe) {
  169 + // javascript:false as iframe src aborts the request
  170 + // and prevents warning popups on HTTPS in IE6.
  171 + // concat is used to avoid the "Script URL" JSLint error:
  172 + iframe
  173 + .unbind('load')
  174 + .prop('src', initialIframeSrc);
  175 + }
  176 + if (form) {
  177 + form.remove();
  178 + }
  179 + }
  180 + };
  181 + }
  182 + });
  183 +
  184 + // The iframe transport returns the iframe content document as response.
  185 + // The following adds converters from iframe to text, json, html, xml
  186 + // and script.
  187 + // Please note that the Content-Type for JSON responses has to be text/plain
  188 + // or text/html, if the browser doesn't include application/json in the
  189 + // Accept header, else IE will show a download dialog.
  190 + // The Content-Type for XML responses on the other hand has to be always
  191 + // application/xml or text/xml, so IE properly parses the XML response.
  192 + // See also
  193 + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
  194 + $.ajaxSetup({
  195 + converters: {
  196 + 'iframe text': function (iframe) {
  197 + return iframe && $(iframe[0].body).text();
  198 + },
  199 + 'iframe json': function (iframe) {
  200 + return iframe && $.parseJSON($(iframe[0].body).text());
  201 + },
  202 + 'iframe html': function (iframe) {
  203 + return iframe && $(iframe[0].body).html();
  204 + },
  205 + 'iframe xml': function (iframe) {
  206 + var xmlDoc = iframe && iframe[0];
  207 + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
  208 + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
  209 + $(xmlDoc.body).html());
  210 + },
  211 + 'iframe script': function (iframe) {
  212 + return iframe && $.globalEval($(iframe[0].body).text());
  213 + }
  214 + }
  215 + });
  216 +
  217 +}));
... ...
common/modules/file/assets/js/vendor/jquery.ui.widget.js 0 → 100644
  1 +/*! jQuery UI - v1.11.4+CommonJS - 2015-08-28
  2 +* http://jqueryui.com
  3 +* Includes: widget.js
  4 +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
  5 +
  6 +(function( factory ) {
  7 + if ( typeof define === "function" && define.amd ) {
  8 +
  9 + // AMD. Register as an anonymous module.
  10 + define([ "jquery" ], factory );
  11 +
  12 + } else if ( typeof exports === "object" ) {
  13 +
  14 + // Node/CommonJS
  15 + factory( require( "jquery" ) );
  16 +
  17 + } else {
  18 +
  19 + // Browser globals
  20 + factory( jQuery );
  21 + }
  22 +}(function( $ ) {
  23 +/*!
  24 + * jQuery UI Widget 1.11.4
  25 + * http://jqueryui.com
  26 + *
  27 + * Copyright jQuery Foundation and other contributors
  28 + * Released under the MIT license.
  29 + * http://jquery.org/license
  30 + *
  31 + * http://api.jqueryui.com/jQuery.widget/
  32 + */
  33 +
  34 +
  35 +var widget_uuid = 0,
  36 + widget_slice = Array.prototype.slice;
  37 +
  38 +$.cleanData = (function( orig ) {
  39 + return function( elems ) {
  40 + var events, elem, i;
  41 + for ( i = 0; (elem = elems[i]) != null; i++ ) {
  42 + try {
  43 +
  44 + // Only trigger remove when necessary to save time
  45 + events = $._data( elem, "events" );
  46 + if ( events && events.remove ) {
  47 + $( elem ).triggerHandler( "remove" );
  48 + }
  49 +
  50 + // http://bugs.jquery.com/ticket/8235
  51 + } catch ( e ) {}
  52 + }
  53 + orig( elems );
  54 + };
  55 +})( $.cleanData );
  56 +
  57 +$.widget = function( name, base, prototype ) {
  58 + var fullName, existingConstructor, constructor, basePrototype,
  59 + // proxiedPrototype allows the provided prototype to remain unmodified
  60 + // so that it can be used as a mixin for multiple widgets (#8876)
  61 + proxiedPrototype = {},
  62 + namespace = name.split( "." )[ 0 ];
  63 +
  64 + name = name.split( "." )[ 1 ];
  65 + fullName = namespace + "-" + name;
  66 +
  67 + if ( !prototype ) {
  68 + prototype = base;
  69 + base = $.Widget;
  70 + }
  71 +
  72 + // create selector for plugin
  73 + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
  74 + return !!$.data( elem, fullName );
  75 + };
  76 +
  77 + $[ namespace ] = $[ namespace ] || {};
  78 + existingConstructor = $[ namespace ][ name ];
  79 + constructor = $[ namespace ][ name ] = function( options, element ) {
  80 + // allow instantiation without "new" keyword
  81 + if ( !this._createWidget ) {
  82 + return new constructor( options, element );
  83 + }
  84 +
  85 + // allow instantiation without initializing for simple inheritance
  86 + // must use "new" keyword (the code above always passes args)
  87 + if ( arguments.length ) {
  88 + this._createWidget( options, element );
  89 + }
  90 + };
  91 + // extend with the existing constructor to carry over any static properties
  92 + $.extend( constructor, existingConstructor, {
  93 + version: prototype.version,
  94 + // copy the object used to create the prototype in case we need to
  95 + // redefine the widget later
  96 + _proto: $.extend( {}, prototype ),
  97 + // track widgets that inherit from this widget in case this widget is
  98 + // redefined after a widget inherits from it
  99 + _childConstructors: []
  100 + });
  101 +
  102 + basePrototype = new base();
  103 + // we need to make the options hash a property directly on the new instance
  104 + // otherwise we'll modify the options hash on the prototype that we're
  105 + // inheriting from
  106 + basePrototype.options = $.widget.extend( {}, basePrototype.options );
  107 + $.each( prototype, function( prop, value ) {
  108 + if ( !$.isFunction( value ) ) {
  109 + proxiedPrototype[ prop ] = value;
  110 + return;
  111 + }
  112 + proxiedPrototype[ prop ] = (function() {
  113 + var _super = function() {
  114 + return base.prototype[ prop ].apply( this, arguments );
  115 + },
  116 + _superApply = function( args ) {
  117 + return base.prototype[ prop ].apply( this, args );
  118 + };
  119 + return function() {
  120 + var __super = this._super,
  121 + __superApply = this._superApply,
  122 + returnValue;
  123 +
  124 + this._super = _super;
  125 + this._superApply = _superApply;
  126 +
  127 + returnValue = value.apply( this, arguments );
  128 +
  129 + this._super = __super;
  130 + this._superApply = __superApply;
  131 +
  132 + return returnValue;
  133 + };
  134 + })();
  135 + });
  136 + constructor.prototype = $.widget.extend( basePrototype, {
  137 + // TODO: remove support for widgetEventPrefix
  138 + // always use the name + a colon as the prefix, e.g., draggable:start
  139 + // don't prefix for widgets that aren't DOM-based
  140 + widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
  141 + }, proxiedPrototype, {
  142 + constructor: constructor,
  143 + namespace: namespace,
  144 + widgetName: name,
  145 + widgetFullName: fullName
  146 + });
  147 +
  148 + // If this widget is being redefined then we need to find all widgets that
  149 + // are inheriting from it and redefine all of them so that they inherit from
  150 + // the new version of this widget. We're essentially trying to replace one
  151 + // level in the prototype chain.
  152 + if ( existingConstructor ) {
  153 + $.each( existingConstructor._childConstructors, function( i, child ) {
  154 + var childPrototype = child.prototype;
  155 +
  156 + // redefine the child widget using the same prototype that was
  157 + // originally used, but inherit from the new version of the base
  158 + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
  159 + });
  160 + // remove the list of existing child constructors from the old constructor
  161 + // so the old child constructors can be garbage collected
  162 + delete existingConstructor._childConstructors;
  163 + } else {
  164 + base._childConstructors.push( constructor );
  165 + }
  166 +
  167 + $.widget.bridge( name, constructor );
  168 +
  169 + return constructor;
  170 +};
  171 +
  172 +$.widget.extend = function( target ) {
  173 + var input = widget_slice.call( arguments, 1 ),
  174 + inputIndex = 0,
  175 + inputLength = input.length,
  176 + key,
  177 + value;
  178 + for ( ; inputIndex < inputLength; inputIndex++ ) {
  179 + for ( key in input[ inputIndex ] ) {
  180 + value = input[ inputIndex ][ key ];
  181 + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
  182 + // Clone objects
  183 + if ( $.isPlainObject( value ) ) {
  184 + target[ key ] = $.isPlainObject( target[ key ] ) ?
  185 + $.widget.extend( {}, target[ key ], value ) :
  186 + // Don't extend strings, arrays, etc. with objects
  187 + $.widget.extend( {}, value );
  188 + // Copy everything else by reference
  189 + } else {
  190 + target[ key ] = value;
  191 + }
  192 + }
  193 + }
  194 + }
  195 + return target;
  196 +};
  197 +
  198 +$.widget.bridge = function( name, object ) {
  199 + var fullName = object.prototype.widgetFullName || name;
  200 + $.fn[ name ] = function( options ) {
  201 + var isMethodCall = typeof options === "string",
  202 + args = widget_slice.call( arguments, 1 ),
  203 + returnValue = this;
  204 +
  205 + if ( isMethodCall ) {
  206 + this.each(function() {
  207 + var methodValue,
  208 + instance = $.data( this, fullName );
  209 + if ( options === "instance" ) {
  210 + returnValue = instance;
  211 + return false;
  212 + }
  213 + if ( !instance ) {
  214 + return $.error( "cannot call methods on " + name + " prior to initialization; " +
  215 + "attempted to call method '" + options + "'" );
  216 + }
  217 + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
  218 + return $.error( "no such method '" + options + "' for " + name + " widget instance" );
  219 + }
  220 + methodValue = instance[ options ].apply( instance, args );
  221 + if ( methodValue !== instance && methodValue !== undefined ) {
  222 + returnValue = methodValue && methodValue.jquery ?
  223 + returnValue.pushStack( methodValue.get() ) :
  224 + methodValue;
  225 + return false;
  226 + }
  227 + });
  228 + } else {
  229 +
  230 + // Allow multiple hashes to be passed on init
  231 + if ( args.length ) {
  232 + options = $.widget.extend.apply( null, [ options ].concat(args) );
  233 + }
  234 +
  235 + this.each(function() {
  236 + var instance = $.data( this, fullName );
  237 + if ( instance ) {
  238 + instance.option( options || {} );
  239 + if ( instance._init ) {
  240 + instance._init();
  241 + }
  242 + } else {
  243 + $.data( this, fullName, new object( options, this ) );
  244 + }
  245 + });
  246 + }
  247 +
  248 + return returnValue;
  249 + };
  250 +};
  251 +
  252 +$.Widget = function( /* options, element */ ) {};
  253 +$.Widget._childConstructors = [];
  254 +
  255 +$.Widget.prototype = {
  256 + widgetName: "widget",
  257 + widgetEventPrefix: "",
  258 + defaultElement: "<div>",
  259 + options: {
  260 + disabled: false,
  261 +
  262 + // callbacks
  263 + create: null
  264 + },
  265 + _createWidget: function( options, element ) {
  266 + element = $( element || this.defaultElement || this )[ 0 ];
  267 + this.element = $( element );
  268 + this.uuid = widget_uuid++;
  269 + this.eventNamespace = "." + this.widgetName + this.uuid;
  270 +
  271 + this.bindings = $();
  272 + this.hoverable = $();
  273 + this.focusable = $();
  274 +
  275 + if ( element !== this ) {
  276 + $.data( element, this.widgetFullName, this );
  277 + this._on( true, this.element, {
  278 + remove: function( event ) {
  279 + if ( event.target === element ) {
  280 + this.destroy();
  281 + }
  282 + }
  283 + });
  284 + this.document = $( element.style ?
  285 + // element within the document
  286 + element.ownerDocument :
  287 + // element is window or document
  288 + element.document || element );
  289 + this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
  290 + }
  291 +
  292 + this.options = $.widget.extend( {},
  293 + this.options,
  294 + this._getCreateOptions(),
  295 + options );
  296 +
  297 + this._create();
  298 + this._trigger( "create", null, this._getCreateEventData() );
  299 + this._init();
  300 + },
  301 + _getCreateOptions: $.noop,
  302 + _getCreateEventData: $.noop,
  303 + _create: $.noop,
  304 + _init: $.noop,
  305 +
  306 + destroy: function() {
  307 + this._destroy();
  308 + // we can probably remove the unbind calls in 2.0
  309 + // all event bindings should go through this._on()
  310 + this.element
  311 + .unbind( this.eventNamespace )
  312 + .removeData( this.widgetFullName )
  313 + // support: jquery <1.6.3
  314 + // http://bugs.jquery.com/ticket/9413
  315 + .removeData( $.camelCase( this.widgetFullName ) );
  316 + this.widget()
  317 + .unbind( this.eventNamespace )
  318 + .removeAttr( "aria-disabled" )
  319 + .removeClass(
  320 + this.widgetFullName + "-disabled " +
  321 + "ui-state-disabled" );
  322 +
  323 + // clean up events and states
  324 + this.bindings.unbind( this.eventNamespace );
  325 + this.hoverable.removeClass( "ui-state-hover" );
  326 + this.focusable.removeClass( "ui-state-focus" );
  327 + },
  328 + _destroy: $.noop,
  329 +
  330 + widget: function() {
  331 + return this.element;
  332 + },
  333 +
  334 + option: function( key, value ) {
  335 + var options = key,
  336 + parts,
  337 + curOption,
  338 + i;
  339 +
  340 + if ( arguments.length === 0 ) {
  341 + // don't return a reference to the internal hash
  342 + return $.widget.extend( {}, this.options );
  343 + }
  344 +
  345 + if ( typeof key === "string" ) {
  346 + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  347 + options = {};
  348 + parts = key.split( "." );
  349 + key = parts.shift();
  350 + if ( parts.length ) {
  351 + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  352 + for ( i = 0; i < parts.length - 1; i++ ) {
  353 + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  354 + curOption = curOption[ parts[ i ] ];
  355 + }
  356 + key = parts.pop();
  357 + if ( arguments.length === 1 ) {
  358 + return curOption[ key ] === undefined ? null : curOption[ key ];
  359 + }
  360 + curOption[ key ] = value;
  361 + } else {
  362 + if ( arguments.length === 1 ) {
  363 + return this.options[ key ] === undefined ? null : this.options[ key ];
  364 + }
  365 + options[ key ] = value;
  366 + }
  367 + }
  368 +
  369 + this._setOptions( options );
  370 +
  371 + return this;
  372 + },
  373 + _setOptions: function( options ) {
  374 + var key;
  375 +
  376 + for ( key in options ) {
  377 + this._setOption( key, options[ key ] );
  378 + }
  379 +
  380 + return this;
  381 + },
  382 + _setOption: function( key, value ) {
  383 + this.options[ key ] = value;
  384 +
  385 + if ( key === "disabled" ) {
  386 + this.widget()
  387 + .toggleClass( this.widgetFullName + "-disabled", !!value );
  388 +
  389 + // If the widget is becoming disabled, then nothing is interactive
  390 + if ( value ) {
  391 + this.hoverable.removeClass( "ui-state-hover" );
  392 + this.focusable.removeClass( "ui-state-focus" );
  393 + }
  394 + }
  395 +
  396 + return this;
  397 + },
  398 +
  399 + enable: function() {
  400 + return this._setOptions({ disabled: false });
  401 + },
  402 + disable: function() {
  403 + return this._setOptions({ disabled: true });
  404 + },
  405 +
  406 + _on: function( suppressDisabledCheck, element, handlers ) {
  407 + var delegateElement,
  408 + instance = this;
  409 +
  410 + // no suppressDisabledCheck flag, shuffle arguments
  411 + if ( typeof suppressDisabledCheck !== "boolean" ) {
  412 + handlers = element;
  413 + element = suppressDisabledCheck;
  414 + suppressDisabledCheck = false;
  415 + }
  416 +
  417 + // no element argument, shuffle and use this.element
  418 + if ( !handlers ) {
  419 + handlers = element;
  420 + element = this.element;
  421 + delegateElement = this.widget();
  422 + } else {
  423 + element = delegateElement = $( element );
  424 + this.bindings = this.bindings.add( element );
  425 + }
  426 +
  427 + $.each( handlers, function( event, handler ) {
  428 + function handlerProxy() {
  429 + // allow widgets to customize the disabled handling
  430 + // - disabled as an array instead of boolean
  431 + // - disabled class as method for disabling individual parts
  432 + if ( !suppressDisabledCheck &&
  433 + ( instance.options.disabled === true ||
  434 + $( this ).hasClass( "ui-state-disabled" ) ) ) {
  435 + return;
  436 + }
  437 + return ( typeof handler === "string" ? instance[ handler ] : handler )
  438 + .apply( instance, arguments );
  439 + }
  440 +
  441 + // copy the guid so direct unbinding works
  442 + if ( typeof handler !== "string" ) {
  443 + handlerProxy.guid = handler.guid =
  444 + handler.guid || handlerProxy.guid || $.guid++;
  445 + }
  446 +
  447 + var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
  448 + eventName = match[1] + instance.eventNamespace,
  449 + selector = match[2];
  450 + if ( selector ) {
  451 + delegateElement.delegate( selector, eventName, handlerProxy );
  452 + } else {
  453 + element.bind( eventName, handlerProxy );
  454 + }
  455 + });
  456 + },
  457 +
  458 + _off: function( element, eventName ) {
  459 + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
  460 + this.eventNamespace;
  461 + element.unbind( eventName ).undelegate( eventName );
  462 +
  463 + // Clear the stack to avoid memory leaks (#10056)
  464 + this.bindings = $( this.bindings.not( element ).get() );
  465 + this.focusable = $( this.focusable.not( element ).get() );
  466 + this.hoverable = $( this.hoverable.not( element ).get() );
  467 + },
  468 +
  469 + _delay: function( handler, delay ) {
  470 + function handlerProxy() {
  471 + return ( typeof handler === "string" ? instance[ handler ] : handler )
  472 + .apply( instance, arguments );
  473 + }
  474 + var instance = this;
  475 + return setTimeout( handlerProxy, delay || 0 );
  476 + },
  477 +
  478 + _hoverable: function( element ) {
  479 + this.hoverable = this.hoverable.add( element );
  480 + this._on( element, {
  481 + mouseenter: function( event ) {
  482 + $( event.currentTarget ).addClass( "ui-state-hover" );
  483 + },
  484 + mouseleave: function( event ) {
  485 + $( event.currentTarget ).removeClass( "ui-state-hover" );
  486 + }
  487 + });
  488 + },
  489 +
  490 + _focusable: function( element ) {
  491 + this.focusable = this.focusable.add( element );
  492 + this._on( element, {
  493 + focusin: function( event ) {
  494 + $( event.currentTarget ).addClass( "ui-state-focus" );
  495 + },
  496 + focusout: function( event ) {
  497 + $( event.currentTarget ).removeClass( "ui-state-focus" );
  498 + }
  499 + });
  500 + },
  501 +
  502 + _trigger: function( type, event, data ) {
  503 + var prop, orig,
  504 + callback = this.options[ type ];
  505 +
  506 + data = data || {};
  507 + event = $.Event( event );
  508 + event.type = ( type === this.widgetEventPrefix ?
  509 + type :
  510 + this.widgetEventPrefix + type ).toLowerCase();
  511 + // the original event may come from any element
  512 + // so we need to reset the target on the new event
  513 + event.target = this.element[ 0 ];
  514 +
  515 + // copy original event properties over to the new event
  516 + orig = event.originalEvent;
  517 + if ( orig ) {
  518 + for ( prop in orig ) {
  519 + if ( !( prop in event ) ) {
  520 + event[ prop ] = orig[ prop ];
  521 + }
  522 + }
  523 + }
  524 +
  525 + this.element.trigger( event, data );
  526 + return !( $.isFunction( callback ) &&
  527 + callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
  528 + event.isDefaultPrevented() );
  529 + }
  530 +};
  531 +
  532 +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  533 + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  534 + if ( typeof options === "string" ) {
  535 + options = { effect: options };
  536 + }
  537 + var hasOptions,
  538 + effectName = !options ?
  539 + method :
  540 + options === true || typeof options === "number" ?
  541 + defaultEffect :
  542 + options.effect || defaultEffect;
  543 + options = options || {};
  544 + if ( typeof options === "number" ) {
  545 + options = { duration: options };
  546 + }
  547 + hasOptions = !$.isEmptyObject( options );
  548 + options.complete = callback;
  549 + if ( options.delay ) {
  550 + element.delay( options.delay );
  551 + }
  552 + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  553 + element[ method ]( options );
  554 + } else if ( effectName !== method && element[ effectName ] ) {
  555 + element[ effectName ]( options.duration, options.easing, callback );
  556 + } else {
  557 + element.queue(function( next ) {
  558 + $( this )[ method ]();
  559 + if ( callback ) {
  560 + callback.call( element[ 0 ] );
  561 + }
  562 + next();
  563 + });
  564 + }
  565 + };
  566 +});
  567 +
  568 +var widget = $.widget;
  569 +
  570 +
  571 +
  572 +}));
... ...
common/modules/file/behaviors/ShowImage.php 0 → 100644
  1 +<?php
  2 +
  3 +namespace common\behaviors;
  4 +
  5 +use yii;
  6 +use yii\base\Behavior;
  7 +
  8 +class ShowImage extends Behavior
  9 +{
  10 + function minImg($dir, $width, $height=null){
  11 + if(empty($dir)){
  12 + return $dir;
  13 + }
  14 +
  15 + if($width=='original'){
  16 + $preg = '/\/(.[^\/]*)$/';
  17 + preg_match('/\.(.[^.]*)$/', $dir, $type);
  18 + $row = preg_replace( $preg, '/original.'.$type[1], $dir);
  19 + } else {
  20 + $preg = '/\/(.[^\/]*)$/';
  21 + preg_match('/\.(.[^.]*)$/', $dir, $type);
  22 + $row = preg_replace( $preg, '/'.$width.'x'.$height.'.'.$type[1], $dir);
  23 + }
  24 +
  25 + return $row;
  26 +
  27 +
  28 + }
  29 +
  30 + function ShowGallery($array){
  31 +
  32 + $gallery = explode(',', $array );
  33 + if(is_array($gallery)){
  34 + array_splice($gallery,-1);
  35 + return $gallery;
  36 + } else {
  37 + return [];
  38 + }
  39 +
  40 + }
  41 +
  42 +
  43 +}
0 44 \ No newline at end of file
... ...
common/modules/file/controllers/UploaderController.php
... ... @@ -9,7 +9,7 @@ namespace common\modules\file\controllers;
9 9 use Yii;
10 10 use yii\helpers\ArrayHelper;
11 11 use yii\web\UploadedFile;
12   -use common\models\ImageSizerForm;
  12 +use common\modules\file\models\ImageSizerForm;
13 13 use yii\web\Controller;
14 14 use Imagine\Gd\Imagine;
15 15 use Imagine\Image\Box;
... ... @@ -51,7 +51,7 @@ class UploaderController extends Controller {
51 51 $e1_height = $height/$width;
52 52  
53 53  
54   -// if($this->isBigger($width,$height,$w,$h)){
  54 +
55 55 if($e_width<$e1_width){
56 56  
57 57 $new_width = $width*($e_width/$e1_width);
... ... @@ -69,10 +69,6 @@ class UploaderController extends Controller {
69 69 }
70 70  
71 71  
72   -// } else {
73   -// $img->save($imageAliasSave, array('flatten' => false));
74   -// return true;
75   -// }
76 72  
77 73  
78 74 Image::crop($imageAlias, $width, $height,[$x,$y])
... ... @@ -123,9 +119,6 @@ class UploaderController extends Controller {
123 119 $model = new $object;
124 120 $model = $model->findOne($request['id']);
125 121 $model->$request['field'] = $request['new_url'];
126   -// print_r($model->validate());
127   -// print_r($model->getErrors());
128   -// die(var_dump($model->save()));
129 122 $model->save();
130 123 }
131 124 }
... ...
common/modules/file/models/ImageSizerForm.php 0 → 100755
  1 +<?php
  2 +namespace common\modules\file\models;
  3 +
  4 +use yii\base\Model;
  5 +use yii\web\UploadedFile;
  6 +
  7 +/**
  8 + * UploadForm is the model behind the upload form.
  9 + */
  10 +class ImageSizerForm extends Model
  11 +{
  12 + /**
  13 + * @var UploadedFile file attribute
  14 + */
  15 + public $file;
  16 + public $width;
  17 + public $height;
  18 + public $field;
  19 + public $model;
  20 + public $form;
  21 + public $multi;
  22 + public $old_img;
  23 + public $img;
  24 + public $price_list;
  25 +
  26 + /**
  27 + * @return array the validation rules.
  28 + */
  29 + public function rules()
  30 + {
  31 + return [
  32 + [['width', 'height', 'multi'], 'integer'],
  33 + [['field', 'multi','old_img'], 'string', 'max' => 255],
  34 + [['model', 'form',], 'string'],
  35 + [['file','img','price_list'], 'file'],
  36 + ];
  37 + }
  38 +}
0 39 \ No newline at end of file
... ...
common/modules/file/widgets/ImageUploader.php 0 → 100755
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: vitaliy
  5 + * Date: 05.10.15
  6 + * Time: 16:18
  7 + */
  8 +
  9 +namespace common\modules\file\widgets;
  10 +use yii\base\Widget;
  11 +
  12 +
  13 +class ImageUploader extends Widget
  14 +{
  15 + public $height = 0;
  16 + public $width = 0;
  17 + public $field;
  18 + public $file;
  19 + public $model;
  20 + public $multi = false;
  21 + public $gallery;
  22 + public $size;
  23 + public $name = 'Add file...';
  24 + public $remover = false;
  25 +
  26 + public function init(){
  27 +
  28 + parent::init();
  29 +
  30 + }
  31 +
  32 +
  33 + public function run()
  34 + {
  35 +
  36 + return $this->render('image_sizer',
  37 + [
  38 + 'model'=>$this->model,
  39 + 'size' => $this->size,
  40 + 'field' => $this->field,
  41 + 'height' => $this->height,
  42 + 'width' => $this->width,
  43 + 'multi' => $this->multi,
  44 + 'name' => $this->name,
  45 + 'remover' => $this->remover
  46 + ]);
  47 +
  48 + }
  49 +
  50 + public function getGallery(){
  51 + if($this->gallery){
  52 + $array = explode(",", $this->gallery);
  53 + if(count($array) > 1){
  54 + array_pop($array);
  55 + }
  56 + return $array;
  57 + } else {
  58 + return array();
  59 + }
  60 +
  61 + }
  62 +
  63 +}
0 64 \ No newline at end of file
... ...
common/modules/file/widgets/views/_gallery_item.php 0 → 100755
  1 +<?php
  2 +use yii\helpers\Html;
  3 +
  4 +?>
  5 +
  6 +<div class="gallery_image">
  7 + <?= Html::img($item['image'])?>
  8 + <span data-url="<?=$item['image']?>" title="удалить изображение" class="glyphicon glyphicon-trash delete-gallery-item"></span>
  9 +</div>
0 10 \ No newline at end of file
... ...
common/modules/file/widgets/views/image_sizer.php 0 → 100755
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: vitaliy
  5 + * Date: 05.10.15
  6 + * Time: 16:20
  7 + */
  8 +use yii\helpers\Html;
  9 +
  10 +$id = $model->tableSchema->primaryKey;
  11 +
  12 +?>
  13 +<div class="file-uploader-block">
  14 + <?php if(!$multi):?>
  15 +
  16 +
  17 + <?= Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
  18 + <input type="hidden" id="<?=$field?>_old_img" name="ImageSizerForm[old_img]" value="<?=$model->$field?>"/>
  19 + <input type="hidden" id="<?=$field?>_new_img" name="ImageSizerForm[new_img]" value=""/>
  20 + <input type="hidden" id="<?=$field?>_row_id" name="ImageSizerForm[new_img]" value="<?=$model->$id?>"/>
  21 + <div class="tst">
  22 + <div id="<?= $field?>_img_block" class="admin-avatar-pattern-wr">
  23 + <div class="admin-avatar-pattern">
  24 + <?php
  25 + if($remover && $model->$field) {
  26 + ?>
  27 + <span id="<?=$field?>_remove_img" class="remover_image" style="left:<?=$size[0]['width']?>px"><img src="/images/delete-ico.png" alt=""></span>
  28 + <?php
  29 + }
  30 + ?>
  31 + <?= $model->$field ? Html::img($model->$field): '' ?>
  32 + </div>
  33 + </div>
  34 + </div>
  35 +
  36 + <span class="btn btn-success fileinput-button uploader-button">
  37 +<!-- <i class="glyphicon glyphicon-plus"></i>-->
  38 + <span><?=$name?></span>
  39 +
  40 + <?= Html::activeFileInput( new \common\models\ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo')]);?>
  41 + </span>
  42 +
  43 + <script>
  44 + $(function()
  45 + {
  46 +
  47 + $("#<?= $field?>").fileupload(
  48 + {
  49 + dataType : 'json', formData : {size : '<?= json_encode($size)?>'},
  50 + done : function(e, data)
  51 + {
  52 + if($("#<?=$field?>_buttons_block").length)
  53 + {
  54 + $("#<?=$field?>_buttons_block").remove()
  55 + }
  56 +
  57 + $("#<?= $field?>").parent().prev().find('.admin-ava-wr').remove()
  58 +
  59 + var host = window.location.host.toString();
  60 + var img = '<div class="admin-ava-wr">' +
  61 + <?php
  62 + if($remover) {
  63 + echo "'<span id=\"" . $field . "_remove_img\" class=\"remover_image\" style=\"left:{$size[0]['width']}px\"><img src=\"/images/delete-ico.png\" alt=\"\"></span>'+";
  64 + }
  65 + ?>
  66 + '<img style="vertical-align: middle; width: <?=$width?>px; height: <?=$height?>px;" src="http://' + host + data.result.link + '"></div>' + '<div id="<?=$field?>_buttons_block">' + '<button type="button" id="<?=$field?>_save_img" class="btn btn-success img-action-buttons" >Сохранить</button>' + '<button type="button" id="<?=$field?>_remove_img" class="btn btn-danger img-action-buttons" >Отмена</button>' + '</div>';
  67 + var block = $("#<?= $field?>_img_block .admin-avatar-pattern");
  68 + block.find('img').remove();
  69 + block.append(img);
  70 + block.parents('.file-uploader-block').parent().addClass('success_download');
  71 + $("#<?=$field?>_picture_link").val(data.result.link);
  72 + $("#<?=$field?>_new_img").val(data.result.link);
  73 + }
  74 + }
  75 + );
  76 +
  77 + $('body').on(
  78 + 'click', '#<?=$field?>_save_img', function()
  79 + {
  80 + $("#<?= $field?>_img_block").parent().parent().parent()
  81 + .removeClass('success_download')
  82 +
  83 + $("#<?=$field?>_buttons_block").remove();
  84 + var old_url = $('#<?=$field?>_old_img').val();
  85 + var new_url = $('#<?=$field?>_new_img').val();
  86 + var model = '<?=str_replace('\\', '-', $model::className());?>';
  87 + $.post(
  88 + "/file/uploader/delete-image", {
  89 + new_url : new_url, old_img : old_url, model : model, field : "<?= $field?>",
  90 + id : "<?=$model->$id?>", action : 'save'
  91 + }, function()
  92 + {
  93 + }
  94 + );
  95 + $("#<?=$field?>_picture_link").val(new_url);
  96 + }
  97 + );
  98 +
  99 + $('body').on(
  100 + 'click', '#<?=$field?>_remove_img', function()
  101 + {
  102 + $("#<?= $field?>_img_block").parent().parent().parent()
  103 + .removeClass('success_download');
  104 + $("#<?= $field?>_img_block").parent().parent().find('.admin-ava-wr').remove();
  105 +
  106 + $("#<?=$field?>_buttons_block").remove();
  107 + var old_url = $('#<?=$field?>_old_img').val();
  108 + var new_url = $('#<?=$field?>_new_img').val();
  109 + $.post(
  110 + "/file/uploader/delete-image", {old_img : new_url}, function()
  111 + {
  112 + }
  113 + );
  114 + <?php
  115 + if($remover) {
  116 + echo "$(\"#{$field}_picture_link\").val('');
  117 + $('#{$field}_img_block').find('img').remove();";
  118 + } else {
  119 + echo "$(\"#{$field}_picture_link\").val(old_url);
  120 + if(old_url.length<=1){
  121 + $('#{$field}_img_block').find('img').remove()
  122 + }
  123 + else {
  124 + $('#{$field}_img_block').find('img').attr('src',old_url);
  125 + }";
  126 + };
  127 + ?>
  128 + }
  129 + );
  130 + });
  131 +</script>
  132 +
  133 +<?php else:?>
  134 +
  135 + <span class="btn btn-success fileinput-button uploader-button">
  136 + <i class="glyphicon glyphicon-plus"></i>
  137 + <span><?=$name?></span>
  138 +
  139 + <?= Html::activeFileInput( new \common\models\ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo'), 'multiple'=> 'multiple' ]);?>
  140 + </span>
  141 +
  142 + <?= Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
  143 +
  144 +
  145 + <input type="hidden" name="ImageSizerForm[multi]" value="true"/>
  146 +
  147 + <div id="<?= $field?>_img_block">
  148 + <?php
  149 +
  150 + foreach($this->context->getGallery() as $image){
  151 + echo $this->render('_gallery_item', [ 'item' => ['image'=>$image]]);
  152 + }
  153 + ?>
  154 + </div>
  155 + <script>
  156 + $(function(){
  157 +
  158 + $("#<?= $field?>").fileupload({
  159 + dataType: 'json',
  160 + formData: {size:'<?= json_encode($size)?>', multi: 1},
  161 + done: function (e, data) {
  162 + var img = data.result.view;
  163 + var block = $("#<?= $field?>_img_block");
  164 + block.append(img);
  165 + var gallery = $("#<?= $field?>_picture_link");
  166 + gallery.val(gallery.val()+data.result.link+',');
  167 + }
  168 + });
  169 + $('body').on('click','.delete-gallery-item', function(){
  170 + var url = $(this).data('url');
  171 + $(this).parent('.gallery_image').remove();
  172 + var gallery = $("#<?= $field?>_picture_link");
  173 + var urls = gallery.val();
  174 + gallery.val(urls.replace(url+',', ""));
  175 + $.post( "/file/uploader/delete-image",{old_img: url}, function( data ) {
  176 + $( ".result" ).html( data );
  177 + });
  178 + })
  179 +
  180 + })
  181 + </script>
  182 +
  183 +<?php endif;?>
  184 +</div>
0 185 \ No newline at end of file
... ...
console/migrations/m160312_143616_project_add_field_total_budget.php
... ... @@ -6,7 +6,7 @@ class m160312_143616_project_add_field_total_budget extends Migration
6 6 {
7 7 public function up()
8 8 {
9   - $this->addColumn('{{%project}}', 'total_budget', $this->integer());
  9 + $this->addColumn('{{%project}}', 'total_budget', $this->float());
10 10 }
11 11  
12 12 public function down()
... ...
frontend/assets/AdminAsset.php
... ... @@ -36,7 +36,7 @@ class AdminAsset extends AssetBundle
36 36 public $depends = [
37 37 'yii\web\YiiAsset',
38 38 'yii\bootstrap\BootstrapPluginAsset',
39   - 'backend\assets\FileUploadAsset',
  39 + 'common\modules\file\FileUploadAsset',
40 40 'frontend\assets\CustomizeBootstrapAsset',
41 41 ];
42 42 public $jsOptions = array(
... ...
frontend/controllers/ChatController.php
... ... @@ -2,6 +2,7 @@
2 2 namespace frontend\controllers;
3 3  
4 4 use common\models\Chat;
  5 +use common\models\Fields;
5 6 use common\models\File;
6 7 use common\models\Message;
7 8 use Yii;
... ... @@ -85,6 +86,9 @@ class ChatController extends Controller
85 86 $chat->save();
86 87 }
87 88  
  89 + $phones = Fields::getData($chat->interlocutor->user_id, User::className(), 'phone');
  90 + $sites = Fields::getData($chat->interlocutor->user_id, User::className(), 'site');
  91 +
88 92  
89 93 $post = \Yii::$app->request->post();
90 94 if(isset($post)){
... ... @@ -136,7 +140,9 @@ class ChatController extends Controller
136 140  
137 141 return $this->render('message',[
138 142 'chat' => $chat,
139   - 'user_id' => $user_id
  143 + 'user_id' => $user_id,
  144 + 'phones' => $phones,
  145 + 'sites' => $sites,
140 146 ]);
141 147 }
142 148  
... ...
frontend/views/accounts/general.php
... ... @@ -8,7 +8,7 @@
8 8 use common\models\Option;
9 9 use common\models\User;
10 10 use common\models\UserInfo;
11   - use common\widgets\ImageUploader;
  11 + use common\modules\file\widgets\ImageUploader;
12 12 use yii\helpers\Html;
13 13 use yii\widgets\ActiveForm;
14 14 use \common\widgets\MultiLangForm;
... ...
frontend/views/chat/message.php
... ... @@ -8,7 +8,9 @@ $this-&gt;registerJsFile(&quot;/js/forms.js&quot;);
8 8 $this->registerJsFile("/js/jmousewhell.js");
9 9 $this->registerJsFile("/js/jscroll.js");
10 10 $this->title = 'Мой профиль';
11   - $this->params['breadcrumbs'][] = $this->title;
  11 + $this->params['breadcrumbs'][] = $this->title;;
  12 +
  13 +
12 14 ?>
13 15  
14 16 <div class="section-box content">
... ... @@ -18,10 +20,10 @@ $this-&gt;registerJsFile(&quot;/js/forms.js&quot;);
18 20 <div class="section-box">
19 21 <div class="cabinet-message-read-autor-wr style">
20 22 <div class="cabinet-message-read-foto-wr">
21   - <div class="cabinet-message-read-foto"><img src="/images/ded-foto.jpg" alt=""/></div>
  23 + <div class="cabinet-message-read-foto"><?= Html::a(Html::img($chat->interlocutor->image));?></div>
22 24 </div>
23 25 <div class="cab-mes-read-cont">
24   - <div class="cab-mes-read-cont-title">Петер Цумтор</div>
  26 + <div class="cab-mes-read-cont-title"><?= $chat->interlocutor->name ?></div>
25 27 <div class="cab-mes-read-cont-stars">
26 28 <div class="rating">
27 29 <!--оценка-->
... ... @@ -30,40 +32,38 @@ $this-&gt;registerJsFile(&quot;/js/forms.js&quot;);
30 32 </div>
31 33 <div class="cab-mes-read-cont-com">30 отзывов</div>
32 34 <div class="cab-mes-read-cont-soc">
33   - <a href="#"><img src="/images/ico-fb.png" alt=""/></a>
34   - <a href="#"><img src="/images/ico-tw.png" alt=""/></a>
35   - <a href="#"><img src="/images/ico-in.png" alt=""/></a>
36   - <a href="#"><img src="/images/ico-vk.png" alt=""/></a>
  35 + <?= Html::a(Html::img('/images/ico-fb.png'),"{$chat->interlocutor->social_fb}",['target'=>'_blank'])?>
  36 +
  37 + <?= Html::a(Html::img('/images/ico-tw.png'),"{$chat->interlocutor->social_t}", ['target'=>'_blank'])?>
  38 +
  39 + <?= Html::a(Html::img('/images/ico-in.png'),"{$chat->interlocutor->social_in}",['target'=>'_blank'])?>
  40 +
  41 + <?= Html::a(Html::img('/images/ico-vk.png'),"{$chat->interlocutor->social_vk}",['target'=>'_blank'])?>
  42 +
37 43 </div>
38 44 </div>
39 45 <div class="performance-vacancy-add-favorite"><a href="#"></a></div>
40 46 <div class="cab-mes-read-last-visit">
41   - <!--<div class="cab-mes-read-min-bl-wr">-->
42   - <!--<div class="cab-mes-read-min-bl">-->
43   - <!--<img src="/images/sidebar-ico/ico-2.png" alt=""/><span><span class="sidebar-views-txt">На сайте: </span>1г. 8 мес.</span>-->
44   - <!--</div>-->
45   - <!--</div>-->
46   - <!--<div class="cab-mes-read-min-bl"></div>-->
47   - <!--<div class="cab-mes-read-min-bl cab-mes-visit"></div>-->
48   - <!--<div class="cab-mes-read-min-bl"></div>-->
49 47 <div class="cab-mes-read-min-bl">
50 48 <div class="profile-phone-site style">
51 49 <div class="style">
52 50 <div class="profile-phone">
53 51 <img src="/images/sidebar-ico/ico-2.png" alt="">
54   - <span style="font-size: 13px" class="sidebar-views-txt">На сайте: </span>1г. 8 мес.
  52 + <span style="font-size: 13px" class="sidebar-views-txt">На сайте: </span><?= $chat->interlocutor->user->liveTime ?>
55 53 </div>
56 54 </div>
57 55 </div>
58 56 </div>
59 57 <div class="cab-mes-read-min-bl">
60 58 <div class="profile-phone-site style">
61   - <div class="style">
62   - <div class="profile-phone">
63   - <img src="/images/ico-phone.png" alt="">
64   - <span>+38 (050) 123-45-67</span>
  59 + <?php foreach( $phones as $phone):?>
  60 + <div class="style">
  61 + <div class="profile-phone">
  62 + <img src="/images/ico-phone.png" alt=""/>
  63 + <span><?= $phone['phone'] ?></span>
  64 + </div>
65 65 </div>
66   - </div>
  66 + <?php endforeach; ?>
67 67 </div>
68 68 </div>
69 69 <div class="cab-mes-read-min-bl">
... ... @@ -71,19 +71,21 @@ $this-&gt;registerJsFile(&quot;/js/forms.js&quot;);
71 71 <div class="style">
72 72 <div class="profile-phone">
73 73 <img src="/images/sidebar-ico/ico-3.png" alt="">
74   - <span style="font-size: 13px" class="sidebar-views-txt">Последний визит:<br></span><p>2 дня назад</p>
  74 + <span style="font-size: 13px" class="sidebar-views-txt">Последний визит:<br></span><p><?= $chat->interlocutor->lastVisit ?></p>
75 75 </div>
76 76 </div>
77 77 </div>
78 78 </div>
79 79 <div class="cab-mes-read-min-bl">
80 80 <div class="profile-phone-site style">
81   - <div class="style">
82   - <div class="profile-site">
83   - <img src="/images/ico-site.png" alt="">
84   - <a href="#" target="_blank">Сайт</a>
  81 + <?php foreach( $sites as $site):?>
  82 + <div class="style">
  83 + <div class="profile-site">
  84 + <img src="/images/ico-site.png" alt=""/>
  85 + <a href="http://<?= $site['site']?>" target="_blank">Сайт</a>
  86 + </div>
85 87 </div>
86   - </div>
  88 + <?php endforeach; ?>
87 89 </div>
88 90 </div>
89 91 </div>
... ...