1 | /*! |
---|
2 | * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2015 |
---|
3 | * @version 4.1.8 |
---|
4 | * |
---|
5 | * File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced |
---|
6 | * features including the FileReader API. |
---|
7 | * |
---|
8 | * The plugin drastically enhances the HTML file input to preview multiple files on the client before |
---|
9 | * upload. In addition it provides the ability to preview content of images, text, videos, audio, html, |
---|
10 | * flash and other objects. It also offers the ability to upload and delete files using AJAX, and add |
---|
11 | * files in batches (i.e. preview, append, or remove before upload). |
---|
12 | * |
---|
13 | * Author: Kartik Visweswaran |
---|
14 | * Copyright: 2015, Kartik Visweswaran, Krajee.com |
---|
15 | * For more JQuery plugins visit http://plugins.krajee.com |
---|
16 | * For more Yii related demos visit http://demos.krajee.com |
---|
17 | */ |
---|
18 | (function ($) { |
---|
19 | "use strict"; |
---|
20 | String.prototype.repl = function (from, to) { |
---|
21 | return this.split(from).join(to); |
---|
22 | }; |
---|
23 | var isIE = function (ver) { |
---|
24 | var div = document.createElement("div"), status; |
---|
25 | div.innerHTML = "<!--[if IE " + ver + "]><i></i><![endif]-->"; |
---|
26 | status = (div.getElementsByTagName("i").length === 1); |
---|
27 | document.body.appendChild(div); |
---|
28 | div.parentNode.removeChild(div); |
---|
29 | return status; |
---|
30 | }, |
---|
31 | previewCache = { |
---|
32 | data: {}, |
---|
33 | init: function (obj) { |
---|
34 | var content = obj.initialPreview, id = obj.id; |
---|
35 | if (content.length > 0 && !isArray(content)) { |
---|
36 | content = content.split(obj.initialPreviewDelimiter); |
---|
37 | } |
---|
38 | previewCache.data[id] = { |
---|
39 | content: content, |
---|
40 | config: obj.initialPreviewConfig, |
---|
41 | tags: obj.initialPreviewThumbTags, |
---|
42 | delimiter: obj.initialPreviewDelimiter, |
---|
43 | template: obj.previewGenericTemplate, |
---|
44 | msg: obj.msgSelected, |
---|
45 | initId: obj.previewInitId, |
---|
46 | footer: obj.getLayoutTemplate('footer'), |
---|
47 | isDelete: obj.initialPreviewShowDelete, |
---|
48 | caption: obj.initialCaption, |
---|
49 | actions: function (showUpload, showDelete, disabled, url, key, index) { |
---|
50 | return obj.renderFileActions(showUpload, showDelete, disabled, url, key, index); |
---|
51 | } |
---|
52 | }; |
---|
53 | }, |
---|
54 | fetch: function (id) { |
---|
55 | return previewCache.data[id].content.filter(function (n) { |
---|
56 | return n !== undefined; |
---|
57 | }); |
---|
58 | }, |
---|
59 | count: function (id) { |
---|
60 | return !!previewCache.data[id] && !!previewCache.data[id].content ? previewCache.fetch(id).length : 0; |
---|
61 | }, |
---|
62 | get: function (id, i, isDisabled) { |
---|
63 | var ind = 'init_' + i, data = previewCache.data[id], |
---|
64 | previewId = data.initId + '-' + ind, out; |
---|
65 | isDisabled = isDisabled === undefined ? true : isDisabled; |
---|
66 | if (data.content[i] === undefined) { |
---|
67 | return ''; |
---|
68 | } |
---|
69 | out = data.template |
---|
70 | .repl('{previewId}', previewId) |
---|
71 | .repl('{frameClass}', ' file-preview-initial') |
---|
72 | .repl('{fileindex}', ind) |
---|
73 | .repl('{content}', data.content[i]) |
---|
74 | .repl('{footer}', previewCache.footer(id, i, isDisabled)); |
---|
75 | if (data.tags.length && data.tags[i]) { |
---|
76 | out = replaceTags(out, data.tags[i]); |
---|
77 | } |
---|
78 | return out; |
---|
79 | }, |
---|
80 | add: function (id, content, config, append) { |
---|
81 | var data = $.extend(true, {}, previewCache.data[id]), index; |
---|
82 | if (!isArray(content)) { |
---|
83 | content = content.split(data.delimiter); |
---|
84 | } |
---|
85 | if (append) { |
---|
86 | index = data.content.push(content) - 1; |
---|
87 | data.config[index] = config; |
---|
88 | } else { |
---|
89 | index = content.length; |
---|
90 | data.content = content; |
---|
91 | data.config = config; |
---|
92 | } |
---|
93 | previewCache.data[id] = data; |
---|
94 | return index; |
---|
95 | }, |
---|
96 | set: function (id, content, config, tags, append) { |
---|
97 | var data = $.extend(true, {}, previewCache.data[id]), i; |
---|
98 | if (!isArray(content)) { |
---|
99 | content = content.split(data.delimiter); |
---|
100 | } |
---|
101 | if (append) { |
---|
102 | for (i = 0; i < content.length; i++) { |
---|
103 | data.content.push(content[i]); |
---|
104 | } |
---|
105 | for (i = 0; i < config.length; i++) { |
---|
106 | data.config.push(config[i]); |
---|
107 | } |
---|
108 | for (i = 0; i < tags.length; i++) { |
---|
109 | data.tags.push(tags[i]); |
---|
110 | } |
---|
111 | } else { |
---|
112 | data.content = content; |
---|
113 | data.config = config; |
---|
114 | data.tags = tags; |
---|
115 | } |
---|
116 | previewCache.data[id] = data; |
---|
117 | }, |
---|
118 | unset: function (id, index) { |
---|
119 | var chk = previewCache.count(id); |
---|
120 | if (!chk) { |
---|
121 | return; |
---|
122 | } |
---|
123 | if (chk === 1) { |
---|
124 | previewCache.data[id].content = []; |
---|
125 | previewCache.data[id].config = []; |
---|
126 | return; |
---|
127 | } |
---|
128 | previewCache.data[id].content[index] = undefined; |
---|
129 | previewCache.data[id].config[index] = undefined; |
---|
130 | }, |
---|
131 | out: function (id) { |
---|
132 | var html = '', data = previewCache.data[id], caption, len = previewCache.count(id); |
---|
133 | if (len === 0) { |
---|
134 | return {content: '', caption: ''}; |
---|
135 | } |
---|
136 | for (var i = 0; i < len; i++) { |
---|
137 | html += previewCache.get(id, i); |
---|
138 | } |
---|
139 | caption = data.msg.repl('{n}', len); |
---|
140 | return {content: html, caption: caption}; |
---|
141 | }, |
---|
142 | footer: function (id, i, isDisabled) { |
---|
143 | var data = previewCache.data[id]; |
---|
144 | isDisabled = isDisabled === undefined ? true : isDisabled; |
---|
145 | if (data.config.length === 0 || isEmpty(data.config[i])) { |
---|
146 | return ''; |
---|
147 | } |
---|
148 | var config = data.config[i], |
---|
149 | caption = isSet('caption', config) ? config.caption : '', |
---|
150 | width = isSet('width', config) ? config.width : 'auto', |
---|
151 | url = isSet('url', config) ? config.url : false, |
---|
152 | key = isSet('key', config) ? config.key : null, |
---|
153 | disabled = (url === false) && isDisabled, |
---|
154 | actions = data.isDelete ? data.actions(false, true, disabled, url, key, i) : '', |
---|
155 | footer = data.footer.repl('{actions}', actions); |
---|
156 | return footer |
---|
157 | .repl('{caption}', caption) |
---|
158 | .repl('{width}', width) |
---|
159 | .repl('{indicator}', '') |
---|
160 | .repl('{indicatorTitle}', ''); |
---|
161 | } |
---|
162 | }, |
---|
163 | PREVIEW_FRAMES = '.file-preview-frame:not(.file-preview-initial)', |
---|
164 | getNum = function (num, def) { |
---|
165 | def = def || 0; |
---|
166 | if (typeof num === "number") { |
---|
167 | return num; |
---|
168 | } |
---|
169 | if (typeof num === "string") { |
---|
170 | num = parseFloat(num); |
---|
171 | } |
---|
172 | return isNaN(num) ? def : num; |
---|
173 | }, |
---|
174 | hasFileAPISupport = function () { |
---|
175 | return window.File && window.FileReader; |
---|
176 | }, |
---|
177 | hasDragDropSupport = function () { |
---|
178 | var $div = document.createElement('div'); |
---|
179 | return !isIE(9) && ($div.draggable !== undefined || ($div.ondragstart !== undefined && $div.ondrop !== undefined)); |
---|
180 | }, |
---|
181 | hasFileUploadSupport = function () { |
---|
182 | return hasFileAPISupport && window.FormData; |
---|
183 | }, |
---|
184 | addCss = function ($el, css) { |
---|
185 | $el.removeClass(css).addClass(css); |
---|
186 | }, |
---|
187 | STYLE_SETTING = 'style="width:{width};height:{height};"', |
---|
188 | OBJECT_PARAMS = ' <param name="controller" value="true" />\n' + |
---|
189 | ' <param name="allowFullScreen" value="true" />\n' + |
---|
190 | ' <param name="allowScriptAccess" value="always" />\n' + |
---|
191 | ' <param name="autoPlay" value="false" />\n' + |
---|
192 | ' <param name="autoStart" value="false" />\n' + |
---|
193 | ' <param name="quality" value="high" />\n', |
---|
194 | DEFAULT_PREVIEW = '<div class="file-preview-other">\n' + |
---|
195 | ' {previewFileIcon}\n' + |
---|
196 | ' </div>', |
---|
197 | defaultFileActionSettings = { |
---|
198 | removeIcon: '<i class="fa fa-trash text-danger"></i>', |
---|
199 | removeClass: 'btn btn-xs btn-light rounded-0', |
---|
200 | removeTitle: 'Remove file', |
---|
201 | uploadIcon: '<i class="fas fa-upload text-info"></i>', |
---|
202 | uploadClass: 'btn btn-xs btn-light rounded-0', |
---|
203 | uploadTitle: 'Upload file', |
---|
204 | indicatorNew: '<i class="fa fa-hand-point-down text-warning"></i>', |
---|
205 | indicatorSuccess: '<i class="fa fa-ok file-icon-large text-success"></i>', |
---|
206 | indicatorError: '<i class="fa fa-exclamation text-danger"></i>', |
---|
207 | indicatorLoading: '<i class="fa fa-hand-up text-muted"></i>', |
---|
208 | indicatorNewTitle: 'Not uploaded yet', |
---|
209 | indicatorSuccessTitle: 'Uploaded', |
---|
210 | indicatorErrorTitle: 'Upload Error', |
---|
211 | indicatorLoadingTitle: 'Uploading ...' |
---|
212 | }, |
---|
213 | tMain1 = '{preview}\n' + |
---|
214 | '<div class="kv-upload-progress hide"></div>\n' + |
---|
215 | '<div class="input-group {class}">\n' + |
---|
216 | ' {caption}\n' + |
---|
217 | ' <div class="input-group-btn">\n' + |
---|
218 | ' {remove}\n' + |
---|
219 | ' {cancel}\n' + |
---|
220 | ' {upload}\n' + |
---|
221 | ' {browse}\n' + |
---|
222 | ' </div>\n' + |
---|
223 | '</div>', |
---|
224 | tMain2 = '{preview}\n<div class="kv-upload-progress hide"></div>\n{remove}\n{cancel}\n{upload}\n{browse}\n', |
---|
225 | tPreview = '<div class="file-preview {class}">\n' + |
---|
226 | ' <div class="close fileinput-remove">×</div>\n' + |
---|
227 | ' <div class="{dropClass}">\n' + |
---|
228 | ' <div class="file-preview-thumbnails">\n' + |
---|
229 | ' </div>\n' + |
---|
230 | ' <div class="clearfix"></div>' + |
---|
231 | ' <div class="file-preview-status text-center text-success"></div>\n' + |
---|
232 | ' <div class="kv-fileinput-error"></div>\n' + |
---|
233 | ' </div>\n' + |
---|
234 | '</div>', |
---|
235 | tIcon = '<span class="fa fa-file kv-caption-icon"></span>', |
---|
236 | tCaption = '<div tabindex="-1" class="form-control file-caption {class}">\n' + |
---|
237 | ' <span class="file-caption-ellipsis">…</span>\n' + |
---|
238 | ' <div class="file-caption-name"></div>\n' + |
---|
239 | '</div>', |
---|
240 | tModal = '<div id="{id}" class="modal fade">\n' + |
---|
241 | ' <div class="modal-dialog modal-lg">\n' + |
---|
242 | ' <div class="modal-content">\n' + |
---|
243 | ' <div class="modal-header">\n' + |
---|
244 | ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\n' + |
---|
245 | ' <h3 class="modal-title">Detailed Preview <small>{title}</small></h3>\n' + |
---|
246 | ' </div>\n' + |
---|
247 | ' <div class="modal-body">\n' + |
---|
248 | ' <textarea class="form-control" style="font-family:Monaco,Consolas,monospace; height: {height}px;" readonly>{body}</textarea>\n' + |
---|
249 | ' </div>\n' + |
---|
250 | ' </div>\n' + |
---|
251 | ' </div>\n' + |
---|
252 | '</div>', |
---|
253 | tProgress = '<div class="progress">\n' + |
---|
254 | ' <div class="{class}" role="progressbar"' + |
---|
255 | ' aria-valuenow="{percent}" aria-valuemin="0" aria-valuemax="100" style="width:{percent}%;">\n' + |
---|
256 | ' {percent}%\n' + |
---|
257 | ' </div>\n' + |
---|
258 | '</div>', |
---|
259 | tFooter = '<div class="file-thumbnail-footer">\n' + |
---|
260 | ' <div class="file-caption-name">{caption}</div>\n' + |
---|
261 | ' {actions}\n' + |
---|
262 | '</div>', |
---|
263 | tActions = '<div class="file-actions">\n' + |
---|
264 | ' <div class="file-footer-buttons">\n' + |
---|
265 | ' {upload}{delete}{other}' + |
---|
266 | ' </div>\n' + |
---|
267 | ' <div class="file-upload-indicator" tabindex="-1" title="{indicatorTitle}">{indicator}</div>\n' + |
---|
268 | ' <div class="clearfix"></div>\n' + |
---|
269 | '</div>', |
---|
270 | tActionDelete = '<button type="button" class="kv-file-remove {removeClass}" ' + |
---|
271 | 'title="{removeTitle}"{dataUrl}{dataKey}>{removeIcon}</button>\n', |
---|
272 | tActionUpload = '<button type="button" class="kv-file-upload {uploadClass}" title="{uploadTitle}">' + |
---|
273 | ' {uploadIcon}\n</button>\n', |
---|
274 | tGeneric = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + |
---|
275 | ' {content}\n' + |
---|
276 | ' {footer}\n' + |
---|
277 | '</div>\n', |
---|
278 | tHtml = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + |
---|
279 | ' <object data="{data}" type="{type}" width="{width}" height="{height}">\n' + |
---|
280 | ' ' + DEFAULT_PREVIEW + '\n' + |
---|
281 | ' </object>\n' + |
---|
282 | ' {footer}\n' + |
---|
283 | '</div>', |
---|
284 | tImage = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + |
---|
285 | ' <img src="{data}" class="file-preview-image" title="{caption}" alt="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
286 | ' {footer}\n' + |
---|
287 | '</div>\n', |
---|
288 | tText = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + |
---|
289 | ' <div class="file-preview-text" title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
290 | ' {data}\n' + |
---|
291 | ' </div>\n' + |
---|
292 | ' {footer}\n' + |
---|
293 | '</div>', |
---|
294 | tVideo = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + |
---|
295 | ' title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
296 | ' <video width="{width}" height="{height}" controls>\n' + |
---|
297 | ' <source src="{data}" type="{type}">\n' + |
---|
298 | ' ' + DEFAULT_PREVIEW + '\n' + |
---|
299 | ' </video>\n' + |
---|
300 | ' {footer}\n' + |
---|
301 | '</div>\n', |
---|
302 | tAudio = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + |
---|
303 | ' title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
304 | ' <audio controls>\n' + |
---|
305 | ' <source src="' + '{data}' + '" type="{type}">\n' + |
---|
306 | ' ' + DEFAULT_PREVIEW + '\n' + |
---|
307 | ' </audio>\n' + |
---|
308 | ' {footer}\n' + |
---|
309 | '</div>', |
---|
310 | tFlash = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + |
---|
311 | ' title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
312 | ' <object type="application/x-shockwave-flash" width="{width}" height="{height}" data="{data}">\n' + |
---|
313 | OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + |
---|
314 | ' </object>\n' + |
---|
315 | ' {footer}\n' + |
---|
316 | '</div>\n', |
---|
317 | tObject = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + |
---|
318 | ' title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
319 | ' <object data="{data}" type="{type}" width="{width}" height="{height}">\n' + |
---|
320 | ' <param name="movie" value="{caption}" />\n' + |
---|
321 | OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + |
---|
322 | ' </object>\n' + |
---|
323 | ' {footer}\n' + |
---|
324 | '</div>', |
---|
325 | tOther = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + |
---|
326 | ' title="{caption}" ' + STYLE_SETTING + '>\n' + |
---|
327 | ' ' + DEFAULT_PREVIEW + '\n' + |
---|
328 | ' {footer}\n' + |
---|
329 | '</div>', |
---|
330 | defaultLayoutTemplates = { |
---|
331 | main1: tMain1, |
---|
332 | main2: tMain2, |
---|
333 | preview: tPreview, |
---|
334 | icon: tIcon, |
---|
335 | caption: tCaption, |
---|
336 | modal: tModal, |
---|
337 | progress: tProgress, |
---|
338 | footer: tFooter, |
---|
339 | actions: tActions, |
---|
340 | actionDelete: tActionDelete, |
---|
341 | actionUpload: tActionUpload |
---|
342 | }, |
---|
343 | defaultPreviewTemplates = { |
---|
344 | generic: tGeneric, |
---|
345 | html: tHtml, |
---|
346 | image: tImage, |
---|
347 | text: tText, |
---|
348 | video: tVideo, |
---|
349 | audio: tAudio, |
---|
350 | flash: tFlash, |
---|
351 | object: tObject, |
---|
352 | other: tOther |
---|
353 | }, |
---|
354 | defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'object'], |
---|
355 | defaultPreviewSettings = { |
---|
356 | image: {width: "auto", height: "160px"}, |
---|
357 | html: {width: "213px", height: "160px"}, |
---|
358 | text: {width: "160px", height: "160px"}, |
---|
359 | video: {width: "213px", height: "160px"}, |
---|
360 | audio: {width: "213px", height: "80px"}, |
---|
361 | flash: {width: "213px", height: "160px"}, |
---|
362 | object: {width: "160px", height: "160px"}, |
---|
363 | other: {width: "160px", height: "160px"} |
---|
364 | }, |
---|
365 | defaultFileTypeSettings = { |
---|
366 | image: function (vType, vName) { |
---|
367 | return (vType !== undefined) ? vType.match('image.*') : vName.match(/\.(gif|png|jpe?g)$/i); |
---|
368 | }, |
---|
369 | html: function (vType, vName) { |
---|
370 | return (vType !== undefined) ? vType === 'text/html' : vName.match(/\.(htm|html)$/i); |
---|
371 | }, |
---|
372 | text: function (vType, vName) { |
---|
373 | return (vType !== undefined && vType.match('text.*')) || vName.match(/\.(txt|md|csv|nfo|php|ini)$/i); |
---|
374 | }, |
---|
375 | video: function (vType, vName) { |
---|
376 | return (vType !== undefined && vType.match(/\.video\/(ogg|mp4|webm)$/i)) || vName.match(/\.(og?|mp4|webm)$/i); |
---|
377 | }, |
---|
378 | audio: function (vType, vName) { |
---|
379 | return (vType !== undefined && vType.match(/\.audio\/(ogg|mp3|wav)$/i)) || vName.match(/\.(ogg|mp3|wav)$/i); |
---|
380 | }, |
---|
381 | flash: function (vType, vName) { |
---|
382 | return (vType !== undefined && vType === 'application/x-shockwave-flash') || vName.match(/\.(swf)$/i); |
---|
383 | }, |
---|
384 | object: function () { |
---|
385 | return true; |
---|
386 | }, |
---|
387 | other: function () { |
---|
388 | return true; |
---|
389 | } |
---|
390 | }, |
---|
391 | isEmpty = function (value, trim) { |
---|
392 | return value === null || value === undefined || value.length === 0 || (trim && $.trim(value) === ''); |
---|
393 | }, |
---|
394 | isArray = function (a) { |
---|
395 | return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; |
---|
396 | }, |
---|
397 | isSet = function (needle, haystack) { |
---|
398 | return (typeof haystack === 'object' && needle in haystack); |
---|
399 | }, |
---|
400 | getElement = function (options, param, value) { |
---|
401 | return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]); |
---|
402 | }, |
---|
403 | uniqId = function () { |
---|
404 | return Math.round(new Date().getTime() + (Math.random() * 100)); |
---|
405 | }, |
---|
406 | htmlEncode = function (str) { |
---|
407 | return String(str).repl('&', '&') |
---|
408 | .repl('"', '"') |
---|
409 | .repl("'", ''') |
---|
410 | .repl('<', '<') |
---|
411 | .repl('>', '>'); |
---|
412 | }, |
---|
413 | replaceTags = function (str, tags) { |
---|
414 | var out = str; |
---|
415 | tags = tags || {}; |
---|
416 | $.each(tags, function (key, value) { |
---|
417 | if (typeof value === "function") { |
---|
418 | value = value(); |
---|
419 | } |
---|
420 | out = out.repl(key, value); |
---|
421 | }); |
---|
422 | return out; |
---|
423 | }, |
---|
424 | objUrl = window.URL || window.webkitURL, |
---|
425 | FileInput = function (element, options) { |
---|
426 | this.$element = $(element); |
---|
427 | if (hasFileAPISupport() || isIE(9)) { |
---|
428 | this.init(options); |
---|
429 | this.listen(); |
---|
430 | } else { |
---|
431 | this.$element.removeClass('file-loading'); |
---|
432 | } |
---|
433 | }; |
---|
434 | |
---|
435 | FileInput.prototype = { |
---|
436 | constructor: FileInput, |
---|
437 | init: function (options) { |
---|
438 | var self = this, $el = self.$element, t; |
---|
439 | $.each(options, function (key, value) { |
---|
440 | if (key === 'maxFileCount' || key === 'maxFileSize') { |
---|
441 | self[key] = getNum(value); |
---|
442 | } |
---|
443 | self[key] = value; |
---|
444 | }); |
---|
445 | self.fileInputCleared = false; |
---|
446 | self.fileBatchCompleted = true; |
---|
447 | if (isEmpty(self.allowedPreviewTypes)) { |
---|
448 | self.allowedPreviewTypes = defaultPreviewTypes; |
---|
449 | } |
---|
450 | self.uploadFileAttr = !isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; |
---|
451 | self.reader = null; |
---|
452 | self.formdata = {}; |
---|
453 | self.isIE9 = isIE(9); |
---|
454 | self.isIE10 = isIE(10); |
---|
455 | self.filestack = []; |
---|
456 | self.ajaxRequests = []; |
---|
457 | self.isError = false; |
---|
458 | self.uploadAborted = false; |
---|
459 | self.dropZoneEnabled = hasDragDropSupport() && self.dropZoneEnabled; |
---|
460 | self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); |
---|
461 | self.isUploadable = hasFileUploadSupport && !isEmpty(self.uploadUrl); |
---|
462 | self.slug = typeof options.slugCallback === "function" ? options.slugCallback : self.slugDefault; |
---|
463 | self.mainTemplate = self.showCaption ? self.getLayoutTemplate('main1') : self.getLayoutTemplate('main2'); |
---|
464 | self.captionTemplate = self.getLayoutTemplate('caption'); |
---|
465 | self.previewGenericTemplate = self.getPreviewTemplate('generic'); |
---|
466 | if (isEmpty(self.$element.attr('id'))) { |
---|
467 | self.$element.attr('id', uniqId()); |
---|
468 | } |
---|
469 | if (self.$container === undefined) { |
---|
470 | self.$container = self.createContainer(); |
---|
471 | } else { |
---|
472 | self.refreshContainer(); |
---|
473 | } |
---|
474 | self.$progress = self.$container.find('.kv-upload-progress'); |
---|
475 | self.$btnUpload = self.$container.find('.kv-fileinput-upload'); |
---|
476 | self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption')); |
---|
477 | self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name')); |
---|
478 | self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview')); |
---|
479 | self.$preview = getElement(options, 'elPreviewImage', self.$container.find('.file-preview-thumbnails')); |
---|
480 | self.$previewStatus = getElement(options, 'elPreviewStatus', self.$container.find('.file-preview-status')); |
---|
481 | self.$errorContainer = getElement(options, 'elErrorContainer', |
---|
482 | self.$previewContainer.find('.kv-fileinput-error')); |
---|
483 | if (!isEmpty(self.msgErrorClass)) { |
---|
484 | addCss(self.$errorContainer, self.msgErrorClass); |
---|
485 | } |
---|
486 | self.$errorContainer.hide(); |
---|
487 | self.fileActionSettings = $.extend(defaultFileActionSettings, options.fileActionSettings); |
---|
488 | self.previewInitId = "preview-" + uniqId(); |
---|
489 | self.id = self.$element.attr('id'); |
---|
490 | previewCache.init(self); |
---|
491 | self.initPreview(true); |
---|
492 | self.initPreviewDeletes(); |
---|
493 | self.options = options; |
---|
494 | self.setFileDropZoneTitle(); |
---|
495 | self.uploadCount = 0; |
---|
496 | self.uploadPercent = 0; |
---|
497 | self.$element.removeClass('file-loading'); |
---|
498 | t = self.getLayoutTemplate('progress'); |
---|
499 | self.progressTemplate = t.replace('{class}', self.progressClass); |
---|
500 | self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); |
---|
501 | self.setEllipsis(); |
---|
502 | }, |
---|
503 | parseError: function (jqXHR, errorThrown, fileName) { |
---|
504 | var self = this, errMsg = $.trim(errorThrown + ''), |
---|
505 | dot = errMsg.slice(-1) === '.' ? '' : '.', |
---|
506 | text = $(jqXHR.responseText).text(); |
---|
507 | if (self.showAjaxErrorDetails) { |
---|
508 | text = $.trim(text.replace(/\n\s*\n/g, '\n')); |
---|
509 | text = text.length > 0 ? '<pre>' + text + '</pre>' : ''; |
---|
510 | errMsg += dot + text; |
---|
511 | } else { |
---|
512 | errMsg += dot; |
---|
513 | } |
---|
514 | return fileName ? '<b>' + fileName + ': </b>' + jqXHR : errMsg; |
---|
515 | }, |
---|
516 | raise: function (event, params) { |
---|
517 | var self = this, e = $.Event(event), out; |
---|
518 | if (params !== undefined) { |
---|
519 | self.$element.trigger(e, params); |
---|
520 | } else { |
---|
521 | self.$element.trigger(e); |
---|
522 | } |
---|
523 | out = e.result || false; |
---|
524 | if (!out) { |
---|
525 | return; |
---|
526 | } |
---|
527 | switch (event) { |
---|
528 | // ignore these events |
---|
529 | case 'filebatchuploadcomplete': |
---|
530 | case 'filebatchuploadsuccess': |
---|
531 | case 'fileuploaded': |
---|
532 | case 'fileclear': |
---|
533 | case 'filecleared': |
---|
534 | case 'filereset': |
---|
535 | case 'fileerror': |
---|
536 | case 'filefoldererror': |
---|
537 | case 'fileuploaderror': |
---|
538 | case 'filebatchuploaderror': |
---|
539 | case 'filedeleteerror': |
---|
540 | case 'filecustomerror': |
---|
541 | break; |
---|
542 | // can trigger filecustomerror to abort upload |
---|
543 | default: |
---|
544 | self.uploadAborted = out; |
---|
545 | break; |
---|
546 | } |
---|
547 | }, |
---|
548 | getLayoutTemplate: function (t) { |
---|
549 | var self = this, |
---|
550 | template = isSet(t, self.layoutTemplates) ? self.layoutTemplates[t] : defaultLayoutTemplates[t]; |
---|
551 | if (isEmpty(self.customLayoutTags)) { |
---|
552 | return template; |
---|
553 | } |
---|
554 | return replaceTags(template, self.customLayoutTags); |
---|
555 | }, |
---|
556 | getPreviewTemplate: function (t) { |
---|
557 | var self = this, |
---|
558 | template = isSet(t, self.previewTemplates) ? self.previewTemplates[t] : defaultPreviewTemplates[t]; |
---|
559 | template = template.repl('{previewFileIcon}', self.previewFileIcon); |
---|
560 | if (isEmpty(self.customPreviewTags)) { |
---|
561 | return template; |
---|
562 | } |
---|
563 | return replaceTags(template, self.customPreviewTags); |
---|
564 | }, |
---|
565 | getOutData: function (jqXHR, responseData, filesData) { |
---|
566 | var self = this; |
---|
567 | jqXHR = jqXHR || {}; |
---|
568 | responseData = responseData || {}; |
---|
569 | filesData = filesData || self.filestack.slice(0) || {}; |
---|
570 | return { |
---|
571 | form: self.formdata, |
---|
572 | files: filesData, |
---|
573 | extra: self.getExtraData(), |
---|
574 | response: responseData, |
---|
575 | reader: self.reader, |
---|
576 | jqXHR: jqXHR |
---|
577 | }; |
---|
578 | }, |
---|
579 | setEllipsis: function () { |
---|
580 | var self = this, $capCont = self.$captionContainer, $cap = self.$caption, |
---|
581 | $div = $cap.clone().css('height', 'auto').hide(); |
---|
582 | $capCont.parent().before($div); |
---|
583 | $capCont.removeClass('kv-has-ellipsis'); |
---|
584 | if ($div.outerWidth() > $cap.outerWidth()) { |
---|
585 | $capCont.addClass('kv-has-ellipsis'); |
---|
586 | } |
---|
587 | $div.remove(); |
---|
588 | }, |
---|
589 | listen: function () { |
---|
590 | var self = this, $el = self.$element, $cap = self.$captionContainer, $btnFile = self.$btnFile, |
---|
591 | $form = $el.closest('form'); |
---|
592 | $el.on('change', $.proxy(self.change, self)); |
---|
593 | $(window).on('resize', function () { |
---|
594 | self.setEllipsis(); |
---|
595 | }); |
---|
596 | $btnFile.off('click').on('click', function () { |
---|
597 | self.raise('filebrowse'); |
---|
598 | if (self.isError && !self.isUploadable) { |
---|
599 | self.clear(); |
---|
600 | } |
---|
601 | $cap.focus(); |
---|
602 | }); |
---|
603 | $form.off('reset').on('reset', $.proxy(self.reset, self)); |
---|
604 | self.$container.off('click') |
---|
605 | .on('click', '.fileinput-remove:not([disabled])', $.proxy(self.clear, self)) |
---|
606 | .on('click', '.fileinput-cancel', $.proxy(self.cancel, self)); |
---|
607 | if (self.isUploadable && self.dropZoneEnabled && self.showPreview) { |
---|
608 | self.initDragDrop(); |
---|
609 | } |
---|
610 | if (!self.isUploadable) { |
---|
611 | $form.on('submit', $.proxy(self.submitForm, self)); |
---|
612 | } |
---|
613 | self.$container.find('.kv-fileinput-upload').off('click').on('click', function (e) { |
---|
614 | if (!self.isUploadable) { |
---|
615 | return; |
---|
616 | } |
---|
617 | e.preventDefault(); |
---|
618 | if (!$(this).hasClass('disabled') && isEmpty($(this).attr('disabled'))) { |
---|
619 | self.upload(); |
---|
620 | } |
---|
621 | }); |
---|
622 | }, |
---|
623 | submitForm: function () { |
---|
624 | var self = this, $el = self.$element, files = $el.get(0).files; |
---|
625 | if (files && files.length < self.minFileCount && self.minFileCount > 0) { |
---|
626 | self.noFilesError({}); |
---|
627 | return false; |
---|
628 | } |
---|
629 | return !self.abort({}); |
---|
630 | }, |
---|
631 | abort: function (params) { |
---|
632 | var self = this, data; |
---|
633 | if (self.uploadAborted && typeof self.uploadAborted === "object" && self.uploadAborted.message !== undefined) { |
---|
634 | if (self.uploadAborted.data !== undefined) { |
---|
635 | data = self.getOutData({}, self.uploadAborted.data); |
---|
636 | } else { |
---|
637 | data = self.getOutData(); |
---|
638 | } |
---|
639 | data = $.extend(data, params); |
---|
640 | self.showUploadError(self.uploadAborted.message, data, 'filecustomerror'); |
---|
641 | return true; |
---|
642 | } |
---|
643 | return false; |
---|
644 | }, |
---|
645 | noFilesError: function (params) { |
---|
646 | var self = this, label = self.minFileCount > 1 ? self.filePlural : self.fileSingle, |
---|
647 | msg = self.msgFilesTooLess.repl('{n}', self.minFileCount).repl('{files}', label), |
---|
648 | $error = self.$errorContainer; |
---|
649 | $error.html(msg); |
---|
650 | self.isError = true; |
---|
651 | self.updateFileDetails(0); |
---|
652 | $error.fadeIn(800); |
---|
653 | self.raise('fileerror', [params]); |
---|
654 | self.clearFileInput(); |
---|
655 | addCss(self.$container, 'has-error'); |
---|
656 | }, |
---|
657 | setProgress: function (p) { |
---|
658 | var self = this, pct = Math.min(p, 100), |
---|
659 | template = pct < 100 ? self.progressTemplate : self.progressCompleteTemplate; |
---|
660 | self.$progress.html(template.repl('{percent}', pct)); |
---|
661 | }, |
---|
662 | upload: function () { |
---|
663 | var self = this, totLen = self.getFileStack().length, params = {}, |
---|
664 | i, outData, len, hasExtraData = !$.isEmptyObject(self.getExtraData()); |
---|
665 | if (totLen < self.minFileCount && self.minFileCount > 0) { |
---|
666 | self.noFilesError(params); |
---|
667 | return; |
---|
668 | } |
---|
669 | if (!self.isUploadable || self.isDisabled || (totLen === 0 && !hasExtraData)) { |
---|
670 | return; |
---|
671 | } |
---|
672 | self.resetUpload(); |
---|
673 | self.$progress.removeClass('hide'); |
---|
674 | self.uploadCount = 0; |
---|
675 | self.uploadPercent = 0; |
---|
676 | self.lock(); |
---|
677 | self.setProgress(0); |
---|
678 | if (totLen === 0 && hasExtraData) { |
---|
679 | self.uploadExtraOnly(); |
---|
680 | return; |
---|
681 | } |
---|
682 | len = self.filestack.length; |
---|
683 | self.hasInitData = false; |
---|
684 | if (self.uploadAsync && self.showPreview) { |
---|
685 | outData = self.getOutData(); |
---|
686 | self.raise('filebatchpreupload', [outData]); |
---|
687 | self.fileBatchCompleted = false; |
---|
688 | self.uploadCache = {content: [], config: [], tags: [], append: true}; |
---|
689 | for (i = 0; i < len; i += 1) { |
---|
690 | if (self.filestack[i] !== undefined) { |
---|
691 | self.uploadSingle(i, self.filestack, true); |
---|
692 | } |
---|
693 | } |
---|
694 | return; |
---|
695 | } |
---|
696 | self.uploadBatch(); |
---|
697 | }, |
---|
698 | lock: function () { |
---|
699 | var self = this; |
---|
700 | self.resetErrors(); |
---|
701 | self.disable(); |
---|
702 | if (self.showRemove) { |
---|
703 | addCss(self.$container.find('.fileinput-remove'), 'hide'); |
---|
704 | } |
---|
705 | if (self.showCancel) { |
---|
706 | self.$container.find('.fileinput-cancel').removeClass('hide'); |
---|
707 | } |
---|
708 | self.raise('filelock', [self.filestack, self.getExtraData()]); |
---|
709 | }, |
---|
710 | unlock: function (reset) { |
---|
711 | var self = this; |
---|
712 | if (reset === undefined) { |
---|
713 | reset = true; |
---|
714 | } |
---|
715 | self.enable(); |
---|
716 | if (self.showCancel) { |
---|
717 | addCss(self.$container.find('.fileinput-cancel'), 'hide'); |
---|
718 | } |
---|
719 | if (self.showRemove) { |
---|
720 | self.$container.find('.fileinput-remove').removeClass('hide'); |
---|
721 | } |
---|
722 | if (reset) { |
---|
723 | self.resetFileStack(); |
---|
724 | } |
---|
725 | self.raise('fileunlock', [self.filestack, self.getExtraData()]); |
---|
726 | }, |
---|
727 | resetFileStack: function () { |
---|
728 | var self = this, i = 0, newstack = []; |
---|
729 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
730 | var $thumb = $(this), ind = $thumb.attr('data-fileindex'), |
---|
731 | file = self.filestack[ind]; |
---|
732 | if (ind === -1) { |
---|
733 | return; |
---|
734 | } |
---|
735 | if (file !== undefined) { |
---|
736 | newstack[i] = file; |
---|
737 | $thumb.attr({ |
---|
738 | 'id': self.previewInitId + '-' + i, |
---|
739 | 'data-fileindex': i |
---|
740 | }); |
---|
741 | i += 1; |
---|
742 | } else { |
---|
743 | $thumb.attr({ |
---|
744 | 'id': 'uploaded-' + uniqId(), |
---|
745 | 'data-fileindex': '-1' |
---|
746 | }); |
---|
747 | } |
---|
748 | }); |
---|
749 | self.filestack = newstack; |
---|
750 | }, |
---|
751 | refresh: function (options) { |
---|
752 | var self = this, $el = self.$element, $zone, |
---|
753 | params = (arguments.length) ? $.extend(self.options, options) : self.options; |
---|
754 | $el.off(); |
---|
755 | self.init(params); |
---|
756 | $zone = self.$container.find('.file-drop-zone'); |
---|
757 | $zone.off('dragenter dragover drop'); |
---|
758 | $(document).off('dragenter dragover drop'); |
---|
759 | self.listen(); |
---|
760 | self.setFileDropZoneTitle(); |
---|
761 | }, |
---|
762 | initDragDrop: function () { |
---|
763 | var self = this, $zone = self.$container.find('.file-drop-zone'); |
---|
764 | $zone.off('dragenter dragover drop'); |
---|
765 | $(document).off('dragenter dragover drop'); |
---|
766 | $zone.on('dragenter dragover', function (e) { |
---|
767 | e.stopPropagation(); |
---|
768 | e.preventDefault(); |
---|
769 | if (self.isDisabled) { |
---|
770 | return; |
---|
771 | } |
---|
772 | addCss($(this), 'highlighted'); |
---|
773 | }); |
---|
774 | $zone.on('dragleave', function (e) { |
---|
775 | e.stopPropagation(); |
---|
776 | e.preventDefault(); |
---|
777 | if (self.isDisabled) { |
---|
778 | return; |
---|
779 | } |
---|
780 | $(this).removeClass('highlighted'); |
---|
781 | }); |
---|
782 | $zone.on('drop', function (e) { |
---|
783 | e.preventDefault(); |
---|
784 | if (self.isDisabled) { |
---|
785 | return; |
---|
786 | } |
---|
787 | self.change(e, 'dragdrop'); |
---|
788 | $(this).removeClass('highlighted'); |
---|
789 | }); |
---|
790 | $(document).on('dragenter dragover drop', function (e) { |
---|
791 | e.stopPropagation(); |
---|
792 | e.preventDefault(); |
---|
793 | }); |
---|
794 | }, |
---|
795 | setFileDropZoneTitle: function () { |
---|
796 | var self = this, $zone = self.$container.find('.file-drop-zone'); |
---|
797 | $zone.find('.' + self.dropZoneTitleClass).remove(); |
---|
798 | if (!self.isUploadable || !self.showPreview || $zone.length === 0 || self.getFileStack().length > 0 || !self.dropZoneEnabled) { |
---|
799 | return; |
---|
800 | } |
---|
801 | if ($zone.find('.file-preview-frame').length === 0) { |
---|
802 | $zone.prepend('<div class="' + self.dropZoneTitleClass + '">' + self.dropZoneTitle + '</div>'); |
---|
803 | } |
---|
804 | self.$container.removeClass('file-input-new'); |
---|
805 | addCss(self.$container, 'file-input-ajax-new'); |
---|
806 | }, |
---|
807 | initFileActions: function () { |
---|
808 | var self = this; |
---|
809 | self.$preview.find('.kv-file-remove').each(function () { |
---|
810 | var $el = $(this), $frame = $el.closest('.file-preview-frame'), |
---|
811 | ind = $frame.attr('data-fileindex'), n, cap; |
---|
812 | $el.off('click').on('click', function () { |
---|
813 | $frame.fadeOut('slow', function () { |
---|
814 | self.filestack[ind] = undefined; |
---|
815 | self.clearObjects($frame); |
---|
816 | $frame.remove(); |
---|
817 | var filestack = self.getFileStack(), len = filestack.length, |
---|
818 | chk = previewCache.count(self.id); |
---|
819 | self.clearFileInput(); |
---|
820 | if (len === 0 && chk === 0) { |
---|
821 | self.reset(); |
---|
822 | } else { |
---|
823 | n = chk + len; |
---|
824 | cap = n > 1 ? self.msgSelected.repl('{n}', n) : filestack[0].name; |
---|
825 | self.setCaption(cap); |
---|
826 | } |
---|
827 | }); |
---|
828 | }); |
---|
829 | }); |
---|
830 | self.$preview.find('.kv-file-upload').each(function () { |
---|
831 | var $el = $(this); |
---|
832 | $el.off('click').on('click', function () { |
---|
833 | var $frame = $el.closest('.file-preview-frame'), |
---|
834 | ind = $frame.attr('data-fileindex'); |
---|
835 | self.uploadSingle(ind, self.filestack, false); |
---|
836 | }); |
---|
837 | }); |
---|
838 | }, |
---|
839 | renderFileFooter: function (caption, width) { |
---|
840 | var self = this, config = self.fileActionSettings, footer, out, |
---|
841 | template = self.getLayoutTemplate('footer'); |
---|
842 | if (self.isUploadable) { |
---|
843 | footer = template.repl('{actions}', self.renderFileActions(true, true, false, false, false, false)); |
---|
844 | out = footer.repl('{caption}', caption) |
---|
845 | .repl('{width}', width) |
---|
846 | .repl('{indicator}', config.indicatorNew) |
---|
847 | .repl('{indicatorTitle}', config.indicatorNewTitle); |
---|
848 | } else { |
---|
849 | out = template.repl('{actions}', '') |
---|
850 | .repl('{caption}', caption) |
---|
851 | .repl('{width}', width) |
---|
852 | .repl('{indicator}', '') |
---|
853 | .repl('{indicatorTitle}', ''); |
---|
854 | } |
---|
855 | out = replaceTags(out, self.previewThumbTags); |
---|
856 | return out; |
---|
857 | }, |
---|
858 | renderFileActions: function (showUpload, showDelete, disabled, url, key, index) { |
---|
859 | if (!showUpload && !showDelete) { |
---|
860 | return ''; |
---|
861 | } |
---|
862 | var self = this, |
---|
863 | vUrl = url === false ? '' : ' data-url="' + url + '"', |
---|
864 | vKey = key === false ? '' : ' data-key="' + key + '"', |
---|
865 | btnDelete = self.getLayoutTemplate('actionDelete'), |
---|
866 | btnUpload = '', |
---|
867 | template = self.getLayoutTemplate('actions'), |
---|
868 | otherButtons = self.otherActionButtons.repl('{dataKey}', vKey), |
---|
869 | config = self.fileActionSettings, |
---|
870 | removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; |
---|
871 | btnDelete = btnDelete |
---|
872 | .repl('{removeClass}', removeClass) |
---|
873 | .repl('{removeIcon}', config.removeIcon) |
---|
874 | .repl('{removeTitle}', config.removeTitle) |
---|
875 | .repl('{dataUrl}', vUrl) |
---|
876 | .repl('{dataKey}', vKey); |
---|
877 | if (showUpload) { |
---|
878 | btnUpload = self.getLayoutTemplate('actionUpload') |
---|
879 | .repl('{uploadClass}', config.uploadClass) |
---|
880 | .repl('{uploadIcon}', config.uploadIcon) |
---|
881 | .repl('{uploadTitle}', config.uploadTitle); |
---|
882 | } |
---|
883 | return template |
---|
884 | .repl('{delete}', btnDelete) |
---|
885 | .repl('{upload}', btnUpload) |
---|
886 | .repl('{other}', otherButtons); |
---|
887 | }, |
---|
888 | initPreview: function (isInit) { |
---|
889 | var self = this, cap, out; |
---|
890 | if (!previewCache.count(self.id)) { |
---|
891 | self.$preview.html(''); |
---|
892 | self.setCaption(''); |
---|
893 | return; |
---|
894 | } |
---|
895 | out = previewCache.out(self.id); |
---|
896 | cap = isInit && self.initialCaption ? self.initialCaption : out.caption; |
---|
897 | self.$preview.html(out.content); |
---|
898 | self.setCaption(cap); |
---|
899 | if (!isEmpty(out.content)) { |
---|
900 | self.$container.removeClass('file-input-new'); |
---|
901 | } |
---|
902 | }, |
---|
903 | initPreviewDeletes: function () { |
---|
904 | var self = this, deleteExtraData = self.deleteExtraData || {}, |
---|
905 | resetProgress = function () { |
---|
906 | if (self.$preview.find('.kv-file-remove').length === 0) { |
---|
907 | self.reset(); |
---|
908 | } |
---|
909 | }; |
---|
910 | self.$preview.find('.kv-file-remove').each(function () { |
---|
911 | var $el = $(this), $frame = $el.closest('.file-preview-frame'), |
---|
912 | cache = previewCache.data[self.id], index, config, extraData, |
---|
913 | vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'), settings, |
---|
914 | params = {id: $el.attr('id'), key: vKey, extra: extraData}; |
---|
915 | if (typeof extraData === "function") { |
---|
916 | extraData = extraData(); |
---|
917 | } |
---|
918 | if (vUrl === undefined || vKey === undefined) { |
---|
919 | return; |
---|
920 | } |
---|
921 | settings = $.extend({ |
---|
922 | url: vUrl, |
---|
923 | type: 'DELETE', |
---|
924 | dataType: 'json', |
---|
925 | data: $.extend({key: vKey}, extraData), |
---|
926 | beforeSend: function (jqXHR) { |
---|
927 | addCss($frame, 'file-uploading'); |
---|
928 | addCss($el, 'disabled'); |
---|
929 | self.raise('filepredelete', [vKey, jqXHR, extraData]); |
---|
930 | }, |
---|
931 | success: function (data, textStatus, jqXHR) { |
---|
932 | index = parseInt($frame.data('fileindex').replace('init_', '')); |
---|
933 | config = isEmpty(cache.config) && isEmpty(cache.config[index]) ? null : cache.config[index]; |
---|
934 | extraData = isEmpty(config) || isEmpty(config.extra) ? deleteExtraData : config.extra; |
---|
935 | if (data === undefined || data.error === undefined) { |
---|
936 | previewCache.unset(self.id, index); |
---|
937 | self.raise('filedeleted', [vKey, jqXHR, extraData]); |
---|
938 | } else { |
---|
939 | params.jqXHR = jqXHR; |
---|
940 | params.response = data; |
---|
941 | self.showError(data.error, params, 'filedeleteerror'); |
---|
942 | $frame.removeClass('file-uploading'); |
---|
943 | $el.removeClass('disabled'); |
---|
944 | resetProgress(); |
---|
945 | return; |
---|
946 | } |
---|
947 | $frame.removeClass('file-uploading').addClass('file-deleted'); |
---|
948 | $frame.fadeOut('slow', function () { |
---|
949 | self.clearObjects($frame); |
---|
950 | $frame.remove(); |
---|
951 | resetProgress(); |
---|
952 | if (!previewCache.count(self.id) && self.getFileStack().length === 0) { |
---|
953 | self.reset(); |
---|
954 | } |
---|
955 | }); |
---|
956 | }, |
---|
957 | error: function (jqXHR, textStatus, errorThrown) { |
---|
958 | var errMsg = self.parseError(jqXHR, errorThrown); |
---|
959 | params.jqXHR = jqXHR; |
---|
960 | params.response = {}; |
---|
961 | self.showError(errMsg, params, 'filedeleteerror'); |
---|
962 | $frame.removeClass('file-uploading'); |
---|
963 | resetProgress(); |
---|
964 | } |
---|
965 | }, self.ajaxDeleteSettings); |
---|
966 | $el.off('click').on('click', function () { |
---|
967 | $.ajax(settings); |
---|
968 | }); |
---|
969 | }); |
---|
970 | }, |
---|
971 | clearObjects: function ($el) { |
---|
972 | $el.find('video audio').each(function () { |
---|
973 | this.pause(); |
---|
974 | $(this).remove(); |
---|
975 | }); |
---|
976 | $el.find('img object div').each(function () { |
---|
977 | $(this).remove(); |
---|
978 | }); |
---|
979 | }, |
---|
980 | clearFileInput: function () { |
---|
981 | var self = this, $el = self.$element, $srcFrm, $tmpFrm, $tmpEl; |
---|
982 | if (isEmpty($el.val())) { |
---|
983 | return; |
---|
984 | } |
---|
985 | // Fix for IE ver < 11, that does not clear file inputs |
---|
986 | // Requires a sequence of steps to prevent IE crashing but |
---|
987 | // still allow clearing of the file input. |
---|
988 | if (self.isIE9 || self.isIE10) { |
---|
989 | $srcFrm = $el.closest('form'); |
---|
990 | $tmpFrm = $(document.createElement('form')); |
---|
991 | $tmpEl = $(document.createElement('div')); |
---|
992 | $el.before($tmpEl); |
---|
993 | if ($srcFrm.length) { |
---|
994 | $srcFrm.after($tmpFrm); |
---|
995 | } else { |
---|
996 | $tmpEl.after($tmpFrm); |
---|
997 | } |
---|
998 | $tmpFrm.append($el).trigger('reset'); |
---|
999 | $tmpEl.before($el).remove(); |
---|
1000 | $tmpFrm.remove(); |
---|
1001 | } else { // normal input clear behavior for other sane browsers |
---|
1002 | $el.val(''); |
---|
1003 | } |
---|
1004 | self.fileInputCleared = true; |
---|
1005 | }, |
---|
1006 | resetUpload: function () { |
---|
1007 | var self = this; |
---|
1008 | self.uploadCache = {content: [], config: [], tags: [], append: true}; |
---|
1009 | self.uploadCount = 0; |
---|
1010 | self.uploadPercent = 0; |
---|
1011 | self.$btnUpload.removeAttr('disabled'); |
---|
1012 | self.setProgress(0); |
---|
1013 | addCss(self.$progress, 'hide'); |
---|
1014 | self.resetErrors(false); |
---|
1015 | self.uploadAborted = false; |
---|
1016 | self.ajaxRequests = []; |
---|
1017 | }, |
---|
1018 | cancel: function () { |
---|
1019 | var self = this, xhr = self.ajaxRequests, len = xhr.length, i; |
---|
1020 | if (len > 0) { |
---|
1021 | for (i = 0; i < len; i += 1) { |
---|
1022 | xhr[i].abort(); |
---|
1023 | } |
---|
1024 | } |
---|
1025 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1026 | var $thumb = $(this), ind = $thumb.attr('data-fileindex'); |
---|
1027 | $thumb.removeClass('file-uploading'); |
---|
1028 | if (self.filestack[ind] !== undefined) { |
---|
1029 | $thumb.find('.kv-file-upload').removeClass('disabled').removeAttr('disabled'); |
---|
1030 | $thumb.find('.kv-file-remove').removeClass('disabled').removeAttr('disabled'); |
---|
1031 | } |
---|
1032 | self.unlock(); |
---|
1033 | }); |
---|
1034 | }, |
---|
1035 | clear: function () { |
---|
1036 | var self = this, cap; |
---|
1037 | self.$btnUpload.removeAttr('disabled'); |
---|
1038 | self.resetUpload(); |
---|
1039 | self.filestack = []; |
---|
1040 | self.clearFileInput(); |
---|
1041 | self.resetErrors(true); |
---|
1042 | self.raise('fileclear'); |
---|
1043 | if (!self.overwriteInitial && previewCache.count(self.id)) { |
---|
1044 | self.showFileIcon(); |
---|
1045 | self.resetPreview(); |
---|
1046 | self.setEllipsis(); |
---|
1047 | self.initPreviewDeletes(); |
---|
1048 | self.$container.removeClass('file-input-new'); |
---|
1049 | } else { |
---|
1050 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1051 | self.clearObjects($(this)); |
---|
1052 | }); |
---|
1053 | self.$preview.html(''); |
---|
1054 | cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? self.initialCaption : ''; |
---|
1055 | self.$caption.html(cap); |
---|
1056 | self.setEllipsis(); |
---|
1057 | self.$caption.attr('title', ''); |
---|
1058 | addCss(self.$container, 'file-input-new'); |
---|
1059 | } |
---|
1060 | if (self.$container.find('.file-preview-frame').length === 0) { |
---|
1061 | self.initialCaption = ''; |
---|
1062 | self.$caption.html(''); |
---|
1063 | self.setEllipsis(); |
---|
1064 | self.$captionContainer.find('.kv-caption-icon').hide(); |
---|
1065 | } |
---|
1066 | self.hideFileIcon(); |
---|
1067 | self.raise('filecleared'); |
---|
1068 | self.$captionContainer.focus(); |
---|
1069 | self.setFileDropZoneTitle(); |
---|
1070 | }, |
---|
1071 | resetPreview: function () { |
---|
1072 | var self = this, out; |
---|
1073 | if (previewCache.count(self.id)) { |
---|
1074 | out = previewCache.out(self.id); |
---|
1075 | self.$preview.html(out.content); |
---|
1076 | self.setCaption(out.caption); |
---|
1077 | } else { |
---|
1078 | self.$preview.html(''); |
---|
1079 | self.$caption.html(''); |
---|
1080 | } |
---|
1081 | }, |
---|
1082 | reset: function () { |
---|
1083 | var self = this; |
---|
1084 | self.clear(); |
---|
1085 | self.resetPreview(); |
---|
1086 | self.setEllipsis(); |
---|
1087 | self.$container.find('.fileinput-filename').text(''); |
---|
1088 | self.raise('filereset'); |
---|
1089 | if (self.initialPreview.length > 0) { |
---|
1090 | self.$container.removeClass('file-input-new'); |
---|
1091 | } |
---|
1092 | self.setFileDropZoneTitle(); |
---|
1093 | self.filestack = []; |
---|
1094 | self.formdata = {}; |
---|
1095 | }, |
---|
1096 | disable: function () { |
---|
1097 | var self = this; |
---|
1098 | self.isDisabled = true; |
---|
1099 | self.raise('filedisabled'); |
---|
1100 | self.$element.attr('disabled', 'disabled'); |
---|
1101 | self.$container.find(".kv-fileinput-caption").addClass("file-caption-disabled"); |
---|
1102 | self.$container.find(".btn-file, .fileinput-remove, .kv-fileinput-upload").attr("disabled", true); |
---|
1103 | self.initDragDrop(); |
---|
1104 | }, |
---|
1105 | enable: function () { |
---|
1106 | var self = this; |
---|
1107 | self.isDisabled = false; |
---|
1108 | self.raise('fileenabled'); |
---|
1109 | self.$element.removeAttr('disabled'); |
---|
1110 | self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled"); |
---|
1111 | self.$container.find(".btn-file, .fileinput-remove, .kv-fileinput-upload").removeAttr("disabled"); |
---|
1112 | self.initDragDrop(); |
---|
1113 | }, |
---|
1114 | getExtraData: function () { |
---|
1115 | var self = this, data = self.uploadExtraData; |
---|
1116 | if (typeof self.uploadExtraData === "function") { |
---|
1117 | data = self.uploadExtraData(); |
---|
1118 | } |
---|
1119 | return data; |
---|
1120 | }, |
---|
1121 | uploadExtra: function () { |
---|
1122 | var self = this, data = self.getExtraData(); |
---|
1123 | if (data.length === 0) { |
---|
1124 | return; |
---|
1125 | } |
---|
1126 | $.each(data, function (key, value) { |
---|
1127 | self.formdata.append(key, value); |
---|
1128 | }); |
---|
1129 | }, |
---|
1130 | initXhr: function (xhrobj, factor) { |
---|
1131 | var self = this; |
---|
1132 | if (xhrobj.upload) { |
---|
1133 | xhrobj.upload.addEventListener('progress', function (event) { |
---|
1134 | var pct = 0, position = event.loaded || event.position, total = event.total; |
---|
1135 | if (event.lengthComputable) { |
---|
1136 | pct = Math.ceil(position / total * factor); |
---|
1137 | } |
---|
1138 | self.uploadPercent = Math.max(pct, self.uploadPercent); |
---|
1139 | self.setProgress(self.uploadPercent); |
---|
1140 | }, false); |
---|
1141 | } |
---|
1142 | return xhrobj; |
---|
1143 | }, |
---|
1144 | ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError) { |
---|
1145 | var self = this, settings; |
---|
1146 | self.uploadExtra(); |
---|
1147 | settings = $.extend({ |
---|
1148 | xhr: function () { |
---|
1149 | var xhrobj = $.ajaxSettings.xhr(); |
---|
1150 | return self.initXhr(xhrobj, 98); |
---|
1151 | }, |
---|
1152 | url: self.uploadUrl, |
---|
1153 | type: 'POST', |
---|
1154 | dataType: 'json', |
---|
1155 | data: self.formdata, |
---|
1156 | cache: false, |
---|
1157 | processData: false, |
---|
1158 | contentType: false, |
---|
1159 | beforeSend: fnBefore, |
---|
1160 | success: fnSuccess, |
---|
1161 | complete: fnComplete, |
---|
1162 | error: fnError |
---|
1163 | }, self.ajaxSettings); |
---|
1164 | self.ajaxRequests.push($.ajax(settings)); |
---|
1165 | }, |
---|
1166 | initUploadSuccess: function (out, $thumb, allFiles) { |
---|
1167 | var self = this, append, data, index, $newThumb, content, config, tags; |
---|
1168 | if (typeof out !== 'object' || $.isEmptyObject(out)) { |
---|
1169 | return; |
---|
1170 | } |
---|
1171 | if (out.initialPreview !== undefined && out.initialPreview.length > 0) { |
---|
1172 | self.hasInitData = true; |
---|
1173 | content = out.initialPreview || []; |
---|
1174 | config = out.initialPreviewConfig || []; |
---|
1175 | tags = out.initialPreviewThumbTags || []; |
---|
1176 | append = out.append === undefined || out.append ? true : false; |
---|
1177 | self.overwriteInitial = false; |
---|
1178 | if ($thumb !== undefined && !!allFiles) { |
---|
1179 | index = previewCache.add(self.id, content, config[0], tags[0], append); |
---|
1180 | data = previewCache.get(self.id, index, false); |
---|
1181 | $newThumb = $(data).hide(); |
---|
1182 | $thumb.after($newThumb).fadeOut('slow', function () { |
---|
1183 | $newThumb.fadeIn('slow').css('display:inline-block'); |
---|
1184 | self.initPreviewDeletes(); |
---|
1185 | }); |
---|
1186 | } else { |
---|
1187 | if (allFiles) { |
---|
1188 | self.uploadCache.content.push(content[0]); |
---|
1189 | self.uploadCache.config.push(config[0]); |
---|
1190 | self.uploadCache.tags.push(tags[0]); |
---|
1191 | self.uploadCache.append = append; |
---|
1192 | } else { |
---|
1193 | previewCache.set(self.id, content, config, tags, append); |
---|
1194 | self.initPreview(); |
---|
1195 | self.initPreviewDeletes(); |
---|
1196 | } |
---|
1197 | } |
---|
1198 | } |
---|
1199 | }, |
---|
1200 | uploadSingle: function (i, files, allFiles) { |
---|
1201 | var self = this, total = self.getFileStack().length, formdata = new FormData(), outData, |
---|
1202 | previewId = self.previewInitId + "-" + i, $thumb = $('#' + previewId + ':not(.file-preview-initial)'), |
---|
1203 | pct, chkComplete, $btnUpload = $thumb.find('.kv-file-upload'), $btnDelete = $thumb.find('.kv-file-remove'), |
---|
1204 | $indicator = $thumb.find('.file-upload-indicator'), config = self.fileActionSettings, |
---|
1205 | hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), |
---|
1206 | setIndicator, updateProgress, resetActions, fnBefore, fnSuccess, fnComplete, fnError, |
---|
1207 | params = {id: previewId, index: i}; |
---|
1208 | self.formdata = formdata; |
---|
1209 | if (total === 0 || !hasPostData || $btnUpload.hasClass('disabled') || self.abort(params)) { |
---|
1210 | return; |
---|
1211 | } |
---|
1212 | chkComplete = function () { |
---|
1213 | var $thumbs = self.$preview.find(PREVIEW_FRAMES + '.file-uploading'); |
---|
1214 | if ($thumbs.length > 0 && self.fileBatchCompleted) { |
---|
1215 | return; |
---|
1216 | } |
---|
1217 | previewCache.set(self.id, self.uploadCache.content, self.uploadCache.config, self.uploadCache.tags, |
---|
1218 | self.uploadCache.append); |
---|
1219 | if (self.hasInitData) { |
---|
1220 | self.initPreview(); |
---|
1221 | self.initPreviewDeletes(); |
---|
1222 | } |
---|
1223 | self.setProgress(100); |
---|
1224 | self.unlock(); |
---|
1225 | self.clearFileInput(); |
---|
1226 | self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); |
---|
1227 | self.fileBatchCompleted = true; |
---|
1228 | }; |
---|
1229 | setIndicator = function (icon, msg) { |
---|
1230 | $indicator.html(config[icon]); |
---|
1231 | $indicator.attr('title', config[msg]); |
---|
1232 | }; |
---|
1233 | updateProgress = function () { |
---|
1234 | if (!allFiles || total === 0 || self.uploadPercent >= 100) { |
---|
1235 | return; |
---|
1236 | } |
---|
1237 | self.uploadCount += 1; |
---|
1238 | pct = 80 + Math.ceil(self.uploadCount * 20 / total); |
---|
1239 | self.uploadPercent = Math.max(pct, self.uploadPercent); |
---|
1240 | self.setProgress(self.uploadPercent); |
---|
1241 | self.initPreviewDeletes(); |
---|
1242 | }; |
---|
1243 | resetActions = function () { |
---|
1244 | $btnUpload.removeAttr('disabled'); |
---|
1245 | $btnDelete.removeAttr('disabled'); |
---|
1246 | $thumb.removeClass('file-uploading'); |
---|
1247 | }; |
---|
1248 | fnBefore = function (jqXHR) { |
---|
1249 | outData = self.getOutData(jqXHR); |
---|
1250 | setIndicator('indicatorLoading', 'indicatorLoadingTitle'); |
---|
1251 | addCss($thumb, 'file-uploading'); |
---|
1252 | $btnUpload.attr('disabled', true); |
---|
1253 | $btnDelete.attr('disabled', true); |
---|
1254 | if (!allFiles) { |
---|
1255 | self.lock(); |
---|
1256 | } |
---|
1257 | self.raise('filepreupload', [outData, previewId, i]); |
---|
1258 | params = $.extend(params, outData); |
---|
1259 | if (self.abort(params)) { |
---|
1260 | jqXHR.abort(); |
---|
1261 | self.setProgress(100); |
---|
1262 | } |
---|
1263 | }; |
---|
1264 | fnSuccess = function (data, textStatus, jqXHR) { |
---|
1265 | outData = self.getOutData(jqXHR, data); |
---|
1266 | params = $.extend(params, outData); |
---|
1267 | setTimeout(function () { |
---|
1268 | if (data.error === undefined) { |
---|
1269 | setIndicator('indicatorSuccess', 'indicatorSuccessTitle'); |
---|
1270 | $btnUpload.hide(); |
---|
1271 | $btnDelete.hide(); |
---|
1272 | self.filestack[i] = undefined; |
---|
1273 | self.raise('fileuploaded', [outData, previewId, i]); |
---|
1274 | self.initUploadSuccess(data, $thumb, allFiles); |
---|
1275 | if (!allFiles) { |
---|
1276 | self.resetFileStack(); |
---|
1277 | } |
---|
1278 | } else { |
---|
1279 | setIndicator('indicatorError', 'indicatorErrorTitle'); |
---|
1280 | self.showUploadError(data.error, params); |
---|
1281 | } |
---|
1282 | }, 100); |
---|
1283 | }; |
---|
1284 | fnComplete = function () { |
---|
1285 | setTimeout(function () { |
---|
1286 | updateProgress(); |
---|
1287 | resetActions(); |
---|
1288 | if (!allFiles) { |
---|
1289 | self.unlock(false); |
---|
1290 | } else { |
---|
1291 | setTimeout(function () { |
---|
1292 | chkComplete(); |
---|
1293 | }, 500); |
---|
1294 | } |
---|
1295 | }, 100); |
---|
1296 | }; |
---|
1297 | fnError = function (jqXHR, textStatus, errorThrown) { |
---|
1298 | var errMsg = self.parseError(jqXHR, errorThrown, (allFiles ? files[i].name : null)); |
---|
1299 | setIndicator('indicatorError', 'indicatorErrorTitle'); |
---|
1300 | params = $.extend(params, self.getOutData(jqXHR)); |
---|
1301 | self.showUploadError(errMsg, params); |
---|
1302 | }; |
---|
1303 | formdata.append(self.uploadFileAttr, files[i]); |
---|
1304 | formdata.append('file_id', i); |
---|
1305 | self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); |
---|
1306 | }, |
---|
1307 | uploadBatch: function () { |
---|
1308 | var self = this, files = self.filestack, total = files.length, config, |
---|
1309 | hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), |
---|
1310 | setIndicator, setAllUploaded, enableActions, fnBefore, fnSuccess, fnComplete, fnError, |
---|
1311 | params = {}; |
---|
1312 | self.formdata = new FormData(); |
---|
1313 | if (total === 0 || !hasPostData || self.abort(params)) { |
---|
1314 | return; |
---|
1315 | } |
---|
1316 | config = self.fileActionSettings; |
---|
1317 | setIndicator = function (i, icon, msg) { |
---|
1318 | var $indicator = $('#' + self.previewInitId + "-" + i).find('.file-upload-indicator'); |
---|
1319 | $indicator.html(config[icon]); |
---|
1320 | $indicator.attr('title', config[msg]); |
---|
1321 | }; |
---|
1322 | enableActions = function (i) { |
---|
1323 | var $thumb = $('#' + self.previewInitId + "-" + i + ':not(.file-preview-initial)'), |
---|
1324 | $btnUpload = $thumb.find('.kv-file-upload'), |
---|
1325 | $btnDelete = $thumb.find('.kv-file-delete'); |
---|
1326 | $thumb.removeClass('file-uploading'); |
---|
1327 | $btnUpload.removeAttr('disabled'); |
---|
1328 | $btnDelete.removeAttr('disabled'); |
---|
1329 | }; |
---|
1330 | setAllUploaded = function () { |
---|
1331 | $.each(files, function (key, data) { |
---|
1332 | self.filestack[key] = undefined; |
---|
1333 | }); |
---|
1334 | self.clearFileInput(); |
---|
1335 | }; |
---|
1336 | fnBefore = function (jqXHR) { |
---|
1337 | self.lock(); |
---|
1338 | var outData = self.getOutData(jqXHR); |
---|
1339 | if (self.showPreview) { |
---|
1340 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1341 | var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), $btnDelete = $thumb.find('.kv-file-remove'); |
---|
1342 | addCss($thumb, 'file-uploading'); |
---|
1343 | $btnUpload.attr('disabled', true); |
---|
1344 | $btnDelete.attr('disabled', true); |
---|
1345 | }); |
---|
1346 | } |
---|
1347 | self.raise('filebatchpreupload', [outData]); |
---|
1348 | if (self.abort(outData)) { |
---|
1349 | jqXHR.abort(); |
---|
1350 | } |
---|
1351 | }; |
---|
1352 | fnSuccess = function (data, textStatus, jqXHR) { |
---|
1353 | var outData = self.getOutData(jqXHR, data), |
---|
1354 | keys = isEmpty(data.errorkeys) ? [] : data.errorkeys; |
---|
1355 | if (data.error === undefined || isEmpty(data.error)) { |
---|
1356 | self.raise('filebatchuploadsuccess', [outData]); |
---|
1357 | setAllUploaded(); |
---|
1358 | if (self.showPreview) { |
---|
1359 | self.$preview.find('.kv-file-upload').hide(); |
---|
1360 | self.$preview.find('.kv-file-remove').hide(); |
---|
1361 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1362 | var $thumb = $(this), key = $thumb.attr('data-fileindex'); |
---|
1363 | setIndicator(key, 'indicatorSuccess', 'indicatorSuccessTitle'); |
---|
1364 | enableActions(key); |
---|
1365 | }); |
---|
1366 | self.initUploadSuccess(data); |
---|
1367 | } else { |
---|
1368 | self.reset(); |
---|
1369 | } |
---|
1370 | } else { |
---|
1371 | if (self.showPreview) { |
---|
1372 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1373 | var $thumb = $(this), key = parseInt($thumb.attr('data-fileindex'), 10); |
---|
1374 | enableActions(key); |
---|
1375 | if (keys.length === 0) { |
---|
1376 | setIndicator(key, 'indicatorError', 'indicatorErrorTitle'); |
---|
1377 | return; |
---|
1378 | } |
---|
1379 | if ($.inArray(key, keys) !== -1) { |
---|
1380 | setIndicator(key, 'indicatorError', 'indicatorErrorTitle'); |
---|
1381 | } else { |
---|
1382 | $thumb.find('.kv-file-upload').hide(); |
---|
1383 | $thumb.find('.kv-file-remove').hide(); |
---|
1384 | setIndicator(key, 'indicatorSuccess', 'indicatorSuccessTitle'); |
---|
1385 | self.filestack[key] = undefined; |
---|
1386 | } |
---|
1387 | }); |
---|
1388 | self.initUploadSuccess(data); |
---|
1389 | } |
---|
1390 | self.showUploadError(data.error, outData, 'filebatchuploaderror'); |
---|
1391 | } |
---|
1392 | }; |
---|
1393 | fnComplete = function () { |
---|
1394 | self.setProgress(100); |
---|
1395 | self.unlock(); |
---|
1396 | self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); |
---|
1397 | self.clearFileInput(); |
---|
1398 | }; |
---|
1399 | fnError = function (jqXHR, textStatus, errorThrown) { |
---|
1400 | var outData = self.getOutData(jqXHR), errMsg = self.parseError(jqXHR, errorThrown); |
---|
1401 | self.showUploadError(errMsg, outData, 'filebatchuploaderror'); |
---|
1402 | self.uploadFileCount = total - 1; |
---|
1403 | if (!self.showPreview) { |
---|
1404 | return; |
---|
1405 | } |
---|
1406 | self.$preview.find(PREVIEW_FRAMES).each(function () { |
---|
1407 | var $thumb = $(this), key = $thumb.attr('data-fileindex'); |
---|
1408 | $thumb.removeClass('file-uploading'); |
---|
1409 | if (self.filestack[key] !== undefined) { |
---|
1410 | setIndicator(key, 'indicatorError', 'indicatorErrorTitle'); |
---|
1411 | } |
---|
1412 | }); |
---|
1413 | self.$preview.find(PREVIEW_FRAMES).removeClass('file-uploading'); |
---|
1414 | self.$preview.find(PREVIEW_FRAMES + ' .kv-file-upload').removeAttr('disabled'); |
---|
1415 | self.$preview.find(PREVIEW_FRAMES + ' .kv-file-delete').removeAttr('disabled'); |
---|
1416 | }; |
---|
1417 | $.each(files, function (key, data) { |
---|
1418 | if (!isEmpty(files[key])) { |
---|
1419 | self.formdata.append(self.uploadFileAttr, data); |
---|
1420 | } |
---|
1421 | }); |
---|
1422 | self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); |
---|
1423 | }, |
---|
1424 | uploadExtraOnly: function () { |
---|
1425 | var self = this, params = {}, fnBefore, fnSuccess, fnComplete, fnError; |
---|
1426 | self.formdata = new FormData(); |
---|
1427 | if (self.abort(params)) { |
---|
1428 | return; |
---|
1429 | } |
---|
1430 | fnBefore = function (jqXHR) { |
---|
1431 | self.lock(); |
---|
1432 | var outData = self.getOutData(jqXHR); |
---|
1433 | self.raise('filebatchpreupload', [outData]); |
---|
1434 | self.setProgress(50); |
---|
1435 | params.data = outData; |
---|
1436 | params.xhr = jqXHR; |
---|
1437 | if (self.abort(params)) { |
---|
1438 | jqXHR.abort(); |
---|
1439 | self.setProgress(100); |
---|
1440 | } |
---|
1441 | }; |
---|
1442 | fnSuccess = function (data, textStatus, jqXHR) { |
---|
1443 | var outData = self.getOutData(jqXHR, data); |
---|
1444 | if (data.error === undefined || isEmpty(data.error)) { |
---|
1445 | self.raise('filebatchuploadsuccess', [outData]); |
---|
1446 | self.clearFileInput(); |
---|
1447 | self.initUploadSuccess(data); |
---|
1448 | } else { |
---|
1449 | self.showUploadError(data.error, outData, 'filebatchuploaderror'); |
---|
1450 | } |
---|
1451 | }; |
---|
1452 | fnComplete = function () { |
---|
1453 | self.setProgress(100); |
---|
1454 | self.unlock(); |
---|
1455 | self.raise('filebatchuploadcomplete', [self.filestack, self.getExtraData()]); |
---|
1456 | self.clearFileInput(); |
---|
1457 | }; |
---|
1458 | fnError = function (jqXHR, textStatus, errorThrown) { |
---|
1459 | var outData = self.getOutData(jqXHR), errMsg = self.parseError(jqXHR, errorThrown); |
---|
1460 | params.data = outData; |
---|
1461 | self.showUploadError(errMsg, outData, 'filebatchuploaderror'); |
---|
1462 | }; |
---|
1463 | self.ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); |
---|
1464 | }, |
---|
1465 | hideFileIcon: function () { |
---|
1466 | if (this.overwriteInitial) { |
---|
1467 | this.$captionContainer.find('.kv-caption-icon').hide(); |
---|
1468 | } |
---|
1469 | }, |
---|
1470 | showFileIcon: function () { |
---|
1471 | this.$captionContainer.find('.kv-caption-icon').show(); |
---|
1472 | }, |
---|
1473 | resetErrors: function (fade) { |
---|
1474 | var self = this, $error = self.$errorContainer; |
---|
1475 | self.isError = false; |
---|
1476 | self.$container.removeClass('has-error'); |
---|
1477 | $error.html(''); |
---|
1478 | if (fade) { |
---|
1479 | $error.fadeOut('slow'); |
---|
1480 | } else { |
---|
1481 | $error.hide(); |
---|
1482 | } |
---|
1483 | }, |
---|
1484 | showFolderError: function (folders) { |
---|
1485 | var self = this, $error = self.$errorContainer; |
---|
1486 | if (!folders) { |
---|
1487 | return; |
---|
1488 | } |
---|
1489 | $error.html(self.msgFoldersNotAllowed.repl('{n}', folders)); |
---|
1490 | $error.fadeIn(800); |
---|
1491 | addCss(self.$container, 'has-error'); |
---|
1492 | self.raise('filefoldererror', [folders]); |
---|
1493 | }, |
---|
1494 | showUploadError: function (msg, params, event) { |
---|
1495 | var self = this, $error = self.$errorContainer, ev = event || 'fileuploaderror'; |
---|
1496 | if ($error.find('ul').length === 0) { |
---|
1497 | $error.html('<ul><li>' + msg + '</li></ul>'); |
---|
1498 | } else { |
---|
1499 | $error.find('ul').append('<li>' + msg + '</li>'); |
---|
1500 | } |
---|
1501 | $error.fadeIn(800); |
---|
1502 | self.raise(ev, [params]); |
---|
1503 | addCss(self.$container, 'has-error'); |
---|
1504 | return true; |
---|
1505 | }, |
---|
1506 | showError: function (msg, params, event) { |
---|
1507 | var self = this, $error = self.$errorContainer, ev = event || 'fileerror'; |
---|
1508 | params = params || {}; |
---|
1509 | params.reader = self.reader; |
---|
1510 | $error.html(msg); |
---|
1511 | $error.fadeIn(800); |
---|
1512 | self.raise(ev, [params]); |
---|
1513 | if (!self.isUploadable) { |
---|
1514 | self.clearFileInput(); |
---|
1515 | } |
---|
1516 | addCss(self.$container, 'has-error'); |
---|
1517 | self.$btnUpload.attr('disabled', true); |
---|
1518 | return true; |
---|
1519 | }, |
---|
1520 | errorHandler: function (evt, caption) { |
---|
1521 | var self = this, err = evt.target.error; |
---|
1522 | switch (err.code) { |
---|
1523 | case err.NOT_FOUND_ERR: |
---|
1524 | self.showError(self.msgFileNotFound.repl('{name}', caption)); |
---|
1525 | break; |
---|
1526 | case err.SECURITY_ERR: |
---|
1527 | self.showError(self.msgFileSecured.repl('{name}', caption)); |
---|
1528 | break; |
---|
1529 | case err.NOT_READABLE_ERR: |
---|
1530 | self.showError(self.msgFileNotReadable.repl('{name}', caption)); |
---|
1531 | break; |
---|
1532 | case err.ABORT_ERR: |
---|
1533 | self.showError(self.msgFilePreviewAborted.repl('{name}', caption)); |
---|
1534 | break; |
---|
1535 | default: |
---|
1536 | self.showError(self.msgFilePreviewError.repl('{name}', caption)); |
---|
1537 | } |
---|
1538 | }, |
---|
1539 | parseFileType: function (file) { |
---|
1540 | var self = this, isValid, vType, cat, i; |
---|
1541 | for (i = 0; i < defaultPreviewTypes.length; i += 1) { |
---|
1542 | cat = defaultPreviewTypes[i]; |
---|
1543 | isValid = isSet(cat, self.fileTypeSettings) ? self.fileTypeSettings[cat] : defaultFileTypeSettings[cat]; |
---|
1544 | vType = isValid(file.type, file.name) ? cat : ''; |
---|
1545 | if (!isEmpty(vType)) { |
---|
1546 | return vType; |
---|
1547 | } |
---|
1548 | } |
---|
1549 | return 'other'; |
---|
1550 | }, |
---|
1551 | previewDefault: function (file, previewId, isDisabled) { |
---|
1552 | if (!this.showPreview) { |
---|
1553 | return; |
---|
1554 | } |
---|
1555 | var self = this, data = objUrl.createObjectURL(file), $obj = $('#' + previewId), |
---|
1556 | config = self.previewSettings.other, |
---|
1557 | footer = self.renderFileFooter(file.name, config.width), |
---|
1558 | previewOtherTemplate = self.getPreviewTemplate('other'), |
---|
1559 | ind = previewId.slice(previewId.lastIndexOf('-') + 1), |
---|
1560 | frameClass = ''; |
---|
1561 | if (isDisabled === true) { |
---|
1562 | frameClass = ' btn disabled'; |
---|
1563 | footer += '<div class="file-other-error text-danger"><i class="fa fa-exclamation"></i></div>'; |
---|
1564 | } |
---|
1565 | self.$preview.append("\n" + previewOtherTemplate |
---|
1566 | .repl('{previewId}', previewId) |
---|
1567 | .repl('{frameClass}', frameClass) |
---|
1568 | .repl('{fileindex}', ind) |
---|
1569 | .repl('{caption}', self.slug(file.name)) |
---|
1570 | .repl('{width}', config.width) |
---|
1571 | .repl('{height}', config.height) |
---|
1572 | .repl('{type}', file.type) |
---|
1573 | .repl('{data}', data) |
---|
1574 | .repl('{footer}', footer)); |
---|
1575 | $obj.on('load', function () { |
---|
1576 | objUrl.revokeObjectURL($obj.attr('data')); |
---|
1577 | }); |
---|
1578 | }, |
---|
1579 | previewFile: function (file, theFile, previewId, data) { |
---|
1580 | if (!this.showPreview) { |
---|
1581 | return; |
---|
1582 | } |
---|
1583 | var self = this, cat = self.parseFileType(file), caption = self.slug(file.name), content, strText, |
---|
1584 | types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, |
---|
1585 | tmplt = self.getPreviewTemplate(cat), |
---|
1586 | config = isSet(cat, self.previewSettings) ? self.previewSettings[cat] : defaultPreviewSettings[cat], |
---|
1587 | wrapLen = parseInt(self.wrapTextLength, 10), wrapInd = self.wrapIndicator, |
---|
1588 | chkTypes = types.indexOf(cat) >= 0, id, height, |
---|
1589 | chkMimes = isEmpty(mimes) || (!isEmpty(mimes) && isSet(file.type, mimes)), |
---|
1590 | footer = self.renderFileFooter(caption, config.width), modal = '', |
---|
1591 | ind = previewId.slice(previewId.lastIndexOf('-') + 1); |
---|
1592 | if (chkTypes && chkMimes) { |
---|
1593 | if (cat === 'text') { |
---|
1594 | strText = htmlEncode(theFile.target.result); |
---|
1595 | objUrl.revokeObjectURL(data); |
---|
1596 | if (strText.length > wrapLen) { |
---|
1597 | id = 'text-' + uniqId(); |
---|
1598 | height = window.innerHeight * 0.75; |
---|
1599 | modal = self.getLayoutTemplate('modal').repl('{id}', id) |
---|
1600 | .repl('{title}', caption) |
---|
1601 | .repl('{height}', height) |
---|
1602 | .repl('{body}', strText); |
---|
1603 | wrapInd = wrapInd |
---|
1604 | .repl('{title}', caption) |
---|
1605 | .repl('{dialog}', "$('#" + id + "').modal('show')"); |
---|
1606 | strText = strText.substring(0, (wrapLen - 1)) + wrapInd; |
---|
1607 | } |
---|
1608 | content = tmplt.repl('{previewId}', previewId).repl('{caption}', caption) |
---|
1609 | .repl('{frameClass}', '') |
---|
1610 | .repl('{type}', file.type).repl('{width}', config.width) |
---|
1611 | .repl('{height}', config.height).repl('{data}', strText) |
---|
1612 | .repl('{footer}', footer).repl('{fileindex}', ind) + modal; |
---|
1613 | } else { |
---|
1614 | content = tmplt.repl('{previewId}', previewId).repl('{caption}', caption) |
---|
1615 | .repl('{frameClass}', '') |
---|
1616 | .repl('{type}', file.type).repl('{data}', data) |
---|
1617 | .repl('{width}', config.width).repl('{height}', config.height) |
---|
1618 | .repl('{footer}', footer).repl('{fileindex}', ind); |
---|
1619 | } |
---|
1620 | self.$preview.append("\n" + content); |
---|
1621 | self.autoSizeImage(previewId); |
---|
1622 | } else { |
---|
1623 | self.previewDefault(file, previewId); |
---|
1624 | } |
---|
1625 | }, |
---|
1626 | slugDefault: function (text) { |
---|
1627 | return isEmpty(text) ? '' : text.split(/(\\|\/)/g).pop().replace(/[^\w\-.\\\/ ]+/g, ''); |
---|
1628 | }, |
---|
1629 | getFileStack: function () { |
---|
1630 | var self = this; |
---|
1631 | return self.filestack.filter(function (n) { |
---|
1632 | return n !== undefined; |
---|
1633 | }); |
---|
1634 | }, |
---|
1635 | readFiles: function (files) { |
---|
1636 | this.reader = new FileReader(); |
---|
1637 | var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader, |
---|
1638 | $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading, |
---|
1639 | msgProgress = self.msgProgress, previewInitId = self.previewInitId, numFiles = files.length, |
---|
1640 | settings = self.fileTypeSettings, ctr = self.filestack.length, |
---|
1641 | throwError = function (msg, file, previewId, index) { |
---|
1642 | var p1 = $.extend(self.getOutData({}, {}, files), {id: previewId, index: index}), |
---|
1643 | p2 = {id: previewId, index: index, file: file, files: files}; |
---|
1644 | self.previewDefault(file, previewId, true); |
---|
1645 | return self.isUploadable ? self.showUploadError(msg, p1) : self.showError(msg, p2); |
---|
1646 | }; |
---|
1647 | |
---|
1648 | function readFile(i) { |
---|
1649 | if (isEmpty($el.attr('multiple'))) { |
---|
1650 | numFiles = 1; |
---|
1651 | } |
---|
1652 | if (i >= numFiles) { |
---|
1653 | if (self.isUploadable && self.filestack.length > 0) { |
---|
1654 | self.raise('filebatchselected', [self.getFileStack()]); |
---|
1655 | } else { |
---|
1656 | self.raise('filebatchselected', [files]); |
---|
1657 | } |
---|
1658 | $container.removeClass('loading'); |
---|
1659 | $status.html(''); |
---|
1660 | return; |
---|
1661 | } |
---|
1662 | var node = ctr + i, previewId = previewInitId + "-" + node, isText, file = files[i], |
---|
1663 | caption = self.slug(file.name), fileSize = (file.size || 0) / 1000, checkFile, fileExtExpr = '', |
---|
1664 | previewData = objUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk, |
---|
1665 | fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), |
---|
1666 | fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '); |
---|
1667 | if (!isEmpty(fileExt)) { |
---|
1668 | fileExtExpr = new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); |
---|
1669 | } |
---|
1670 | fileSize = fileSize.toFixed(2); |
---|
1671 | if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { |
---|
1672 | msg = self.msgSizeTooLarge.repl('{name}', caption) |
---|
1673 | .repl('{size}', fileSize) |
---|
1674 | .repl('{maxSize}', self.maxFileSize); |
---|
1675 | self.isError = throwError(msg, file, previewId, i); |
---|
1676 | return; |
---|
1677 | } |
---|
1678 | if (!isEmpty(fileTypes) && isArray(fileTypes)) { |
---|
1679 | for (j = 0; j < fileTypes.length; j += 1) { |
---|
1680 | typ = fileTypes[j]; |
---|
1681 | checkFile = settings[typ]; |
---|
1682 | chk = (checkFile !== undefined && checkFile(file.type, caption)); |
---|
1683 | fileCount += isEmpty(chk) ? 0 : chk.length; |
---|
1684 | } |
---|
1685 | if (fileCount === 0) { |
---|
1686 | msg = self.msgInvalidFileType.repl('{name}', caption).repl('{types}', strTypes); |
---|
1687 | self.isError = throwError(msg, file, previewId, i); |
---|
1688 | return; |
---|
1689 | } |
---|
1690 | } |
---|
1691 | if (fileCount === 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) { |
---|
1692 | chk = caption.match(fileExtExpr); |
---|
1693 | fileCount += isEmpty(chk) ? 0 : chk.length; |
---|
1694 | if (fileCount === 0) { |
---|
1695 | msg = self.msgInvalidFileExtension.repl('{name}', caption).repl('{extensions}', |
---|
1696 | strExt); |
---|
1697 | self.isError = throwError(msg, file, previewId, i); |
---|
1698 | return; |
---|
1699 | } |
---|
1700 | } |
---|
1701 | if (!self.showPreview) { |
---|
1702 | self.filestack.push(file); |
---|
1703 | setTimeout(readFile(i + 1), 100); |
---|
1704 | self.raise('fileloaded', [file, previewId, i, reader]); |
---|
1705 | return; |
---|
1706 | } |
---|
1707 | if ($preview.length > 0 && FileReader !== undefined) { |
---|
1708 | $status.html(msgLoading.repl('{index}', i + 1).repl('{files}', numFiles)); |
---|
1709 | $container.addClass('loading'); |
---|
1710 | reader.onerror = function (evt) { |
---|
1711 | self.errorHandler(evt, caption); |
---|
1712 | }; |
---|
1713 | reader.onload = function (theFile) { |
---|
1714 | self.previewFile(file, theFile, previewId, previewData); |
---|
1715 | self.initFileActions(); |
---|
1716 | }; |
---|
1717 | reader.onloadend = function () { |
---|
1718 | msg = msgProgress |
---|
1719 | .repl('{index}', i + 1).repl('{files}', numFiles) |
---|
1720 | .repl('{percent}', 50).repl('{name}', caption); |
---|
1721 | setTimeout(function () { |
---|
1722 | $status.html(msg); |
---|
1723 | objUrl.revokeObjectURL(previewData); |
---|
1724 | }, 100); |
---|
1725 | setTimeout(function () { |
---|
1726 | readFile(i + 1); |
---|
1727 | self.updateFileDetails(numFiles); |
---|
1728 | }, 100); |
---|
1729 | self.raise('fileloaded', [file, previewId, i, reader]); |
---|
1730 | }; |
---|
1731 | reader.onprogress = function (data) { |
---|
1732 | if (data.lengthComputable) { |
---|
1733 | var fact = (data.loaded / data.total) * 100, progress = Math.ceil(fact); |
---|
1734 | msg = msgProgress.repl('{index}', i + 1).repl('{files}', numFiles) |
---|
1735 | .repl('{percent}', progress).repl('{name}', caption); |
---|
1736 | setTimeout(function () { |
---|
1737 | $status.html(msg); |
---|
1738 | }, 100); |
---|
1739 | } |
---|
1740 | }; |
---|
1741 | isText = isSet('text', settings) ? settings.text : defaultFileTypeSettings.text; |
---|
1742 | if (isText(file.type, caption)) { |
---|
1743 | reader.readAsText(file, self.textEncoding); |
---|
1744 | } else { |
---|
1745 | reader.readAsArrayBuffer(file); |
---|
1746 | } |
---|
1747 | } else { |
---|
1748 | self.previewDefault(file, previewId); |
---|
1749 | setTimeout(function () { |
---|
1750 | readFile(i + 1); |
---|
1751 | self.updateFileDetails(numFiles); |
---|
1752 | }, 100); |
---|
1753 | self.raise('fileloaded', [file, previewId, i, reader]); |
---|
1754 | } |
---|
1755 | self.filestack.push(file); |
---|
1756 | } |
---|
1757 | |
---|
1758 | readFile(0); |
---|
1759 | self.updateFileDetails(numFiles, false); |
---|
1760 | }, |
---|
1761 | updateFileDetails: function (numFiles) { |
---|
1762 | var self = this, msgSelected = self.msgSelected, $el = self.$element, fileStack = self.getFileStack(), |
---|
1763 | name = $el.val() || (fileStack.length && fileStack[0].name) || '', label = self.slug(name), |
---|
1764 | n = self.isUploadable ? fileStack.length : numFiles, |
---|
1765 | nFiles = previewCache.count(self.id) + n, |
---|
1766 | log = n > 1 ? msgSelected.repl('{n}', nFiles) : label; |
---|
1767 | if (self.isError) { |
---|
1768 | self.$previewContainer.removeClass('loading'); |
---|
1769 | self.$previewStatus.html(''); |
---|
1770 | self.$captionContainer.find('.kv-caption-icon').hide(); |
---|
1771 | } else { |
---|
1772 | self.showFileIcon(); |
---|
1773 | } |
---|
1774 | self.setCaption(log, self.isError); |
---|
1775 | self.$container.removeClass('file-input-new file-input-ajax-new'); |
---|
1776 | if (arguments.length === 1) { |
---|
1777 | self.raise('fileselect', [numFiles, label]); |
---|
1778 | } |
---|
1779 | if (previewCache.count(self.id)) { |
---|
1780 | self.initPreviewDeletes(); |
---|
1781 | } |
---|
1782 | }, |
---|
1783 | change: function (e) { |
---|
1784 | var self = this, $el = self.$element; |
---|
1785 | if (!self.isUploadable && isEmpty($el.val()) && self.fileInputCleared) { // IE 11 fix |
---|
1786 | self.fileInputCleared = false; |
---|
1787 | return; |
---|
1788 | } |
---|
1789 | self.fileInputCleared = false; |
---|
1790 | var tfiles, msg, total, $preview = self.$preview, isDragDrop = arguments.length > 1, |
---|
1791 | files = isDragDrop ? e.originalEvent.dataTransfer.files : $el.get(0).files, |
---|
1792 | isSingleUpload = isEmpty($el.attr('multiple')), i = 0, f, m, folders = 0, |
---|
1793 | ctr = self.filestack.length, isAjaxUpload = self.isUploadable, |
---|
1794 | throwError = function (mesg, file, previewId, index) { |
---|
1795 | var p1 = $.extend(self.getOutData({}, {}, files), {id: previewId, index: index}), |
---|
1796 | p2 = {id: previewId, index: index, file: file, files: files}; |
---|
1797 | return self.isUploadable ? self.showUploadError(mesg, p1) : self.showError(mesg, p2); |
---|
1798 | }; |
---|
1799 | self.reader = null; |
---|
1800 | self.resetUpload(); |
---|
1801 | self.hideFileIcon(); |
---|
1802 | if (self.isUploadable) { |
---|
1803 | self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); |
---|
1804 | } |
---|
1805 | if (isDragDrop) { |
---|
1806 | tfiles = []; |
---|
1807 | while (files[i]) { |
---|
1808 | f = files[i]; |
---|
1809 | if (!f.type && f.size % 4096 === 0) { |
---|
1810 | folders++; |
---|
1811 | } else { |
---|
1812 | tfiles.push(f); |
---|
1813 | } |
---|
1814 | i++; |
---|
1815 | } |
---|
1816 | } else { |
---|
1817 | if (e.target.files === undefined) { |
---|
1818 | tfiles = e.target && e.target.value ? [ |
---|
1819 | {name: e.target.value.replace(/^.+\\/, '')} |
---|
1820 | ] : []; |
---|
1821 | } else { |
---|
1822 | tfiles = e.target.files; |
---|
1823 | } |
---|
1824 | } |
---|
1825 | if (isEmpty(tfiles) || tfiles.length === 0) { |
---|
1826 | if (!isAjaxUpload) { |
---|
1827 | self.clear(); |
---|
1828 | } |
---|
1829 | self.showFolderError(folders); |
---|
1830 | self.raise('fileselectnone'); |
---|
1831 | return; |
---|
1832 | } |
---|
1833 | self.resetErrors(); |
---|
1834 | if (!isAjaxUpload || (isSingleUpload && ctr > 0)) { |
---|
1835 | if (!self.overwriteInitial && previewCache.count(self.id)) { |
---|
1836 | var out = previewCache.out(self.id); |
---|
1837 | $preview.html(out.content); |
---|
1838 | self.setCaption(out.caption); |
---|
1839 | self.initPreviewDeletes(); |
---|
1840 | } else { |
---|
1841 | $preview.html(''); |
---|
1842 | } |
---|
1843 | |
---|
1844 | if (isSingleUpload && ctr > 0) { |
---|
1845 | self.filestack = []; |
---|
1846 | } |
---|
1847 | } |
---|
1848 | total = self.isUploadable ? self.getFileStack().length + tfiles.length : tfiles.length; |
---|
1849 | if (self.maxFileCount > 0 && total > self.maxFileCount) { |
---|
1850 | msg = self.msgFilesTooMany.repl('{m}', self.maxFileCount).repl('{n}', total); |
---|
1851 | self.isError = throwError(msg, null, null, null); |
---|
1852 | self.$captionContainer.find('.kv-caption-icon').hide(); |
---|
1853 | self.$caption.html(self.msgValidationError); |
---|
1854 | self.setEllipsis(); |
---|
1855 | self.$container.removeClass('file-input-new file-input-ajax-new'); |
---|
1856 | return; |
---|
1857 | } |
---|
1858 | if (!self.isIE9) { |
---|
1859 | self.readFiles(tfiles); |
---|
1860 | } else { |
---|
1861 | self.updateFileDetails(1); |
---|
1862 | } |
---|
1863 | self.showFolderError(folders); |
---|
1864 | }, |
---|
1865 | autoSizeImage: function (previewId) { |
---|
1866 | var self = this, $preview = self.$preview, |
---|
1867 | $thumb = $preview.find("#" + previewId), |
---|
1868 | $img = $thumb.find('img'), w1, w2, $cap; |
---|
1869 | if (!$img.length) { |
---|
1870 | return; |
---|
1871 | } |
---|
1872 | $img.on('load', function () { |
---|
1873 | w1 = $thumb.width(); |
---|
1874 | w2 = $preview.width(); |
---|
1875 | if (w1 > w2) { |
---|
1876 | $img.css('width', '100%'); |
---|
1877 | $thumb.css('width', '97%'); |
---|
1878 | } |
---|
1879 | $cap = $img.closest('.file-preview-frame').find('.file-caption-name'); |
---|
1880 | if ($cap.length) { |
---|
1881 | $cap.width($img.width()); |
---|
1882 | $cap.attr('title', $cap.text()); |
---|
1883 | } |
---|
1884 | self.raise('fileimageloaded', previewId); |
---|
1885 | }); |
---|
1886 | }, |
---|
1887 | setCaption: function (content, isError) { |
---|
1888 | var self = this, err = isError || false, title, out; |
---|
1889 | if (isEmpty(content) || self.$caption.length === 0) { |
---|
1890 | return; |
---|
1891 | } |
---|
1892 | if (err) { |
---|
1893 | title = $('<div>' + self.msgValidationError + '</div>').text(); |
---|
1894 | out = '<span class="' + self.msgValidationErrorClass + '">' + |
---|
1895 | self.msgValidationErrorIcon + title + '</span>'; |
---|
1896 | } else { |
---|
1897 | title = $('<div>' + content + '</div>').text(); |
---|
1898 | out = title + self.getLayoutTemplate('icon'); |
---|
1899 | } |
---|
1900 | self.$caption.html(out); |
---|
1901 | self.$caption.attr('title', title); |
---|
1902 | self.$captionContainer.find('.file-caption-ellipsis').attr('title', title); |
---|
1903 | self.setEllipsis(); |
---|
1904 | }, |
---|
1905 | initBrowse: function ($container) { |
---|
1906 | var self = this; |
---|
1907 | self.$btnFile = $container.find('.btn-file'); |
---|
1908 | self.$btnFile.append(self.$element); |
---|
1909 | }, |
---|
1910 | createContainer: function () { |
---|
1911 | var self = this, |
---|
1912 | $container = $(document.createElement("span")) |
---|
1913 | .attr({"class": 'file-input file-input-new'}) |
---|
1914 | .html(self.renderMain()); |
---|
1915 | self.$element.before($container); |
---|
1916 | self.initBrowse($container); |
---|
1917 | return $container; |
---|
1918 | }, |
---|
1919 | refreshContainer: function () { |
---|
1920 | var self = this, $container = self.$container; |
---|
1921 | $container.before(self.$element); |
---|
1922 | $container.html(self.renderMain()); |
---|
1923 | self.initBrowse($container); |
---|
1924 | }, |
---|
1925 | renderMain: function () { |
---|
1926 | var self = this, dropCss = (self.isUploadable && self.dropZoneEnabled) ? ' file-drop-zone' : '', |
---|
1927 | preview = self.showPreview ? self.getLayoutTemplate('preview').repl('{class}', self.previewClass) |
---|
1928 | .repl('{dropClass}', dropCss) : '', |
---|
1929 | css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass, |
---|
1930 | caption = self.captionTemplate.repl('{class}', css + ' kv-fileinput-caption'); |
---|
1931 | return self.mainTemplate.repl('{class}', self.mainClass) |
---|
1932 | .repl('{preview}', preview) |
---|
1933 | .repl('{caption}', caption) |
---|
1934 | .repl('{upload}', self.renderUpload()) |
---|
1935 | .repl('{remove}', self.renderRemove()) |
---|
1936 | .repl('{cancel}', self.renderCancel()) |
---|
1937 | .repl('{browse}', self.renderBrowse()); |
---|
1938 | }, |
---|
1939 | renderBrowse: function () { |
---|
1940 | var self = this, css = self.browseClass + ' btn-file', status = ''; |
---|
1941 | if (self.isDisabled) { |
---|
1942 | status = ' disabled '; |
---|
1943 | } |
---|
1944 | return '<div class="' + css + '"' + status + '> ' + self.browseIcon + self.browseLabel + ' </div>'; |
---|
1945 | }, |
---|
1946 | renderRemove: function () { |
---|
1947 | var self = this, css = self.removeClass + ' fileinput-remove fileinput-remove-button', status = ''; |
---|
1948 | if (!self.showRemove) { |
---|
1949 | return ''; |
---|
1950 | } |
---|
1951 | if (self.isDisabled) { |
---|
1952 | status = ' disabled '; |
---|
1953 | } |
---|
1954 | return '<button type="button" title="' + self.removeTitle + '" class="' + css + '"' + status + '>' + self.removeIcon + self.removeLabel + '</button>'; |
---|
1955 | }, |
---|
1956 | renderCancel: function () { |
---|
1957 | var self = this, css = self.cancelClass + ' fileinput-cancel fileinput-cancel-button'; |
---|
1958 | if (!self.showCancel) { |
---|
1959 | return ''; |
---|
1960 | } |
---|
1961 | return '<button type="button" title="' + self.cancelTitle + '" class="hide ' + css + '">' + self.cancelIcon + self.cancelLabel + '</button>'; |
---|
1962 | }, |
---|
1963 | renderUpload: function () { |
---|
1964 | var self = this, css = self.uploadClass + ' kv-fileinput-upload fileinput-upload-button', content = '', status = ''; |
---|
1965 | if (!self.showUpload) { |
---|
1966 | return ''; |
---|
1967 | } |
---|
1968 | if (self.isDisabled) { |
---|
1969 | status = ' disabled '; |
---|
1970 | } |
---|
1971 | if (!self.isUploadable || self.isDisabled) { |
---|
1972 | content = '<button type="submit" title="' + self.uploadTitle + '"class="' + css + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</button>'; |
---|
1973 | } else { |
---|
1974 | content = '<a href="' + self.uploadUrl + '" title="' + self.uploadTitle + '" class="' + css + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</a>'; |
---|
1975 | } |
---|
1976 | return content; |
---|
1977 | } |
---|
1978 | }; |
---|
1979 | |
---|
1980 | //FileInput plugin definition |
---|
1981 | $.fn.fileinput = function (option) { |
---|
1982 | if (!hasFileAPISupport() && !isIE(9)) { |
---|
1983 | return; |
---|
1984 | } |
---|
1985 | |
---|
1986 | var args = Array.apply(null, arguments); |
---|
1987 | args.shift(); |
---|
1988 | return this.each(function () { |
---|
1989 | var $this = $(this), |
---|
1990 | data = $this.data('fileinput'), |
---|
1991 | options = typeof option === 'object' && option; |
---|
1992 | |
---|
1993 | if (!data) { |
---|
1994 | data = new FileInput(this, $.extend({}, $.fn.fileinput.defaults, options, $(this).data())); |
---|
1995 | $this.data('fileinput', data); |
---|
1996 | } |
---|
1997 | |
---|
1998 | if (typeof option === 'string') { |
---|
1999 | data[option].apply(data, args); |
---|
2000 | } |
---|
2001 | }); |
---|
2002 | }; |
---|
2003 | |
---|
2004 | $.fn.fileinput.defaults = { |
---|
2005 | showCaption: true, |
---|
2006 | showPreview: true, |
---|
2007 | showRemove: true, |
---|
2008 | showUpload: true, |
---|
2009 | showCancel: true, |
---|
2010 | mainClass: '', |
---|
2011 | previewClass: '', |
---|
2012 | captionClass: '', |
---|
2013 | mainTemplate: null, |
---|
2014 | initialCaption: '', |
---|
2015 | initialPreview: [], |
---|
2016 | initialPreviewDelimiter: '*$$*', |
---|
2017 | initialPreviewConfig: [], |
---|
2018 | initialPreviewThumbTags: [], |
---|
2019 | previewThumbTags: {}, |
---|
2020 | initialPreviewShowDelete: true, |
---|
2021 | deleteUrl: '', |
---|
2022 | deleteExtraData: {}, |
---|
2023 | overwriteInitial: true, |
---|
2024 | layoutTemplates: defaultLayoutTemplates, |
---|
2025 | previewTemplates: defaultPreviewTemplates, |
---|
2026 | allowedPreviewTypes: defaultPreviewTypes, |
---|
2027 | allowedPreviewMimeTypes: null, |
---|
2028 | allowedFileTypes: null, |
---|
2029 | allowedFileExtensions: null, |
---|
2030 | customLayoutTags: {}, |
---|
2031 | customPreviewTags: {}, |
---|
2032 | previewSettings: defaultPreviewSettings, |
---|
2033 | fileTypeSettings: defaultFileTypeSettings, |
---|
2034 | previewFileIcon: '<i class="fa fa-file"></i>', |
---|
2035 | browseIcon: '<i class="fa fa-folder-open"></i> ', |
---|
2036 | browseClass: 'btn btn-primary rounded-0', |
---|
2037 | removeIcon: '<i class="fa fa-trash"></i> ', |
---|
2038 | removeClass: 'btn btn-light rounded-0', |
---|
2039 | cancelIcon: '<i class="fa fa-ban"></i> ', |
---|
2040 | cancelClass: 'btn btn-light rounded-0', |
---|
2041 | uploadIcon: '<i class="fa fa-upload"></i> ', |
---|
2042 | uploadClass: 'btn btn-light rounded-0', |
---|
2043 | uploadUrl: null, |
---|
2044 | uploadAsync: true, |
---|
2045 | uploadExtraData: {}, |
---|
2046 | maxFileSize: 0, |
---|
2047 | minFileCount: 0, |
---|
2048 | maxFileCount: 0, |
---|
2049 | msgValidationErrorClass: 'text-danger', |
---|
2050 | msgValidationErrorIcon: '<i class="fa fa-exclamation"></i> ', |
---|
2051 | msgErrorClass: 'file-error-message', |
---|
2052 | progressClass: "progress-bar progress-bar-success progress-bar-striped active", |
---|
2053 | progressCompleteClass: "progress-bar progress-bar-success", |
---|
2054 | previewFileType: 'image', |
---|
2055 | wrapTextLength: 250, |
---|
2056 | wrapIndicator: ' <span class="wrap-indicator" title="{title}" onclick="{dialog}">[…]</span>', |
---|
2057 | elCaptionContainer: null, |
---|
2058 | elCaptionText: null, |
---|
2059 | elPreviewContainer: null, |
---|
2060 | elPreviewImage: null, |
---|
2061 | elPreviewStatus: null, |
---|
2062 | elErrorContainer: null, |
---|
2063 | slugCallback: null, |
---|
2064 | dropZoneEnabled: true, |
---|
2065 | dropZoneTitleClass: 'file-drop-zone-title', |
---|
2066 | fileActionSettings: {}, |
---|
2067 | otherActionButtons: '', |
---|
2068 | textEncoding: 'UTF-8', |
---|
2069 | ajaxSettings: {}, |
---|
2070 | ajaxDeleteSettings: {}, |
---|
2071 | showAjaxErrorDetails: true |
---|
2072 | }; |
---|
2073 | |
---|
2074 | $.fn.fileinput.locales = {}; |
---|
2075 | |
---|
2076 | $.fn.fileinput.locales.en = { |
---|
2077 | fileSingle: 'file', |
---|
2078 | filePlural: 'files', |
---|
2079 | browseLabel: 'Browse …', |
---|
2080 | removeLabel: 'Remove', |
---|
2081 | removeTitle: 'Clear selected files', |
---|
2082 | cancelLabel: 'Cancel', |
---|
2083 | cancelTitle: 'Abort ongoing upload', |
---|
2084 | uploadLabel: 'Upload', |
---|
2085 | uploadTitle: 'Upload selected files', |
---|
2086 | msgSizeTooLarge: 'File "{name}" (<b>{size} KB</b>) exceeds maximum allowed upload size of <b>{maxSize} KB</b>. Please retry your upload!', |
---|
2087 | msgFilesTooLess: 'You must select at least <b>{n}</b> {files} to upload. Please retry your upload!', |
---|
2088 | msgFilesTooMany: 'Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>. Please retry your upload!', |
---|
2089 | msgFileNotFound: 'File "{name}" not found!', |
---|
2090 | msgFileSecured: 'Security restrictions prevent reading the file "{name}".', |
---|
2091 | msgFileNotReadable: 'File "{name}" is not readable.', |
---|
2092 | msgFilePreviewAborted: 'File preview aborted for "{name}".', |
---|
2093 | msgFilePreviewError: 'An error occurred while reading the file "{name}".', |
---|
2094 | msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', |
---|
2095 | msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', |
---|
2096 | msgValidationError: 'File Upload Error', |
---|
2097 | msgLoading: 'Loading file {index} of {files} …', |
---|
2098 | msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', |
---|
2099 | msgSelected: '{n} files selected', |
---|
2100 | msgFoldersNotAllowed: 'Drag & drop files only! {n} folder(s) dropped were skipped.', |
---|
2101 | dropZoneTitle: 'Drag & drop files here …' |
---|
2102 | }; |
---|
2103 | |
---|
2104 | $.extend($.fn.fileinput.defaults, $.fn.fileinput.locales.en); |
---|
2105 | |
---|
2106 | $.fn.fileinput.Constructor = FileInput; |
---|
2107 | |
---|
2108 | /** |
---|
2109 | * Convert automatically file inputs with class 'file' |
---|
2110 | * into a bootstrap fileinput control. |
---|
2111 | */ |
---|
2112 | $(document).ready(function () { |
---|
2113 | var $input = $('input.file[type=file]'), count = $input.attr('type') ? $input.length : 0; |
---|
2114 | if (count > 0) { |
---|
2115 | $input.fileinput(); |
---|
2116 | } |
---|
2117 | }); |
---|
2118 | })(window.jQuery); |
---|