diff --git a/Bootstrap.Admin/Controllers/Api/ProfilesController.cs b/Bootstrap.Admin/Controllers/Api/ProfilesController.cs index 7f068075..de8cb427 100644 --- a/Bootstrap.Admin/Controllers/Api/ProfilesController.cs +++ b/Bootstrap.Admin/Controllers/Api/ProfilesController.cs @@ -1,9 +1,12 @@ using Bootstrap.DataAccess; using Longbow.Logging; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Specialized; using System.IO; +using System.Threading.Tasks; namespace Bootstrap.Admin.Controllers.Api { @@ -14,34 +17,51 @@ namespace Bootstrap.Admin.Controllers.Api public class ProfilesController : Controller { [HttpPost] - public string Post() + public async Task Post([FromServices]IHostingEnvironment env, IFormCollection files) { - var ret = string.Empty; + var previewUrl = string.Empty; + long fileSize = 0; var userName = User.Identity.Name; + var error = string.Empty; if (User.IsInRole("Administrators")) userName = "default"; - //var files = HttpContext.Request.Files; - //if (files.Count > 0) - //{ - // var webSiteUrl = DictHelper.RetrieveIconFolderPath().Code; - // var fileName = string.Format("{0}{1}", userName, Path.GetExtension(files[0].FileName)); - // var fileUrl = string.Format("{0}{1}", webSiteUrl, fileName); - // var filePath = HttpContext.Current.Server.MapPath(fileUrl); - // var fileFolder = Path.GetDirectoryName(filePath); - // try - // { - // if (!Directory.Exists(fileFolder)) Directory.CreateDirectory(fileFolder); - // files[0].SaveAs(filePath); - // ret = string.Format("{0}?q={1}", Url.Content(fileUrl), DateTime.Now.Ticks); - // UserHelper.SaveUserIconByName(userName, fileName); - // } - // catch (Exception ex) - // { - // var nv = new NameValueCollection(); - // nv.Add("UpLoadFileName", filePath); - // ExceptionManager.Publish(ex, nv); - // } - //} - return ret; + if (files.Count > 0) + { + var uploadFile = files.Files[0]; + var webSiteUrl = DictHelper.RetrieveIconFolderPath().Code; + var fileName = string.Format("{0}{1}", userName, Path.GetExtension(uploadFile.FileName)); + var fileUrl = string.Format("{0}{1}", webSiteUrl, fileName); + var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace("/", "\\").TrimStart('\\') + fileName); + var fileFolder = Path.GetDirectoryName(filePath); + fileSize = uploadFile.Length; + try + { + if (!Directory.Exists(fileFolder)) Directory.CreateDirectory(fileFolder); + using (var fs = new FileStream(filePath, FileMode.Create)) + { + await uploadFile.CopyToAsync(fs); + } + previewUrl = string.Format("{0}?q={1}", Url.Content(fileUrl), DateTime.Now.Ticks); + UserHelper.SaveUserIconByName(userName, fileName); + } + catch (Exception ex) + { + var nv = new NameValueCollection + { + { "UpLoadFileName", filePath } + }; + error = ex.Message; + ExceptionManager.Publish(ex, nv); + } + } + return new JsonResult(new + { + error = string.IsNullOrEmpty(error) ? error : $"服务器端错误-{error}", + initialPreview = new string[] { previewUrl }, + initialPreviewConfig = new object[] { + new { caption= "新头像", size= fileSize, showZoom= true } + }, + append = false + }); } } } \ No newline at end of file diff --git a/Bootstrap.Admin/wwwroot/css/fileinput.css b/Bootstrap.Admin/wwwroot/css/fileinput.css index 82ffee4a..f249a2cf 100644 --- a/Bootstrap.Admin/wwwroot/css/fileinput.css +++ b/Bootstrap.Admin/wwwroot/css/fileinput.css @@ -1,23 +1,128 @@ -/*! - * bootstrap-fileinput v4.3.6 +/*! + * bootstrap-fileinput v4.4.9 * http://plugins.krajee.com/file-input * + * Krajee default styling for bootstrap-fileinput. + * * Author: Kartik Visweswaran - * Copyright: 2014 - 2016, Kartik Visweswaran, Krajee.com + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com * * Licensed under the BSD 3-Clause * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md */ -.file-loading { - top: 0; - right: 0; - width: 25px; - height: 25px; - font-size: 999px; - text-align: right; - color: #fff; - background: transparent url('../images/loading.gif') top left no-repeat; +.file-loading input[type=file], input[type=file].file-loading { + width: 0; + height: 0; +} + +.file-no-browse { + position: absolute; + left: 50%; + bottom: 20%; + width: 1px; + height: 1px; + font-size: 0; + opacity: 0; border: none; + background: none; + outline: none; + box-shadow: none; +} + +.kv-hidden, .file-caption-icon, .file-zoom-dialog .modal-header:before, .file-zoom-dialog .modal-header:after, .file-input-new .file-preview, .file-input-new .close, .file-input-new .glyphicon-file, .file-input-new .fileinput-remove-button, .file-input-new .fileinput-upload-button, .file-input-new .no-browse .input-group-btn, .file-input-ajax-new .fileinput-remove-button, .file-input-ajax-new .fileinput-upload-button, .file-input-ajax-new .no-browse .input-group-btn, .hide-content .kv-file-content { + display: none; +} + +.btn-file input[type=file], .file-caption-icon, .file-preview .fileinput-remove, .krajee-default .file-thumb-progress, .file-zoom-dialog .btn-navigate, .file-zoom-dialog .floating-buttons { + position: absolute; +} + +.file-input, .file-loading:before, .btn-file, .file-caption, .file-preview, .krajee-default.file-preview-frame, .krajee-default .file-thumbnail-footer, .file-zoom-dialog .modal-dialog { + position: relative; +} + +.file-error-message pre, .file-error-message ul, .krajee-default .file-actions, .krajee-default .file-other-error { + text-align: left; +} + +.file-error-message pre, .file-error-message ul { + margin: 0; +} + +.krajee-default .file-drag-handle, .krajee-default .file-upload-indicator { + float: left; + margin: 5px 0 -5px; + width: 16px; + height: 16px; +} + +.krajee-default .file-thumb-progress .progress, .krajee-default .file-thumb-progress .progress-bar { + height: 11px; + font-size: 9px; + line-height: 10px; +} + +.krajee-default .file-caption-info, .krajee-default .file-size-info { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 160px; + height: 15px; + margin: auto; +} + +.file-zoom-content > .file-object.type-video, .file-zoom-content > .file-object.type-flash, .file-zoom-content > .file-object.type-image { + max-width: 100%; + max-height: 100%; + width: auto; +} + +.file-zoom-content > .file-object.type-video, .file-zoom-content > .file-object.type-flash { + height: 100%; +} + +.file-zoom-content > .file-object.type-pdf, .file-zoom-content > .file-object.type-html, .file-zoom-content > .file-object.type-text, .file-zoom-content > .file-object.type-default { + width: 100%; +} + +.rotate-2 { + transform: rotateY(180deg); +} + +.rotate-3 { + transform: rotate(180deg); +} + +.rotate-4 { + transform: rotate(180deg) rotateY(180deg); +} + +.rotate-5 { + transform: rotate(270deg) rotateY(180deg); +} + +.rotate-6 { + transform: rotate(90deg); +} + +.rotate-7 { + transform: rotate(90deg) rotateY(180deg); +} + +.rotate-8 { + transform: rotate(270deg); +} + +.file-loading:before { + content: " Loading..."; + display: inline-block; + padding-left: 20px; + line-height: 16px; + font-size: 13px; + font-variant: small-caps; + color: #999; + background: transparent url(../img/loading.gif) top left no-repeat; } .file-object { @@ -26,14 +131,12 @@ } .btn-file { - position: relative; overflow: hidden; } .btn-file input[type=file] { - position: absolute; top: 0; - right: 0; + left: 0; min-width: 100%; min-height: 100%; text-align: right; @@ -43,19 +146,32 @@ display: block; } -.file-caption-name { +.btn-file ::-ms-browse { + font-size: 10000px; + width: 100%; + height: 100%; +} + +.file-caption .file-caption-name { + width: 100%; + margin: 0; + padding: 0; + box-shadow: none; + border: none; + background: none; + outline: none; +} + +.file-caption.icon-visible .file-caption-icon { display: inline-block; - overflow: hidden; - height: 20px; - word-break: break-all; } -.input-group-lg .file-caption-name { - height: 25px; +.file-caption.icon-visible .file-caption-name { + padding-left: 15px; } -.file-zoom-dialog { - text-align: left; +.file-caption-icon { + left: 8px; } .file-error-message { @@ -67,17 +183,12 @@ padding: 15px; } -.file-error-message pre, .file-error-message ul { - margin: 0; - text-align: left; -} - .file-error-message pre { margin: 5px 0; } .file-caption-disabled { - background-color: #EEEEEE; + background-color: #eee; cursor: not-allowed; opacity: 1; } @@ -85,34 +196,56 @@ .file-preview { border-radius: 5px; border: 1px solid #ddd; - padding: 5px; + padding: 8px; width: 100%; margin-bottom: 5px; } -.file-preview-frame { - position: relative; - display: table; +.file-preview .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.file-preview .fileinput-remove { + top: 1px; + right: 1px; + line-height: 10px; +} + +.file-preview .clickable { + cursor: pointer; +} + +.file-preview-image { + font: 40px Impact, Charcoal, sans-serif; + color: #008000; +} + +.krajee-default.file-preview-frame { margin: 8px; - height: 160px; border: 1px solid #ddd; box-shadow: 1px 1px 5px 0 #a2958a; padding: 6px; float: left; text-align: center; - vertical-align: middle; } -.file-preview-frame:not(.file-preview-error):hover { +.krajee-default.file-preview-frame .kv-file-content { + width: 213px; + height: 160px; +} + +.krajee-default.file-preview-frame .file-thumbnail-footer { + height: 70px; +} + +.krajee-default.file-preview-frame:not(.file-preview-error):hover { box-shadow: 3px 3px 5px 0 #333; } -.file-preview-image { - vertical-align: middle; - image-orientation: from-image; -} - -.file-preview-text { +.krajee-default .file-preview-text { display: block; color: #428bca; border: 1px solid #ddd; @@ -122,208 +255,83 @@ resize: none; } -.file-preview-html { +.krajee-default .file-preview-html { border: 1px solid #ddd; padding: 8px; overflow: auto; } -.file-zoom-dialog .file-preview-text { - font-size: 1.2em; +.krajee-default .file-other-icon { + font-size: 6em; } -.file-preview-other { - left: 0; - top: 0; - right: 0; - bottom: 0; - margin: auto; - text-align: center; - vertical-align: middle; - padding: 10px; -} - -.file-preview-other:hover { - opacity: 0.8; -} - -.file-actions, .file-other-error { - text-align: left; -} - -.file-other-icon { - font-size: 4.8em; -} - -/* noinspection CssOverwrittenProperties */ -.file-zoom-dialog .file-other-icon { - font-size: 8em; - font-size: 55vmin; -} - -.file-input-new .file-preview, .file-input-new .close, .file-input-new .glyphicon-file, -.file-input-new .fileinput-remove-button, .file-input-new .fileinput-upload-button, -.file-input-ajax-new .fileinput-remove-button, .file-input-ajax-new .fileinput-upload-button { - display: none; -} - -.file-caption-main { - width: 100%; -} - -.file-input-ajax-new .no-browse .input-group-btn, -.file-input-new .no-browse .input-group-btn { - display: none; -} - -.file-input-ajax-new .no-browse .form-control, -.file-input-new .no-browse .form-control { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -.file-thumb-loading { - background: transparent url('../images/loading.gif') no-repeat scroll center center content-box !important; -} - -.file-actions { - margin-top: 15px; -} - -.file-footer-buttons { +.krajee-default .file-footer-buttons { float: right; } -.file-upload-indicator { - display: inline; - cursor: default; - opacity: 0.8; - width: 60%; -} - -.file-upload-indicator:hover { - font-weight: bold; - opacity: 1; -} - -.file-footer-caption { +.krajee-default .file-footer-caption { display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 160px; text-align: center; padding-top: 4px; font-size: 11px; color: #777; - margin: 5px auto; + margin-bottom: 15px; } -.file-preview-error { +.krajee-default .file-preview-error { opacity: 0.65; box-shadow: none; } -.file-preview-frame:not(.file-preview-error) .file-footer-caption:hover { +.krajee-default .file-thumb-progress { + height: 11px; + top: 37px; + left: 0; + right: 0; +} + +.krajee-default.kvsortable-ghost { + background: #e1edf7; + border: 2px solid #a1abff; +} + +.krajee-default .file-preview-other:hover { + opacity: 0.8; +} + +.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover { color: #000; } -.file-drop-zone { - border: 1px dashed #aaa; - border-radius: 4px; - height: 100%; - text-align: center; - vertical-align: middle; - margin: 12px 15px 12px 12px; - padding: 5px; +.kv-upload-progress .progress { + height: 20px; + line-height: 20px; + margin: 10px 0; + overflow: hidden; } -.file-drop-zone-title { - color: #aaa; - font-size: 1.6em; - padding: 85px 10px; - cursor: default; +.kv-upload-progress .progress-bar { + height: 20px; + line-height: 20px; } -.file-preview .clickable, -.clickable .file-drop-zone-title { - cursor: pointer; +/*noinspection CssOverwrittenProperties*/ +.file-zoom-dialog .file-other-icon { + font-size: 22em; + font-size: 50vmin; } -.file-drop-zone.clickable:hover { - border: 2px dashed #999; +.file-zoom-dialog .modal-dialog { + width: auto; } -.file-drop-zone.clickable:focus { - border: 2px solid #5acde2; -} - -.file-drop-zone .file-preview-thumbnails { - cursor: default; -} - -.file-highlighted { - border: 2px dashed #999 !important; - background-color: #f0f0f0; -} - -.file-uploading { - background: url('../images/loading-sm.gif') no-repeat center bottom 10px; - opacity: 0.65; -} - -.file-thumb-progress { - height: 10px; -} - -.file-thumb-progress .progress, .file-thumb-progress .progress-bar { - height: 10px; - font-size: 9px; - line-height: 10px; -} - -.file-thumbnail-footer { - position: relative; -} - -.file-thumb-progress { - position: absolute; - top: 35px; - left: 0; - right: 0; -} - -.file-zoom-fullscreen.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; -} - -.file-zoom-fullscreen .modal-dialog { - position: fixed; - margin: 0; - width: 100%; - height: 100%; - padding: 0; -} - -.file-zoom-fullscreen .modal-content { - border-radius: 0; - box-shadow: none; -} - -.file-zoom-fullscreen .modal-body { - overflow-y: auto; -} - -.file-zoom-dialog .modal-body { - position: relative !important; +.file-zoom-dialog .modal-header { + display: flex; + align-items: center; + justify-content: space-between; } .file-zoom-dialog .btn-navigate { - position: absolute; padding: 0; margin: 0; background: transparent; @@ -335,26 +343,15 @@ color: #1c94c4; } -.file-zoom-dialog .floating-buttons { - position: absolute; - top: 5px; - right: 10px; -} - -.floating-buttons, .floating-buttons .btn { - z-index: 3000; -} - -.file-zoom-dialog .kv-zoom-actions .btn, -.floating-buttons .btn { - margin-left: 3px; -} - -.file-zoom-dialog .btn-navigate:not([disabled]):hover, -.file-zoom-dialog .btn-navigate:not([disabled]):focus { +.file-zoom-dialog .btn-navigate:not([disabled]):hover { outline: none; box-shadow: none; - opacity: 0.5; + opacity: 0.6; +} + +.file-zoom-dialog .floating-buttons { + top: 5px; + right: 10px; } .file-zoom-dialog .btn-navigate[disabled] { @@ -369,16 +366,118 @@ right: 1px; } -.file-drag-handle { - display: inline; - margin-right: 2px; - font-size: 16px; - cursor: move; - cursor: -webkit-grabbing; +.file-zoom-dialog .kv-zoom-title { + font-weight: 300; + color: #999; + max-width: 50%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } -.file-drag-handle:hover { - opacity: 0.7; +.file-input-new .no-browse .form-control { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.file-input-ajax-new .no-browse .form-control { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.file-caption-main { + width: 100%; +} + +.file-thumb-loading { + background: transparent url(../img/loading.gif) no-repeat scroll center center content-box !important; +} + +.file-drop-zone { + border: 1px dashed #aaa; + border-radius: 4px; + height: 100%; + text-align: center; + vertical-align: middle; + margin: 12px 15px 12px 12px; + padding: 5px; +} + +.file-drop-zone.clickable:hover { + border: 2px dashed #999; +} + +.file-drop-zone.clickable:focus { + border: 2px solid #5acde2; +} + +.file-drop-zone .file-preview-thumbnails { + cursor: default; +} + +.file-drop-zone-title { + color: #aaa; + font-size: 1.6em; + padding: 85px 10px; + cursor: default; +} + +.file-highlighted { + border: 2px dashed #999 !important; + background-color: #eee; +} + +.file-uploading { + background: url(../img/loading-sm.gif) no-repeat center bottom 10px; + opacity: 0.65; +} + +@media (min-width: 576px) { + .file-zoom-dialog .modal-dialog { + max-width: 500px; + } +} + +@media (min-width: 992px) { + .file-zoom-dialog .modal-lg { + max-width: 800px; + } +} + +.file-zoom-fullscreen.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.file-zoom-fullscreen .modal-dialog { + position: fixed; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; +} + +.file-zoom-fullscreen .modal-content { + border-radius: 0; + box-shadow: none; +} + +.file-zoom-fullscreen .modal-body { + overflow-y: auto; +} + +.floating-buttons { + z-index: 3000; +} + +.floating-buttons .btn-kv { + margin-left: 3px; + z-index: 3000; } .file-zoom-content { @@ -386,16 +485,70 @@ text-align: center; } +.file-zoom-content .file-preview-image { + max-height: 100%; +} + +.file-zoom-content .file-preview-video { + max-height: 100%; +} + +.file-zoom-content .is-portrait-gt4 { + margin-top: 60px; +} + +.file-zoom-content > .file-object.type-image { + height: auto; + min-height: inherit; +} + +.file-zoom-content > .file-object.type-audio { + width: auto; + height: 30px; +} + +@media screen and (max-width: 767px) { + .file-preview-thumbnails { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } + + .file-zoom-dialog .modal-header { + flex-direction: column; + } +} + +@media screen and (max-width: 350px) { + .krajee-default.file-preview-frame .kv-file-content { + width: 160px; + } +} + +.file-loading[dir=rtl]:before { + background: transparent url(../img/loading.gif) top right no-repeat; + padding-left: 0; + padding-right: 20px; +} + +.file-sortable .file-drag-handle { + cursor: move; + opacity: 1; +} + +.file-sortable .file-drag-handle:hover { + opacity: 0.7; +} + +.clickable .file-drop-zone-title { + cursor: pointer; +} + +.kv-zoom-actions .btn-kv { + margin-left: 3px; +} + .file-preview-initial.sortable-chosen { background-color: #d9edf7; -} - -.file-preview-frame.sortable-ghost { - background-color: #eee; -} - -/* IE 10 fix */ -.btn-file ::-ms-browse { - width: 100%; - height: 100%; } \ No newline at end of file diff --git a/Bootstrap.Admin/wwwroot/css/fileinput.min.css b/Bootstrap.Admin/wwwroot/css/fileinput.min.css index 7a2e9402..2ed85981 100644 --- a/Bootstrap.Admin/wwwroot/css/fileinput.min.css +++ b/Bootstrap.Admin/wwwroot/css/fileinput.min.css @@ -1,10 +1,12 @@ /*! - * bootstrap-fileinput v4.3.6 + * bootstrap-fileinput v4.4.9 * http://plugins.krajee.com/file-input * + * Krajee default styling for bootstrap-fileinput. + * * Author: Kartik Visweswaran - * Copyright: 2014 - 2016, Kartik Visweswaran, Krajee.com + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com * * Licensed under the BSD 3-Clause * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md - */.file-loading{top:0;right:0;width:25px;height:25px;font-size:999px;text-align:right;color:#fff;background:transparent url('../img/loading.gif') top left no-repeat;border:0}.file-object{margin:0 0 -5px 0;padding:0}.btn-file{position:relative;overflow:hidden}.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;text-align:right;opacity:0;background:none repeat scroll 0 0 transparent;cursor:inherit;display:block}.file-caption-name{display:inline-block;overflow:hidden;height:20px;word-break:break-all}.input-group-lg .file-caption-name{height:25px}.file-zoom-dialog{text-align:left}.file-error-message{color:#a94442;background-color:#f2dede;margin:5px;border:1px solid #ebccd1;border-radius:4px;padding:15px}.file-error-message pre,.file-error-message ul{margin:0;text-align:left}.file-error-message pre{margin:5px 0}.file-caption-disabled{background-color:#eee;cursor:not-allowed;opacity:1}.file-preview{border-radius:5px;border:1px solid #ddd;padding:5px;width:100%;margin-bottom:5px}.file-preview-frame{position:relative;display:table;margin:8px;height:160px;border:1px solid #ddd;box-shadow:1px 1px 5px 0 #a2958a;padding:6px;float:left;text-align:center;vertical-align:middle}.file-preview-frame:not(.file-preview-error):hover{box-shadow:3px 3px 5px 0 #333}.file-preview-image{vertical-align:middle;image-orientation:from-image}.file-preview-text{display:block;color:#428bca;border:1px solid #ddd;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;outline:0;padding:8px;resize:none}.file-preview-html{border:1px solid #ddd;padding:8px;overflow:auto}.file-zoom-dialog .file-preview-text{font-size:1.2em}.file-preview-other{left:0;top:0;right:0;bottom:0;margin:auto;text-align:center;vertical-align:middle;padding:10px}.file-preview-other:hover{opacity:.8}.file-actions,.file-other-error{text-align:left}.file-other-icon{font-size:4.8em}.file-zoom-dialog .file-other-icon{font-size:8em;font-size:55vmin}.file-input-new .file-preview,.file-input-new .close,.file-input-new .glyphicon-file,.file-input-new .fileinput-remove-button,.file-input-new .fileinput-upload-button,.file-input-ajax-new .fileinput-remove-button,.file-input-ajax-new .fileinput-upload-button{display:none}.file-caption-main{width:100%}.file-input-ajax-new .no-browse .input-group-btn,.file-input-new .no-browse .input-group-btn{display:none}.file-input-ajax-new .no-browse .form-control,.file-input-new .no-browse .form-control{border-top-right-radius:4px;border-bottom-right-radius:4px}.file-thumb-loading{background:transparent url('../img/loading.gif') no-repeat scroll center center content-box!important}.file-actions{margin-top:15px}.file-footer-buttons{float:right}.file-upload-indicator{display:inline;cursor:default;opacity:.8;width:60%}.file-upload-indicator:hover{font-weight:bold;opacity:1}.file-footer-caption{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:160px;text-align:center;padding-top:4px;font-size:11px;color:#777;margin:5px auto}.file-preview-error{opacity:.65;box-shadow:none}.file-preview-frame:not(.file-preview-error) .file-footer-caption:hover{color:#000}.file-drop-zone{border:1px dashed #aaa;border-radius:4px;height:100%;text-align:center;vertical-align:middle;margin:12px 15px 12px 12px;padding:5px}.file-drop-zone-title{color:#aaa;font-size:1.6em;padding:85px 10px;cursor:default}.file-preview .clickable,.clickable .file-drop-zone-title{cursor:pointer}.file-drop-zone.clickable:hover{border:2px dashed #999}.file-drop-zone.clickable:focus{border:2px solid #5acde2}.file-drop-zone .file-preview-thumbnails{cursor:default}.file-highlighted{border:2px dashed #999!important;background-color:#f0f0f0}.file-uploading{background:url('../img/loading-sm.gif') no-repeat center bottom 10px;opacity:.65}.file-thumb-progress{height:10px}.file-thumb-progress .progress,.file-thumb-progress .progress-bar{height:10px;font-size:9px;line-height:10px}.file-thumbnail-footer{position:relative}.file-thumb-progress{position:absolute;top:35px;left:0;right:0}.file-zoom-fullscreen.modal{position:fixed;top:0;right:0;bottom:0;left:0}.file-zoom-fullscreen .modal-dialog{position:fixed;margin:0;width:100%;height:100%;padding:0}.file-zoom-fullscreen .modal-content{border-radius:0;box-shadow:none}.file-zoom-fullscreen .modal-body{overflow-y:auto}.file-zoom-dialog .modal-body{position:relative!important}.file-zoom-dialog .btn-navigate{position:absolute;padding:0;margin:0;background:transparent;text-decoration:none;outline:0;opacity:.7;top:45%;font-size:4em;color:#1c94c4}.file-zoom-dialog .floating-buttons{position:absolute;top:5px;right:10px}.floating-buttons,.floating-buttons .btn{z-index:3000}.file-zoom-dialog .kv-zoom-actions .btn,.floating-buttons .btn{margin-left:3px}.file-zoom-dialog .btn-navigate:not([disabled]):hover,.file-zoom-dialog .btn-navigate:not([disabled]):focus{outline:0;box-shadow:none;opacity:.5}.file-zoom-dialog .btn-navigate[disabled]{opacity:.3}.file-zoom-dialog .btn-prev{left:1px}.file-zoom-dialog .btn-next{right:1px}.file-drag-handle{display:inline;margin-right:2px;font-size:16px;cursor:move;cursor:-webkit-grabbing}.file-drag-handle:hover{opacity:.7}.file-zoom-content{height:480px;text-align:center}.file-preview-initial.sortable-chosen{background-color:#d9edf7}.file-preview-frame.sortable-ghost{background-color:#eee}.btn-file ::-ms-browse{width:100%;height:100%} \ No newline at end of file + */.btn-file input[type=file],.file-caption-icon,.file-no-browse,.file-preview .fileinput-remove,.file-zoom-dialog .btn-navigate,.file-zoom-dialog .floating-buttons,.krajee-default .file-thumb-progress{position:absolute}.file-loading input[type=file],input[type=file].file-loading{width:0;height:0}.file-no-browse{left:50%;bottom:20%;width:1px;height:1px;font-size:0;opacity:0;border:none;background:0 0;outline:0;box-shadow:none}.file-caption-icon,.file-input-ajax-new .fileinput-remove-button,.file-input-ajax-new .fileinput-upload-button,.file-input-ajax-new .no-browse .input-group-btn,.file-input-new .close,.file-input-new .file-preview,.file-input-new .fileinput-remove-button,.file-input-new .fileinput-upload-button,.file-input-new .glyphicon-file,.file-input-new .no-browse .input-group-btn,.file-zoom-dialog .modal-header:after,.file-zoom-dialog .modal-header:before,.hide-content .kv-file-content,.kv-hidden{display:none}.btn-file,.file-caption,.file-input,.file-loading:before,.file-preview,.file-zoom-dialog .modal-dialog,.krajee-default .file-thumbnail-footer,.krajee-default.file-preview-frame{position:relative}.file-error-message pre,.file-error-message ul,.krajee-default .file-actions,.krajee-default .file-other-error{text-align:left}.file-error-message pre,.file-error-message ul{margin:0}.krajee-default .file-drag-handle,.krajee-default .file-upload-indicator{float:left;margin:5px 0 -5px;width:16px;height:16px}.krajee-default .file-thumb-progress .progress,.krajee-default .file-thumb-progress .progress-bar{height:11px;font-size:9px;line-height:10px}.krajee-default .file-caption-info,.krajee-default .file-size-info{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:160px;height:15px;margin:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-image,.file-zoom-content>.file-object.type-video{max-width:100%;max-height:100%;width:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-video{height:100%}.file-zoom-content>.file-object.type-default,.file-zoom-content>.file-object.type-html,.file-zoom-content>.file-object.type-pdf,.file-zoom-content>.file-object.type-text{width:100%}.rotate-2{transform:rotateY(180deg)}.rotate-3{transform:rotate(180deg)}.rotate-4{transform:rotate(180deg) rotateY(180deg)}.rotate-5{transform:rotate(270deg) rotateY(180deg)}.rotate-6{transform:rotate(90deg)}.rotate-7{transform:rotate(90deg) rotateY(180deg)}.rotate-8{transform:rotate(270deg)}.file-loading:before{content:" Loading...";display:inline-block;padding-left:20px;line-height:16px;font-size:13px;font-variant:small-caps;color:#999;background:url(../img/loading.gif) top left no-repeat}.file-object{margin:0 0 -5px;padding:0}.btn-file{overflow:hidden}.btn-file input[type=file]{top:0;left:0;min-width:100%;min-height:100%;text-align:right;opacity:0;background:none;cursor:inherit;display:block}.btn-file ::-ms-browse{font-size:10000px;width:100%;height:100%}.file-caption .file-caption-name{width:100%;margin:0;padding:0;box-shadow:none;border:none;background:0 0;outline:0}.file-caption.icon-visible .file-caption-icon{display:inline-block}.file-caption.icon-visible .file-caption-name{padding-left:15px}.file-caption-icon{left:8px}.file-error-message{color:#a94442;background-color:#f2dede;margin:5px;border:1px solid #ebccd1;border-radius:4px;padding:15px}.file-error-message pre{margin:5px 0}.file-caption-disabled{background-color:#eee;cursor:not-allowed;opacity:1}.file-preview{border-radius:5px;border:1px solid #ddd;padding:8px;width:100%;margin-bottom:5px}.file-preview .btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.file-preview .fileinput-remove{top:1px;right:1px;line-height:10px}.file-preview .clickable{cursor:pointer}.file-preview-image{font:40px Impact,Charcoal,sans-serif;color:green}.krajee-default.file-preview-frame{margin:8px;border:1px solid #ddd;box-shadow:1px 1px 5px 0 #a2958a;padding:6px;float:left;text-align:center}.krajee-default.file-preview-frame .kv-file-content{width:213px;height:160px}.krajee-default.file-preview-frame .file-thumbnail-footer{height:70px}.krajee-default.file-preview-frame:not(.file-preview-error):hover{box-shadow:3px 3px 5px 0 #333}.krajee-default .file-preview-text{display:block;color:#428bca;border:1px solid #ddd;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;outline:0;padding:8px;resize:none}.krajee-default .file-preview-html{border:1px solid #ddd;padding:8px;overflow:auto}.krajee-default .file-other-icon{font-size:6em}.krajee-default .file-footer-buttons{float:right}.krajee-default .file-footer-caption{display:block;text-align:center;padding-top:4px;font-size:11px;color:#777;margin-bottom:15px}.krajee-default .file-preview-error{opacity:.65;box-shadow:none}.krajee-default .file-thumb-progress{height:11px;top:37px;left:0;right:0}.krajee-default.kvsortable-ghost{background:#e1edf7;border:2px solid #a1abff}.krajee-default .file-preview-other:hover{opacity:.8}.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover{color:#000}.kv-upload-progress .progress{height:20px;line-height:20px;margin:10px 0;overflow:hidden}.kv-upload-progress .progress-bar{height:20px;line-height:20px}.file-zoom-dialog .file-other-icon{font-size:22em;font-size:50vmin}.file-zoom-dialog .modal-dialog{width:auto}.file-zoom-dialog .modal-header{display:flex;align-items:center;justify-content:space-between}.file-zoom-dialog .btn-navigate{padding:0;margin:0;background:0 0;text-decoration:none;outline:0;opacity:.7;top:45%;font-size:4em;color:#1c94c4}.file-zoom-dialog .btn-navigate:not([disabled]):hover{outline:0;box-shadow:none;opacity:.6}.file-zoom-dialog .floating-buttons{top:5px;right:10px}.file-zoom-dialog .btn-navigate[disabled]{opacity:.3}.file-zoom-dialog .btn-prev{left:1px}.file-zoom-dialog .btn-next{right:1px}.file-zoom-dialog .kv-zoom-title{font-weight:300;color:#999;max-width:50%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-input-ajax-new .no-browse .form-control,.file-input-new .no-browse .form-control{border-top-right-radius:4px;border-bottom-right-radius:4px}.file-caption-main{width:100%}.file-thumb-loading{background:url(../img/loading.gif) center center no-repeat content-box!important}.file-drop-zone{border:1px dashed #aaa;border-radius:4px;height:100%;text-align:center;vertical-align:middle;margin:12px 15px 12px 12px;padding:5px}.file-drop-zone.clickable:hover{border:2px dashed #999}.file-drop-zone.clickable:focus{border:2px solid #5acde2}.file-drop-zone .file-preview-thumbnails{cursor:default}.file-drop-zone-title{color:#aaa;font-size:1.6em;padding:85px 10px;cursor:default}.file-highlighted{border:2px dashed #999!important;background-color:#eee}.file-uploading{background:url(../img/loading-sm.gif) center bottom 10px no-repeat;opacity:.65}@media (min-width:576px){.file-zoom-dialog .modal-dialog{max-width:500px}}@media (min-width:992px){.file-zoom-dialog .modal-lg{max-width:800px}}.file-zoom-fullscreen.modal{position:fixed;top:0;right:0;bottom:0;left:0}.file-zoom-fullscreen .modal-dialog{position:fixed;margin:0;padding:0;width:100%;height:100%;max-width:100%;max-height:100%}.file-zoom-fullscreen .modal-content{border-radius:0;box-shadow:none}.file-zoom-fullscreen .modal-body{overflow-y:auto}.floating-buttons{z-index:3000}.floating-buttons .btn-kv{margin-left:3px;z-index:3000}.file-zoom-content{height:480px;text-align:center}.file-zoom-content .file-preview-image,.file-zoom-content .file-preview-video{max-height:100%}.file-zoom-content .is-portrait-gt4{margin-top:60px}.file-zoom-content>.file-object.type-image{height:auto;min-height:inherit}.file-zoom-content>.file-object.type-audio{width:auto;height:30px}@media screen and (max-width:767px){.file-preview-thumbnails{display:flex;justify-content:center;align-items:center;flex-direction:column}.file-zoom-dialog .modal-header{flex-direction:column}}@media screen and (max-width:350px){.krajee-default.file-preview-frame .kv-file-content{width:160px}}.file-loading[dir=rtl]:before{background:url(../img/loading.gif) top right no-repeat;padding-left:0;padding-right:20px}.file-sortable .file-drag-handle{cursor:move;opacity:1}.file-sortable .file-drag-handle:hover{opacity:.7}.clickable .file-drop-zone-title{cursor:pointer}.kv-zoom-actions .btn-kv{margin-left:3px}.file-preview-initial.sortable-chosen{background-color:#d9edf7} \ No newline at end of file diff --git a/Bootstrap.Admin/wwwroot/js/Profiles.js b/Bootstrap.Admin/wwwroot/js/Profiles.js index ef6bc581..dd03f267 100644 --- a/Bootstrap.Admin/wwwroot/js/Profiles.js +++ b/Bootstrap.Admin/wwwroot/js/Profiles.js @@ -2,7 +2,8 @@ var $headerIcon = $('#headerIcon'); var preIcon = $headerIcon.attr('src'); $('#fileIcon').fileinput({ - uploadUrl: Profiles.url, + uploadUrl: $.formatUrl(Profiles.url), + browseOnZoneClick: true, language: 'zh', maxFileSize: 5000, allowedFileExtensions: ['jpg', 'png', 'bmp', 'gif', 'jpeg'], @@ -10,13 +11,13 @@ preIcon ], initialPreviewConfig: [ - { caption: "现在头像", size: $('#fileIcon').attr('data-init'), showZoom: false }, + { caption: "现在头像", size: $('#fileIcon').attr('data-init'), showZoom: true }, ], initialPreviewAsData: true, overwriteInitial: true, dropZoneTitle: "请选择头像" }).on('fileuploaded', function (event, data, previewId, index) { - var url = data.response; + var url = data.response.initialPreview[0]; if (!!url) $headerIcon.attr('src', url); }); @@ -60,5 +61,4 @@ } }); $('button[data-admin="False"]').removeAttr('disabled'); - $('#kvFileinputModal').appendTo('body'); }); \ No newline at end of file diff --git a/Bootstrap.Admin/wwwroot/js/fileinput.js b/Bootstrap.Admin/wwwroot/js/fileinput.js index 2537c829..9b63fac6 100644 --- a/Bootstrap.Admin/wwwroot/js/fileinput.js +++ b/Bootstrap.Admin/wwwroot/js/fileinput.js @@ -1,9 +1,9 @@ -/*! - * bootstrap-fileinput v4.3.6 +/*! + * bootstrap-fileinput v4.4.9 * http://plugins.krajee.com/file-input * * Author: Kartik Visweswaran - * Copyright: 2014 - 2016, Kartik Visweswaran, Krajee.com + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com * * Licensed under the BSD 3-Clause * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md @@ -30,614 +30,493 @@ $.fn.fileinputLocales = {}; $.fn.fileinputThemes = {}; - var NAMESPACE, MODAL_ID, STYLE_SETTING, OBJECT_PARAMS, DEFAULT_PREVIEW, objUrl, compare, isIE, handler, - previewCache, getNum, hasFileAPISupport, hasDragDropSupport, hasFileUploadSupport, addCss, tMain1, tMain2, - tPreview, tFileIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse, tModalMain, tModal, tProgress, tSize, - tFooter, tActions, tActionDelete, tActionUpload, tActionZoom, tActionDrag, tTagBef, tTagBef1, tTagBef2, tTagAft, - tGeneric, tHtml, tImage, tText, tVideo, tAudio, tFlash, tObject, tPdf, tOther, defaultFileActionSettings, - defaultLayoutTemplates, defaultPreviewTemplates, defaultPreviewZoomSettings, defaultPreviewTypes, getElement, - defaultPreviewSettings, defaultFileTypeSettings, isEmpty, isArray, ifSet, uniqId, htmlEncode, replaceTags, - cleanMemory, findFileName, checkFullScreen, toggleFullScreen, moveArray, FileInput; + String.prototype.setTokens = function (replacePairs) { + var str = this.toString(), key, re; + for (key in replacePairs) { + if (replacePairs.hasOwnProperty(key)) { + re = new RegExp("\{" + key + "\}", "g"); + str = str.replace(re, replacePairs[key]); + } + } + return str; + }; - NAMESPACE = '.fileinput'; - MODAL_ID = 'kvFileinputModal'; - STYLE_SETTING = 'style="width:{width};height:{height};"'; - OBJECT_PARAMS = '\n' + + var $h, FileInput; + + // fileinput helper object for all global variables and internal helper methods + //noinspection JSUnresolvedVariable + $h = { + FRAMES: '.kv-preview-thumb', + SORT_CSS: 'file-sortable', + OBJECT_PARAMS: '\n' + '\n' + '\n' + '\n' + '\n' + - '\n'; - DEFAULT_PREVIEW = '
\n' + + '\n', + DEFAULT_PREVIEW: '
\n' + '{previewFileIcon}\n' + - '
'; - //noinspection JSUnresolvedVariable - objUrl = window.URL || window.webkitURL; - compare = function (input, str, exact) { - return input !== undefined && (exact ? input === str : input.match(str)); - }; - isIE = function (ver) { - // check for IE versions < 11 - if (navigator.appName !== 'Microsoft Internet Explorer') { + '
', + MODAL_ID: 'kvFileinputModal', + MODAL_EVENTS: ['show', 'shown', 'hide', 'hidden', 'loaded'], + objUrl: window.URL || window.webkitURL, + compare: function (input, str, exact) { + return input !== undefined && (exact ? input === str : input.match(str)); + }, + isIE: function (ver) { + // check for IE versions < 11 + if (navigator.appName !== 'Microsoft Internet Explorer') { + return false; + } + if (ver === 10) { + return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent); + } + var div = document.createElement("div"), status; + div.innerHTML = ""; + status = div.getElementsByTagName("i").length; + document.body.appendChild(div); + div.parentNode.removeChild(div); + return status; + }, + canAssignFilesToInput: function () { + var input = document.createElement('input'); + try { + input.type = "file"; + input.files = null; + return true; + } catch (err) { + return false; + } + }, + getDragDropFolders: function (items) { + var i, item, len = items.length, folders = 0; + if (len > 0 && items[0].webkitGetAsEntry()) { + for (i = 0; i < len; i++) { + item = items[i].webkitGetAsEntry(); + if (item && item.isDirectory) { + folders++; + } + } + } + return folders; + }, + initModal: function ($modal) { + var $body = $('body'); + if ($body.length) { + $modal.appendTo($body); + } + }, + isEmpty: function (value, trim) { + return value === undefined || value === null || value.length === 0 || (trim && $.trim(value) === ''); + }, + isArray: function (a) { + return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; + }, + ifSet: function (needle, haystack, def) { + def = def || ''; + return (haystack && typeof haystack === 'object' && needle in haystack) ? haystack[needle] : def; + }, + cleanArray: function (arr) { + if (!(arr instanceof Array)) { + arr = []; + } + return arr.filter(function (e) { + return (e !== undefined && e !== null); + }); + }, + spliceArray: function (arr, index, reverseOrder) { + var i, j = 0, out = [], newArr; + if (!(arr instanceof Array)) { + return []; + } + newArr = $.extend(true, [], arr); + if (reverseOrder) { + newArr.reverse(); + } + for (i = 0; i < newArr.length; i++) { + if (i !== index) { + out[j] = newArr[i]; + j++; + } + } + if (reverseOrder) { + out.reverse(); + } + return out; + }, + getNum: function (num, def) { + def = def || 0; + if (typeof num === "number") { + return num; + } + if (typeof num === "string") { + num = parseFloat(num); + } + return isNaN(num) ? def : num; + }, + hasFileAPISupport: function () { + return !!(window.File && window.FileReader); + }, + hasDragDropSupport: function () { + var div = document.createElement('div'); + /** @namespace div.draggable */ + /** @namespace div.ondragstart */ + /** @namespace div.ondrop */ + return !$h.isIE(9) && + (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); + }, + hasFileUploadSupport: function () { + return $h.hasFileAPISupport() && window.FormData; + }, + hasBlobSupport: function () { + try { + return !!window.Blob && Boolean(new Blob()); + } catch (e) { + return false; + } + }, + hasArrayBufferViewSupport: function () { + try { + return new Blob([new Uint8Array(100)]).size === 100; + } catch (e) { + return false; + } + }, + dataURI2Blob: function (dataURI) { + //noinspection JSUnresolvedVariable + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || + window.MSBlobBuilder, canBlob = $h.hasBlobSupport(), byteStr, arrayBuffer, intArray, i, mimeStr, bb, + canProceed = (canBlob || BlobBuilder) && window.atob && window.ArrayBuffer && window.Uint8Array; + if (!canProceed) { + return null; + } + if (dataURI.split(',')[0].indexOf('base64') >= 0) { + byteStr = atob(dataURI.split(',')[1]); + } else { + byteStr = decodeURIComponent(dataURI.split(',')[1]); + } + arrayBuffer = new ArrayBuffer(byteStr.length); + intArray = new Uint8Array(arrayBuffer); + for (i = 0; i < byteStr.length; i += 1) { + intArray[i] = byteStr.charCodeAt(i); + } + mimeStr = dataURI.split(',')[0].split(':')[1].split(';')[0]; + if (canBlob) { + return new Blob([$h.hasArrayBufferViewSupport() ? intArray : arrayBuffer], {type: mimeStr}); + } + bb = new BlobBuilder(); + bb.append(arrayBuffer); + return bb.getBlob(mimeStr); + }, + arrayBuffer2String: function (buffer) { + //noinspection JSUnresolvedVariable + if (window.TextDecoder) { + // noinspection JSUnresolvedFunction + return new TextDecoder("utf-8").decode(buffer); + } + var array = Array.prototype.slice.apply(new Uint8Array(buffer)), out = '', i = 0, len, c, char2, char3; + len = array.length; + while (i < len) { + c = array[i++]; + switch (c >> 4) { // jshint ignore:line + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); // jshint ignore:line + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | // jshint ignore:line + ((char2 & 0x3F) << 6) | // jshint ignore:line + ((char3 & 0x3F) << 0)); // jshint ignore:line + break; + } + } + return out; + }, + isHtml: function (str) { + var a = document.createElement('div'); + a.innerHTML = str; + for (var c = a.childNodes, i = c.length; i--;) { + if (c[i].nodeType === 1) { + return true; + } + } return false; - } - if (ver === 10) { - return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent); - } - var div = document.createElement("div"), status; - div.innerHTML = ""; - status = div.getElementsByTagName("i").length; - document.body.appendChild(div); - div.parentNode.removeChild(div); - return status; - }; - handler = function ($el, event, callback, skipNS) { - var ev = skipNS ? event : event.split(' ').join(NAMESPACE + ' ') + NAMESPACE; - $el.off(ev).on(ev, callback); - }; - previewCache = { - data: {}, - init: function (obj) { - var content = obj.initialPreview, id = obj.id; - if (content.length > 0 && !isArray(content)) { - content = content.split(obj.initialPreviewDelimiter); + }, + isSvg: function (str) { + return str.match(/^\s*<\?xml/i) && (str.match(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + replaceTags: function (str, tags) { + var out = str; + if (!tags) { + return out; + } + $.each(tags, function (key, value) { + if (typeof value === "function") { + value = value(); } + out = out.split(key).join(value); + }); + return out; + }, + cleanMemory: function ($thumb) { + var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src'); + /** @namespace $h.objUrl.revokeObjectURL */ + $h.objUrl.revokeObjectURL(data); + }, + findFileName: function (filePath) { + var sepIndex = filePath.lastIndexOf('/'); + if (sepIndex === -1) { + sepIndex = filePath.lastIndexOf('\\'); + } + return filePath.split(filePath.substring(sepIndex, sepIndex + 1)).pop(); + }, + checkFullScreen: function () { + //noinspection JSUnresolvedVariable + return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || + document.msFullscreenElement; + }, + toggleFullScreen: function (maximize) { + var doc = document, de = doc.documentElement; + if (de && maximize && !$h.checkFullScreen()) { + /** @namespace document.requestFullscreen */ + /** @namespace document.msRequestFullscreen */ + /** @namespace document.mozRequestFullScreen */ + /** @namespace document.webkitRequestFullscreen */ + /** @namespace Element.ALLOW_KEYBOARD_INPUT */ + if (de.requestFullscreen) { + de.requestFullscreen(); + } else if (de.msRequestFullscreen) { + de.msRequestFullscreen(); + } else if (de.mozRequestFullScreen) { + de.mozRequestFullScreen(); + } else if (de.webkitRequestFullscreen) { + de.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + } else { + /** @namespace document.exitFullscreen */ + /** @namespace document.msExitFullscreen */ + /** @namespace document.mozCancelFullScreen */ + /** @namespace document.webkitExitFullscreen */ + if (doc.exitFullscreen) { + doc.exitFullscreen(); + } else if (doc.msExitFullscreen) { + doc.msExitFullscreen(); + } else if (doc.mozCancelFullScreen) { + doc.mozCancelFullScreen(); + } else if (doc.webkitExitFullscreen) { + doc.webkitExitFullscreen(); + } + } + }, + moveArray: function (arr, oldIndex, newIndex, reverseOrder) { + var newArr = $.extend(true, [], arr); + if (reverseOrder) { + newArr.reverse(); + } + if (newIndex >= newArr.length) { + var k = newIndex - newArr.length; + while ((k--) + 1) { + newArr.push(undefined); + } + } + newArr.splice(newIndex, 0, newArr.splice(oldIndex, 1)[0]); + if (reverseOrder) { + newArr.reverse(); + } + return newArr; + }, + cleanZoomCache: function ($el) { + var $cache = $el.closest('.kv-zoom-cache-theme'); + if (!$cache.length) { + $cache = $el.closest('.kv-zoom-cache'); + } + $cache.remove(); + }, + setOrientation: function (buffer, callback) { + var scanner = new DataView(buffer), idx = 0, value = 1, // Non-rotated is the default + maxBytes, uInt16, exifLength; + if (scanner.getUint16(idx) !== 0xFFD8 || buffer.length < 2) { // not a proper JPEG + if (callback) { + callback(); + } + return; + } + idx += 2; + maxBytes = scanner.byteLength; + while (idx < maxBytes - 2) { + uInt16 = scanner.getUint16(idx); + idx += 2; + switch (uInt16) { + case 0xFFE1: // Start of EXIF + exifLength = scanner.getUint16(idx); + maxBytes = exifLength - idx; + idx += 2; + break; + case 0x0112: // Orientation tag + value = scanner.getUint16(idx + 6, false); + maxBytes = 0; // Stop scanning + break; + } + } + if (callback) { + callback(value); + } + }, + validateOrientation: function (file, callback) { + if (!window.FileReader || !window.DataView) { + return; // skip orientation if pre-requisite libraries not supported by browser + } + var reader = new FileReader(), buffer; + reader.onloadend = function () { + buffer = reader.result; + $h.setOrientation(buffer, callback); }; + reader.readAsArrayBuffer(file); }, - fetch: function (id) { - return previewCache.data[id].content.filter(function (n) { - return n !== null; - }); - }, - count: function (id, all) { - return !!previewCache.data[id] && !!previewCache.data[id].content ? - (all ? previewCache.data[id].content.length : previewCache.fetch(id).length) : 0; - }, - get: function (id, i, isDisabled) { - var ind = 'init_' + i, data = previewCache.data[id], config = data.config[i], content = data.content[i], - previewId = data.initId + '-' + ind, out, $tmp, frameClass = ' file-preview-initial', cat, cap, ftr, - ftype, asData = ifSet('previewAsData', config, data.previewAsData); - isDisabled = isDisabled === undefined ? true : isDisabled; - /** @namespace config.frameAttr */ - /** @namespace config.frameClass */ - /** @namespace config.filetype */ - if (!content) { - return ''; + adjustOrientedImage: function ($img, isZoom) { + var offsetContTop, offsetTop, newTop; + if (!$img.hasClass('is-portrait-gt4')) { + return; } - if (config && config.frameClass) { - frameClass += ' ' + config.frameClass; - } - if (asData) { - cat = data.previewAsData ? ifSet('type', config, data.previewFileType || 'generic') : 'generic'; - cap = ifSet('caption', config); - ftr = previewCache.footer(id, i, isDisabled, (config && config.size || null)); - ftype = ifSet('filetype', config, cat); - out = data.parseTemplate(cat, content, cap, ftype, previewId, ftr, ind, null); + if (isZoom) { + $img.css({width: $img.parent().height()}); + return; } else { - out = data.template - .replace(/\{previewId}/g, previewId).replace(/\{frameClass}/g, frameClass) - .replace(/\{fileindex}/g, ind).replace(/\{content}/g, data.content[i]) - .replace(/\{template}/g, ifSet('type', config, data.previewFileType)) - .replace(/\{footer}/g, previewCache.footer(id, i, isDisabled, (config && config.size || null))); + $img.css({height: 'auto', width: $img.height()}); } - if (data.tags.length && data.tags[i]) { - out = replaceTags(out, data.tags[i]); - } - if (!isEmpty(config) && !isEmpty(config.frameAttr)) { - $tmp = $(document.createElement('div')).html(out); - $tmp.find('.file-preview-initial').attr(config.frameAttr); - out = $tmp.html(); - $tmp.remove(); - } - return out; + offsetContTop = $img.parent().offset().top; + offsetTop = $img.offset().top; + newTop = offsetContTop - offsetTop; + $img.css('margin-top', newTop); }, - add: function (id, content, config, tags, append) { - var data = $.extend(true, {}, previewCache.data[id]), index; - if (!isArray(content)) { - content = content.split(data.delimiter); - } - if (append) { - index = data.content.push(content) - 1; - data.config[index] = config; - data.tags[index] = tags; - } else { - index = content.length - 1; - data.content = content; - data.config = config; - data.tags = tags; - } - previewCache.data[id] = data; - return index; - }, - set: function (id, content, config, tags, append) { - var data = $.extend(true, {}, previewCache.data[id]), i, chk; - if (!content || !content.length) { - return; - } - if (!isArray(content)) { - content = content.split(data.delimiter); - } - chk = content.filter(function (n) { - return n !== null; - }); - if (!chk.length) { - return; - } - if (data.content === undefined) { - data.content = []; - } - if (data.config === undefined) { - data.config = []; - } - if (data.tags === undefined) { - data.tags = []; - } - if (append) { - for (i = 0; i < content.length; i++) { - if (content[i]) { - data.content.push(content[i]); - } - } - for (i = 0; i < config.length; i++) { - if (config[i]) { - data.config.push(config[i]); - } - } - for (i = 0; i < tags.length; i++) { - if (tags[i]) { - data.tags.push(tags[i]); - } - } - } else { - data.content = content; - data.config = config; - data.tags = tags; - } - previewCache.data[id] = data; - }, - unset: function (obj, index) { - var chk = previewCache.count(obj.id); - if (!chk) { - return; - } - if (chk === 1) { - previewCache.data[obj.id].content = []; - previewCache.data[obj.id].config = []; - previewCache.data[obj.id].tags = []; - obj.initialPreview = []; - obj.initialPreviewConfig = []; - obj.initialPreviewThumbTags = []; - return; - } - - previewCache.data[obj.id].content[index] = null; - previewCache.data[obj.id].config[index] = null; - previewCache.data[obj.id].tags[index] = null; - }, - out: function (id) { - var html = '', data = previewCache.data[id], caption, len = previewCache.count(id, true); - if (len === 0) { - return { content: '', caption: '' }; - } - for (var i = 0; i < len; i++) { - html += previewCache.get(id, i); - } - caption = data.msg(previewCache.count(id)); - return { content: '
' + html + '
', caption: caption }; - }, - footer: function (id, i, isDisabled, size) { - var data = previewCache.data[id]; - isDisabled = isDisabled === undefined ? true : isDisabled; - if (data.config.length === 0 || isEmpty(data.config[i])) { - return ''; - } - var config = data.config[i], caption = ifSet('caption', config), width = ifSet('width', config, 'auto'), - url = ifSet('url', config, false), key = ifSet('key', config, null), - showDel = ifSet('showDelete', config, true), showZoom = ifSet('showZoom', config, data.showZoom), - showDrag = ifSet('showDrag', config, data.showDrag), disabled = (url === false) && isDisabled, - actions = data.isDelete ? data.actions(false, showDel, showZoom, showDrag, disabled, url, key) : '', - footer = data.footer.replace(/\{actions}/g, actions); - return footer.replace(/\{caption}/g, caption).replace(/\{size}/g, data.getSize(size)) - .replace(/\{width}/g, width).replace(/\{indicator}/g, '').replace(/\{indicatorTitle}/g, ''); + closeButton: function (css) { + css = css ? 'close ' + css : 'close'; + return ''; } }; - getNum = function (num, def) { - def = def || 0; - if (typeof num === "number") { - return num; - } - if (typeof num === "string") { - num = parseFloat(num); - } - return isNaN(num) ? def : num; - }; - hasFileAPISupport = function () { - return !!(window.File && window.FileReader); - }; - hasDragDropSupport = function () { - var div = document.createElement('div'); - /** @namespace div.draggable */ - /** @namespace div.ondragstart */ - /** @namespace div.ondrop */ - return !isIE(9) && (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); - }; - hasFileUploadSupport = function () { - return hasFileAPISupport() && window.FormData; - }; - addCss = function ($el, css) { - $el.removeClass(css).addClass(css); - }; - defaultFileActionSettings = { - showRemove: true, - showUpload: true, - showZoom: true, - showDrag: true, - removeIcon: '', - removeClass: 'btn btn-xs btn-default', - removeTitle: 'Remove file', - uploadIcon: '', - uploadClass: 'btn btn-xs btn-default', - uploadTitle: 'Upload file', - zoomIcon: '', - zoomClass: 'btn btn-xs btn-default', - zoomTitle: 'View Details', - dragIcon: '', - dragClass: 'text-info', - dragTitle: 'Move / Rearrange', - dragSettings: {}, - indicatorNew: '', - indicatorSuccess: '', - indicatorError: '', - indicatorLoading: '', - indicatorNewTitle: 'Not uploaded yet', - indicatorSuccessTitle: 'Uploaded', - indicatorErrorTitle: 'Upload Error', - indicatorLoadingTitle: 'Uploading ...' - }; - tMain1 = '{preview}\n' + - '
\n' + - '
\n' + - ' {caption}\n' + - '
\n' + - ' {remove}\n' + - ' {cancel}\n' + - ' {upload}\n' + - ' {browse}\n' + - '
\n' + - '
'; - tMain2 = '{preview}\n
\n{remove}\n{cancel}\n{upload}\n{browse}\n'; - tPreview = '
\n' + - ' {close}' + - '
\n' + - '
\n' + - '
\n' + - '
' + - '
\n' + - '
\n' + - '
\n' + - '
'; - tClose = '
×
\n'; - tFileIcon = ''; - tCaption = '
\n' + - '
\n' + - '
\n'; - //noinspection HtmlUnknownAttribute - tBtnDefault = ''; - //noinspection HtmlUnknownAttribute - tBtnLink = '{icon} {label}'; - //noinspection HtmlUnknownAttribute - tBtnBrowse = '
{icon} {label}
'; - tModalMain = ''; - tModal = '\n'; - tProgress = '
\n' + - '
\n' + - ' {percent}%\n' + - '
\n' + - '
'; - tSize = '
({sizeText})'; - tFooter = ''; - tActions = '
\n' + - ' \n' + - ' {drag}\n' + - '
{indicator}
\n' + - '
\n' + - '
'; - //noinspection HtmlUnknownAttribute - tActionDelete = '\n'; - tActionUpload = ''; - tActionZoom = ''; - tActionDrag = '{dragIcon}'; - tTagBef = '
\n'; - tTagBef2 = tTagBef + ' title="{caption}" ' + STYLE_SETTING + '>
\n'; - tTagAft = '
{footer}\n
\n'; - tGeneric = '{content}\n'; - tHtml = '
{data}
\n'; - tImage = '{caption}\n'; - tText = '\n'; - tVideo = '\n'; - tAudio = '\n'; - tFlash = '\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n\n'; - tObject = '\n' + - '\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n\n'; - tPdf = '\n'; - tOther = '
\n' + DEFAULT_PREVIEW + '\n
\n'; - defaultLayoutTemplates = { - main1: tMain1, - main2: tMain2, - preview: tPreview, - close: tClose, - fileIcon: tFileIcon, - caption: tCaption, - modalMain: tModalMain, - modal: tModal, - progress: tProgress, - size: tSize, - footer: tFooter, - actions: tActions, - actionDelete: tActionDelete, - actionUpload: tActionUpload, - actionZoom: tActionZoom, - actionDrag: tActionDrag, - btnDefault: tBtnDefault, - btnLink: tBtnLink, - btnBrowse: tBtnBrowse - }; - defaultPreviewTemplates = { - generic: tTagBef1 + tGeneric + tTagAft, - html: tTagBef1 + tHtml + tTagAft, - image: tTagBef1 + tImage + tTagAft, - text: tTagBef1 + tText + tTagAft, - video: tTagBef2 + tVideo + tTagAft, - audio: tTagBef2 + tAudio + tTagAft, - flash: tTagBef2 + tFlash + tTagAft, - object: tTagBef2 + tObject + tTagAft, - pdf: tTagBef2 + tPdf + tTagAft, - other: tTagBef2 + tOther + tTagAft - }; - defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'pdf', 'object']; - defaultPreviewSettings = { - image: { width: "auto", height: "160px" }, - html: { width: "213px", height: "160px" }, - text: { width: "213px", height: "160px" }, - video: { width: "213px", height: "160px" }, - audio: { width: "213px", height: "80px" }, - flash: { width: "213px", height: "160px" }, - object: { width: "160px", height: "160px" }, - pdf: { width: "160px", height: "160px" }, - other: { width: "160px", height: "160px" } - }; - defaultPreviewZoomSettings = { - image: { width: "auto", height: "auto", 'max-width': "100%", 'max-height': "100%" }, - html: { width: "100%", height: "100%", 'min-height': "480px" }, - text: { width: "100%", height: "100%", 'min-height': "480px" }, - video: { width: "auto", height: "100%", 'max-width': "100%" }, - audio: { width: "100%", height: "30px" }, - flash: { width: "auto", height: "480px" }, - object: { width: "auto", height: "100%", 'min-height': "480px" }, - pdf: { width: "100%", height: "100%", 'min-height': "480px" }, - other: { width: "auto", height: "100%", 'min-height': "480px" } - }; - defaultFileTypeSettings = { - image: function (vType, vName) { - return compare(vType, 'image.*') || compare(vName, /\.(gif|png|jpe?g)$/i); - }, - html: function (vType, vName) { - return compare(vType, 'text/html') || compare(vName, /\.(htm|html)$/i); - }, - text: function (vType, vName) { - return compare(vType, 'text.*') || compare(vName, /\.(xml|javascript)$/i) || - compare(vName, /\.(txt|md|csv|nfo|ini|json|php|js|css)$/i); - }, - video: function (vType, vName) { - return compare(vType, 'video.*') && (compare(vType, /(ogg|mp4|mp?g|webm|3gp)$/i) || - compare(vName, /\.(og?|mp4|webm|mp?g|3gp)$/i)); - }, - audio: function (vType, vName) { - return compare(vType, 'audio.*') && (compare(vName, /(ogg|mp3|mp?g|wav)$/i) || - compare(vName, /\.(og?|mp3|mp?g|wav)$/i)); - }, - flash: function (vType, vName) { - return compare(vType, 'application/x-shockwave-flash', true) || compare(vName, /\.(swf)$/i); - }, - pdf: function (vType, vName) { - return compare(vType, 'application/pdf', true) || compare(vName, /\.(pdf)$/i); - }, - object: function () { - return true; - }, - other: function () { - return true; - } - }; - isEmpty = function (value, trim) { - return value === undefined || value === null || value.length === 0 || (trim && $.trim(value) === ''); - }; - isArray = function (a) { - return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; - }; - ifSet = function (needle, haystack, def) { - def = def || ''; - return (haystack && typeof haystack === 'object' && needle in haystack) ? haystack[needle] : def; - }; - getElement = function (options, param, value) { - return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]); - }; - uniqId = function () { - return Math.round(new Date().getTime() + (Math.random() * 100)); - }; - htmlEncode = function (str) { - return str.replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - }; - replaceTags = function (str, tags) { - var out = str; - if (!tags) { - return out; - } - $.each(tags, function (key, value) { - if (typeof value === "function") { - value = value(); - } - out = out.split(key).join(value); - }); - return out; - }; - cleanMemory = function ($thumb) { - var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src'); - /** @namespace objUrl.revokeObjectURL */ - objUrl.revokeObjectURL(data); - }; - findFileName = function (filePath) { - var sepIndex = filePath.lastIndexOf('/'); - if (sepIndex === -1) { - sepIndex = filePath.lastIndexOf('\\'); - } - return filePath.split(filePath.substring(sepIndex, sepIndex + 1)).pop(); - }; - checkFullScreen = function () { - //noinspection JSUnresolvedVariable - return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || - document.msFullscreenElement; - }; - toggleFullScreen = function (maximize) { - if (maximize && !checkFullScreen()) { - /** @namespace document.documentElement.requestFullscreen */ - /** @namespace document.documentElement.msRequestFullscreen */ - /** @namespace document.documentElement.mozRequestFullScreen */ - /** @namespace document.documentElement.webkitRequestFullscreen */ - /** @namespace Element.ALLOW_KEYBOARD_INPUT */ - if (document.documentElement.requestFullscreen) { - document.documentElement.requestFullscreen(); - } else if (document.documentElement.msRequestFullscreen) { - document.documentElement.msRequestFullscreen(); - } else if (document.documentElement.mozRequestFullScreen) { - document.documentElement.mozRequestFullScreen(); - } else if (document.documentElement.webkitRequestFullscreen) { - document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } - } else { - /** @namespace document.exitFullscreen */ - /** @namespace document.msExitFullscreen */ - /** @namespace document.mozCancelFullScreen */ - /** @namespace document.webkitExitFullscreen */ - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } - } - }; - moveArray = function (arr, oldIndex, newIndex) { - if (newIndex >= arr.length) { - var k = newIndex - arr.length; - while ((k--) + 1) { - arr.push(undefined); - } - } - arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]); - return arr; - }; FileInput = function (element, options) { var self = this; self.$element = $(element); + self.$parent = self.$element.parent(); if (!self._validate()) { return; } - self.isPreviewable = hasFileAPISupport(); - self.isIE9 = isIE(9); - self.isIE10 = isIE(10); + self.isPreviewable = $h.hasFileAPISupport(); + self.isIE9 = $h.isIE(9); + self.isIE10 = $h.isIE(10); if (self.isPreviewable || self.isIE9) { self._init(options); self._listen(); - } else { - self.$element.removeClass('file-loading'); } + self.$element.removeClass('file-loading'); }; + //noinspection JSUnusedGlobalSymbols FileInput.prototype = { constructor: FileInput, - _init: function (options) { - var self = this, $el = self.$element, t; - $.each(options, function (key, value) { - switch (key) { - case 'minFileCount': - case 'maxFileCount': - case 'maxFileSize': - self[key] = getNum(value); - break; - default: - self[key] = value; - break; - } - }); - self.fileInputCleared = false; - self.fileBatchCompleted = true; - if (!self.isPreviewable) { - self.showPreview = false; - } - self.uploadFileAttr = !isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; + _cleanup: function () { + var self = this; self.reader = null; self.formdata = {}; - self.clearStack(); self.uploadCount = 0; self.uploadStatus = {}; self.uploadLog = []; @@ -645,114 +524,689 @@ self.loadedImages = []; self.totalImagesCount = 0; self.ajaxRequests = []; + self.clearStack(); + self.fileBatchCompleted = true; + if (!self.isPreviewable) { + self.showPreview = false; + } self.isError = false; self.ajaxAborted = false; self.cancelling = false; + }, + _init: function (options, refreshMode) { + var self = this, f, $el = self.$element, $cont, t, tmp; + self.options = options; + $.each(options, function (key, value) { + switch (key) { + case 'minFileCount': + case 'maxFileCount': + case 'minFileSize': + case 'maxFileSize': + case 'maxFilePreviewSize': + case 'resizeImageQuality': + case 'resizeIfSizeMoreThan': + case 'progressUploadThreshold': + case 'initialPreviewCount': + case 'zoomModalHeight': + case 'minImageHeight': + case 'maxImageHeight': + case 'minImageWidth': + case 'maxImageWidth': + self[key] = $h.getNum(value); + break; + default: + self[key] = value; + break; + } + }); + if (self.rtl) { // swap buttons for rtl + tmp = self.previewZoomButtonIcons.prev; + self.previewZoomButtonIcons.prev = self.previewZoomButtonIcons.next; + self.previewZoomButtonIcons.next = tmp; + } + if (!refreshMode) { + self._cleanup(); + } + self.$form = $el.closest('form'); + self._initTemplateDefaults(); + self.uploadFileAttr = !$h.isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; t = self._getLayoutTemplate('progress'); self.progressTemplate = t.replace('{class}', self.progressClass); self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); self.progressErrorTemplate = t.replace('{class}', self.progressErrorClass); - self.dropZoneEnabled = hasDragDropSupport() && self.dropZoneEnabled; - self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); - self.isUploadable = hasFileUploadSupport() && !isEmpty(self.uploadUrl); + self.isDisabled = $el.attr('disabled') || $el.attr('readonly'); + if (self.isDisabled) { + $el.attr('disabled', true); + } + self.isAjaxUpload = $h.hasFileUploadSupport() && !$h.isEmpty(self.uploadUrl); + self.dropZoneEnabled = $h.hasDragDropSupport() && self.dropZoneEnabled; + if (!self.isAjaxUpload) { + self.dropZoneEnabled = self.dropZoneEnabled && $h.canAssignFilesToInput(); + } self.isClickable = self.browseOnZoneClick && self.showPreview && - (self.isUploadable && self.dropZoneEnabled || !isEmpty(self.defaultPreviewContent)); + (self.dropZoneEnabled || !$h.isEmpty(self.defaultPreviewContent)); self.slug = typeof options.slugCallback === "function" ? options.slugCallback : self._slugDefault; self.mainTemplate = self.showCaption ? self._getLayoutTemplate('main1') : self._getLayoutTemplate('main2'); self.captionTemplate = self._getLayoutTemplate('caption'); self.previewGenericTemplate = self._getPreviewTemplate('generic'); - if (self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) { + if (!self.imageCanvas && self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) { self.imageCanvas = document.createElement('canvas'); self.imageCanvasContext = self.imageCanvas.getContext('2d'); } - if (isEmpty(self.$element.attr('id'))) { - self.$element.attr('id', uniqId()); + if ($h.isEmpty($el.attr('id'))) { + $el.attr('id', $h.uniqId()); } + self.namespace = '.fileinput_' + $el.attr('id').replace(/-/g, '_'); if (self.$container === undefined) { self.$container = self._createContainer(); } else { self._refreshContainer(); } - self.$dropZone = self.$container.find('.file-drop-zone'); - self.$progress = self.$container.find('.kv-upload-progress'); - self.$btnUpload = self.$container.find('.fileinput-upload'); - self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption')); - self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name')); - self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview')); - self.$preview = getElement(options, 'elPreviewImage', self.$container.find('.file-preview-thumbnails')); - self.$previewStatus = getElement(options, 'elPreviewStatus', self.$container.find('.file-preview-status')); - self.$errorContainer = getElement(options, 'elErrorContainer', - self.$previewContainer.find('.kv-fileinput-error')); - if (!isEmpty(self.msgErrorClass)) { - addCss(self.$errorContainer, self.msgErrorClass); + $cont = self.$container; + self.$dropZone = $cont.find('.file-drop-zone'); + self.$progress = $cont.find('.kv-upload-progress'); + self.$btnUpload = $cont.find('.fileinput-upload'); + self.$captionContainer = $h.getElement(options, 'elCaptionContainer', $cont.find('.file-caption')); + self.$caption = $h.getElement(options, 'elCaptionText', $cont.find('.file-caption-name')); + if (!$h.isEmpty(self.msgPlaceholder)) { + f = $el.attr('multiple') ? self.filePlural : self.fileSingle; + self.$caption.attr('placeholder', self.msgPlaceholder.replace('{files}', f)); + } + self.$captionIcon = self.$captionContainer.find('.file-caption-icon'); + self.$previewContainer = $h.getElement(options, 'elPreviewContainer', $cont.find('.file-preview')); + self.$preview = $h.getElement(options, 'elPreviewImage', $cont.find('.file-preview-thumbnails')); + self.$previewStatus = $h.getElement(options, 'elPreviewStatus', $cont.find('.file-preview-status')); + self.$errorContainer = $h.getElement(options, 'elErrorContainer', self.$previewContainer.find('.kv-fileinput-error')); + self._validateDisabled(); + if (!$h.isEmpty(self.msgErrorClass)) { + $h.addCss(self.$errorContainer, self.msgErrorClass); + } + if (!refreshMode) { + self.$errorContainer.hide(); + self.previewInitId = "preview-" + $h.uniqId(); + self._initPreviewCache(); + self._initPreview(true); + self._initPreviewActions(); + if (self.$parent.hasClass('file-loading')) { + self.$container.insertBefore(self.$parent); + self.$parent.remove(); + } + } else { + if (!self._errorsExist()) { + self.$errorContainer.hide(); + } } - self.$errorContainer.hide(); - self.fileActionSettings = $.extend(true, defaultFileActionSettings, options.fileActionSettings); - self.previewInitId = "preview-" + uniqId(); - self.id = self.$element.attr('id'); - previewCache.init(self); - self._initPreview(true); - self._initPreviewActions(); - self.options = options; self._setFileDropZoneTitle(); - self.$element.removeClass('file-loading'); - if (self.$element.attr('disabled')) { + if ($el.attr('disabled')) { self.disable(); } self._initZoom(); + if (self.hideThumbnailContent) { + $h.addCss(self.$preview, 'hide-content'); + } + }, + _initTemplateDefaults: function () { + var self = this, tMain1, tMain2, tPreview, tFileIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse, + tModalMain, tModal, tProgress, tSize, tFooter, tActions, tActionDelete, tActionUpload, tActionDownload, + tActionZoom, tActionDrag, tIndicator, tTagBef, tTagBef1, tTagBef2, tTagAft, tGeneric, tHtml, tImage, + tText, tOffice, tGdocs, tVideo, tAudio, tFlash, tObject, tPdf, tOther, tStyle, tZoomCache, vDefaultDim; + tMain1 = '{preview}\n' + + '
\n' + + '
\n' + + ' {caption}\n' + + '
\n' + + ' {remove}\n' + + ' {cancel}\n' + + ' {upload}\n' + + ' {browse}\n' + + '
\n' + + '
'; + tMain2 = '{preview}\n
\n
\n{remove}\n{cancel}\n{upload}\n{browse}\n'; + tPreview = '
\n' + + ' {close}' + + '
\n' + + '
\n' + + '
\n' + + '
' + + '
\n' + + '
\n' + + '
\n' + + '
'; + tClose = $h.closeButton('fileinput-remove'); + tFileIcon = ''; + // noinspection HtmlUnknownAttribute + tCaption = '
\n' + + ' \n' + + ' \n' + + '
'; + //noinspection HtmlUnknownAttribute + tBtnDefault = ''; + //noinspection HtmlUnknownAttribute + tBtnLink = '{icon} {label}'; + //noinspection HtmlUnknownAttribute + tBtnBrowse = '
{icon} {label}
'; + tModalMain = ''; + tModal = '\n'; + tProgress = '
\n' + + '
\n' + + ' {status}\n' + + '
\n' + + '
'; + tSize = ' ({sizeText})'; + tFooter = ''; + tActions = '
\n' + + ' \n' + + '
\n' + + '{drag}\n' + + '
'; + //noinspection HtmlUnknownAttribute + tActionDelete = '\n'; + tActionUpload = ''; + tActionDownload = '{downloadIcon}'; + tActionZoom = ''; + tActionDrag = '{dragIcon}'; + tIndicator = '
{indicator}
'; + tTagBef = '
\n'; + tTagBef2 = tTagBef + ' title="{caption}">
\n'; + tTagAft = '
{footer}\n
\n'; + tGeneric = '{content}\n'; + tStyle = ' {style}'; + tHtml = '
{data}
\n'; + tImage = '\n'; + tText = '\n'; + tOffice = ''; + tGdocs = ''; + tVideo = '\n'; + tAudio = '\n'; + tFlash = '\n'; + tPdf = '\n'; + tObject = '\n' + '\n' + + $h.OBJECT_PARAMS + ' ' + $h.DEFAULT_PREVIEW + '\n\n'; + tOther = '
\n' + $h.DEFAULT_PREVIEW + '\n
\n'; + tZoomCache = ''; + vDefaultDim = {width: "100%", height: "100%", 'min-height': "480px"}; + self.defaults = { + layoutTemplates: { + main1: tMain1, + main2: tMain2, + preview: tPreview, + close: tClose, + fileIcon: tFileIcon, + caption: tCaption, + modalMain: tModalMain, + modal: tModal, + progress: tProgress, + size: tSize, + footer: tFooter, + indicator: tIndicator, + actions: tActions, + actionDelete: tActionDelete, + actionUpload: tActionUpload, + actionDownload: tActionDownload, + actionZoom: tActionZoom, + actionDrag: tActionDrag, + btnDefault: tBtnDefault, + btnLink: tBtnLink, + btnBrowse: tBtnBrowse, + zoomCache: tZoomCache + }, + previewMarkupTags: { + tagBefore1: tTagBef1, + tagBefore2: tTagBef2, + tagAfter: tTagAft + }, + previewContentTemplates: { + generic: tGeneric, + html: tHtml, + image: tImage, + text: tText, + office: tOffice, + gdocs: tGdocs, + video: tVideo, + audio: tAudio, + flash: tFlash, + object: tObject, + pdf: tPdf, + other: tOther + }, + allowedPreviewTypes: ['image', 'html', 'text', 'video', 'audio', 'flash', 'pdf', 'object'], + previewTemplates: {}, + previewSettings: { + image: {width: "auto", height: "auto", 'max-width': "100%", 'max-height': "100%"}, + html: {width: "213px", height: "160px"}, + text: {width: "213px", height: "160px"}, + office: {width: "213px", height: "160px"}, + gdocs: {width: "213px", height: "160px"}, + video: {width: "213px", height: "160px"}, + audio: {width: "100%", height: "30px"}, + flash: {width: "213px", height: "160px"}, + object: {width: "213px", height: "160px"}, + pdf: {width: "213px", height: "160px"}, + other: {width: "213px", height: "160px"} + }, + previewSettingsSmall: { + image: {width: "auto", height: "auto", 'max-width': "100%", 'max-height': "100%"}, + html: {width: "100%", height: "160px"}, + text: {width: "100%", height: "160px"}, + office: {width: "100%", height: "160px"}, + gdocs: {width: "100%", height: "160px"}, + video: {width: "100%", height: "auto"}, + audio: {width: "100%", height: "30px"}, + flash: {width: "100%", height: "auto"}, + object: {width: "100%", height: "auto"}, + pdf: {width: "100%", height: "160px"}, + other: {width: "100%", height: "160px"} + }, + previewZoomSettings: { + image: {width: "auto", height: "auto", 'max-width': "100%", 'max-height': "100%"}, + html: vDefaultDim, + text: vDefaultDim, + office: {width: "100%", height: "100%", 'max-width': "100%", 'min-height': "480px"}, + gdocs: {width: "100%", height: "100%", 'max-width': "100%", 'min-height': "480px"}, + video: {width: "auto", height: "100%", 'max-width': "100%"}, + audio: {width: "100%", height: "30px"}, + flash: {width: "auto", height: "480px"}, + object: {width: "auto", height: "100%", 'max-width': "100%", 'min-height': "480px"}, + pdf: vDefaultDim, + other: {width: "auto", height: "100%", 'min-height': "480px"} + }, + fileTypeSettings: { + image: function (vType, vName) { + return ($h.compare(vType, 'image.*') && !$h.compare(vType, /(tiff?|wmf)$/i) || + $h.compare(vName, /\.(gif|png|jpe?g)$/i)); + }, + html: function (vType, vName) { + return $h.compare(vType, 'text/html') || $h.compare(vName, /\.(htm|html)$/i); + }, + office: function (vType, vName) { + return $h.compare(vType, /(word|excel|powerpoint|office)$/i) || + $h.compare(vName, /\.(docx?|xlsx?|pptx?|pps|potx?)$/i); + }, + gdocs: function (vType, vName) { + return $h.compare(vType, /(word|excel|powerpoint|office|iwork-pages|tiff?)$/i) || + $h.compare(vName, /\.(docx?|xlsx?|pptx?|pps|potx?|rtf|ods|odt|pages|ai|dxf|ttf|tiff?|wmf|e?ps)$/i); + }, + text: function (vType, vName) { + return $h.compare(vType, 'text.*') || $h.compare(vName, /\.(xml|javascript)$/i) || + $h.compare(vName, /\.(txt|md|csv|nfo|ini|json|php|js|css)$/i); + }, + video: function (vType, vName) { + return $h.compare(vType, 'video.*') && ($h.compare(vType, /(ogg|mp4|mp?g|mov|webm|3gp)$/i) || + $h.compare(vName, /\.(og?|mp4|webm|mp?g|mov|3gp)$/i)); + }, + audio: function (vType, vName) { + return $h.compare(vType, 'audio.*') && ($h.compare(vName, /(ogg|mp3|mp?g|wav)$/i) || + $h.compare(vName, /\.(og?|mp3|mp?g|wav)$/i)); + }, + flash: function (vType, vName) { + return $h.compare(vType, 'application/x-shockwave-flash', true) || $h.compare(vName, /\.(swf)$/i); + }, + pdf: function (vType, vName) { + return $h.compare(vType, 'application/pdf', true) || $h.compare(vName, /\.(pdf)$/i); + }, + object: function () { + return true; + }, + other: function () { + return true; + } + }, + fileActionSettings: { + showRemove: true, + showUpload: true, + showDownload: true, + showZoom: true, + showDrag: true, + removeIcon: '', + removeClass: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + removeErrorClass: 'btn btn-sm btn-kv btn-danger', + removeTitle: 'Remove file', + uploadIcon: '', + uploadClass: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + uploadTitle: 'Upload file', + uploadRetryIcon: '', + uploadRetryTitle: 'Retry upload', + downloadIcon: '', + downloadClass: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + downloadTitle: 'Download file', + zoomIcon: '', + zoomClass: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + zoomTitle: 'View Details', + dragIcon: '', + dragClass: 'text-info', + dragTitle: 'Move / Rearrange', + dragSettings: {}, + indicatorNew: '', + indicatorSuccess: '', + indicatorError: '', + indicatorLoading: '', + indicatorNewTitle: 'Not uploaded yet', + indicatorSuccessTitle: 'Uploaded', + indicatorErrorTitle: 'Upload Error', + indicatorLoadingTitle: 'Uploading ...' + } + }; + $.each(self.defaults, function (key, setting) { + if (key === 'allowedPreviewTypes') { + if (self.allowedPreviewTypes === undefined) { + self.allowedPreviewTypes = setting; + } + return; + } + self[key] = $.extend(true, {}, setting, self[key]); + }); + self._initPreviewTemplates(); + }, + _initPreviewTemplates: function () { + var self = this, cfg = self.defaults, tags = self.previewMarkupTags, tagBef, tagAft = tags.tagAfter; + $.each(cfg.previewContentTemplates, function (key, value) { + if ($h.isEmpty(self.previewTemplates[key])) { + tagBef = tags.tagBefore2; + if (key === 'generic' || key === 'image' || key === 'html' || key === 'text') { + tagBef = tags.tagBefore1; + } + self.previewTemplates[key] = tagBef + value + tagAft; + } + }); + }, + _initPreviewCache: function () { + var self = this; + self.previewCache = { + data: {}, + init: function () { + var content = self.initialPreview; + if (content.length > 0 && !$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + self.previewCache.data = { + content: content, + config: self.initialPreviewConfig, + tags: self.initialPreviewThumbTags + }; + }, + count: function () { + return !!self.previewCache.data && !!self.previewCache.data.content ? + self.previewCache.data.content.length : 0; + }, + get: function (i, isDisabled) { + var ind = 'init_' + i, data = self.previewCache.data, config = data.config[i], + content = data.content[i], previewId = self.previewInitId + '-' + ind, out, $tmp, cat, ftr, + fname, ftype, frameClass, asData = $h.ifSet('previewAsData', config, self.initialPreviewAsData), + parseTemplate = function (cat, dat, fn, ft, id, ftr, ind, fc, t) { + fc = ' file-preview-initial ' + $h.SORT_CSS + (fc ? ' ' + fc : ''); + return self._generatePreviewTemplate(cat, dat, fn, ft, id, false, null, fc, ftr, ind, t); + }; + if (!content) { + return ''; + } + isDisabled = isDisabled === undefined ? true : isDisabled; + cat = $h.ifSet('type', config, self.initialPreviewFileType || 'generic'); + fname = $h.ifSet('filename', config, $h.ifSet('caption', config)); + ftype = $h.ifSet('filetype', config, cat); + ftr = self.previewCache.footer(i, isDisabled, (config && config.size || null)); + frameClass = $h.ifSet('frameClass', config); + if (asData) { + out = parseTemplate(cat, content, fname, ftype, previewId, ftr, ind, frameClass); + } else { + out = parseTemplate('generic', content, fname, ftype, previewId, ftr, ind, frameClass, cat) + .setTokens({'content': data.content[i]}); + } + if (data.tags.length && data.tags[i]) { + out = $h.replaceTags(out, data.tags[i]); + } + /** @namespace config.frameAttr */ + if (!$h.isEmpty(config) && !$h.isEmpty(config.frameAttr)) { + $tmp = $(document.createElement('div')).html(out); + $tmp.find('.file-preview-initial').attr(config.frameAttr); + out = $tmp.html(); + $tmp.remove(); + } + return out; + }, + add: function (content, config, tags, append) { + var data = self.previewCache.data, index; + if (!$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + if (append) { + index = data.content.push(content) - 1; + data.config[index] = config; + data.tags[index] = tags; + } else { + index = content.length - 1; + data.content = content; + data.config = config; + data.tags = tags; + } + self.previewCache.data = data; + return index; + }, + set: function (content, config, tags, append) { + var data = self.previewCache.data, i, chk; + if (!content || !content.length) { + return; + } + if (!$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + chk = content.filter(function (n) { + return n !== null; + }); + if (!chk.length) { + return; + } + if (data.content === undefined) { + data.content = []; + } + if (data.config === undefined) { + data.config = []; + } + if (data.tags === undefined) { + data.tags = []; + } + if (append) { + for (i = 0; i < content.length; i++) { + if (content[i]) { + data.content.push(content[i]); + } + } + for (i = 0; i < config.length; i++) { + if (config[i]) { + data.config.push(config[i]); + } + } + for (i = 0; i < tags.length; i++) { + if (tags[i]) { + data.tags.push(tags[i]); + } + } + } else { + data.content = content; + data.config = config; + data.tags = tags; + } + self.previewCache.data = data; + }, + unset: function (index) { + var chk = self.previewCache.count(), rev = self.reversePreviewOrder; + if (!chk) { + return; + } + if (chk === 1) { + self.previewCache.data.content = []; + self.previewCache.data.config = []; + self.previewCache.data.tags = []; + self.initialPreview = []; + self.initialPreviewConfig = []; + self.initialPreviewThumbTags = []; + return; + } + self.previewCache.data.content = $h.spliceArray(self.previewCache.data.content, index, rev); + self.previewCache.data.config = $h.spliceArray(self.previewCache.data.config, index, rev); + self.previewCache.data.tags = $h.spliceArray(self.previewCache.data.tags, index, rev); + }, + out: function () { + var html = '', caption, len = self.previewCache.count(), i, content; + if (len === 0) { + return {content: '', caption: ''}; + } + for (i = 0; i < len; i++) { + content = self.previewCache.get(i); + html = self.reversePreviewOrder ? (content + html) : (html + content); + } + caption = self._getMsgSelected(len); + return {content: html, caption: caption}; + }, + footer: function (i, isDisabled, size) { + var data = self.previewCache.data || {}; + if ($h.isEmpty(data.content)) { + return ''; + } + if ($h.isEmpty(data.config) || $h.isEmpty(data.config[i])) { + data.config[i] = {}; + } + isDisabled = isDisabled === undefined ? true : isDisabled; + var config = data.config[i], caption = $h.ifSet('caption', config), a, + width = $h.ifSet('width', config, 'auto'), url = $h.ifSet('url', config, false), + key = $h.ifSet('key', config, null), fs = self.fileActionSettings, + initPreviewShowDel = self.initialPreviewShowDelete || false, + dUrl = config.downloadUrl || self.initialPreviewDownloadUrl || '', + dFil = config.filename || config.caption || '', + initPreviewShowDwl = !!(dUrl), + sDel = $h.ifSet('showRemove', config, $h.ifSet('showRemove', fs, initPreviewShowDel)), + sDwl = $h.ifSet('showDownload', config, $h.ifSet('showDownload', fs, initPreviewShowDwl)), + sZm = $h.ifSet('showZoom', config, $h.ifSet('showZoom', fs, true)), + sDrg = $h.ifSet('showDrag', config, $h.ifSet('showDrag', fs, true)), + dis = (url === false) && isDisabled; + sDwl = sDwl && config.downloadUrl !== false && !!dUrl; + a = self._renderFileActions(false, sDwl, sDel, sZm, sDrg, dis, url, key, true, dUrl, dFil); + return self._getLayoutTemplate('footer').setTokens({ + 'progress': self._renderThumbProgress(), + 'actions': a, + 'caption': caption, + 'size': self._getSize(size), + 'width': width, + 'indicator': '' + }); + } + }; + self.previewCache.init(); + }, + _handler: function ($el, event, callback) { + var self = this, ns = self.namespace, ev = event.split(' ').join(ns + ' ') + ns; + if (!$el || !$el.length) { + return; + } + $el.off(ev).on(ev, callback); + }, + _log: function (msg) { + var self = this, id = self.$element.attr('id'); + if (id) { + msg = '"' + id + '": ' + msg; + } + if (typeof window.console.log !== "undefined") { + window.console.log(msg); + } else { + window.alert(msg); + } }, _validate: function () { - var self = this, $exception; - if (self.$element.attr('type') === 'file') { - return true; + var self = this, status = self.$element.attr('type') === 'file'; + if (!status) { + self._log('The input "type" must be set to "file" for initializing the "bootstrap-fileinput" plugin.'); } - $exception = '
' + - '

Invalid Input Type

' + - 'You must set an input type = file for bootstrap-fileinput plugin to initialize.' + - '
'; - self.$element.after($exception); - return false; + return status; }, _errorsExist: function () { - var self = this, $err; - if (self.$errorContainer.find('li').length) { + var self = this, $err, $errList = self.$errorContainer.find('li'); + if ($errList.length) { return true; } $err = $(document.createElement('div')).html(self.$errorContainer.html()); - $err.find('span.kv-error-close').remove(); + $err.find('.kv-error-close').remove(); $err.find('ul').remove(); - return $.trim($err.text()).length ? true : false; + return !!$.trim($err.text()).length; }, _errorHandler: function (evt, caption) { - var self = this, err = evt.target.error; + var self = this, err = evt.target.error, showError = function (msg) { + self._showError(msg.replace('{name}', caption)); + }; /** @namespace err.NOT_FOUND_ERR */ /** @namespace err.SECURITY_ERR */ /** @namespace err.NOT_READABLE_ERR */ if (err.code === err.NOT_FOUND_ERR) { - self._showError(self.msgFileNotFound.replace('{name}', caption)); + showError(self.msgFileNotFound); } else if (err.code === err.SECURITY_ERR) { - self._showError(self.msgFileSecured.replace('{name}', caption)); + showError(self.msgFileSecured); } else if (err.code === err.NOT_READABLE_ERR) { - self._showError(self.msgFileNotReadable.replace('{name}', caption)); + showError(self.msgFileNotReadable); } else if (err.code === err.ABORT_ERR) { - self._showError(self.msgFilePreviewAborted.replace('{name}', caption)); + showError(self.msgFilePreviewAborted); } else { - self._showError(self.msgFilePreviewError.replace('{name}', caption)); + showError(self.msgFilePreviewError); } }, _addError: function (msg) { var self = this, $error = self.$errorContainer; if (msg && $error.length) { $error.html(self.errorCloseButton + msg); - handler($error.find('.kv-error-close'), 'click', function () { - $error.fadeOut('slow'); + self._handler($error.find('.kv-error-close'), 'click', function () { + setTimeout(function () { + if (self.showPreview && !self.getFrames().length) { + self.clear(); + } + $error.fadeOut('slow'); + }, 10); }); } }, + _setValidationError: function (css) { + var self = this; + css = (css ? css + ' ' : '') + 'has-error'; + self.$container.removeClass(css).addClass('has-error'); + $h.addCss(self.$captionContainer, 'is-invalid'); + }, _resetErrors: function (fade) { var self = this, $error = self.$errorContainer; self.isError = false; self.$container.removeClass('has-error'); + self.$captionContainer.removeClass('is-invalid'); $error.html(''); if (fade) { $error.fadeOut('slow'); @@ -765,15 +1219,18 @@ if (!folders) { return; } - msg = self.msgFoldersNotAllowed.replace(/\{n}/g, folders); + if (!self.isAjaxUpload) { + self._clearFileInput(); + } + msg = self.msgFoldersNotAllowed.replace('{n}', folders); self._addError(msg); - addCss(self.$container, 'has-error'); + self._setValidationError(); $error.fadeIn(800); self._raise('filefoldererror', [folders, msg]); }, _showUploadError: function (msg, params, event) { var self = this, $error = self.$errorContainer, ev = event || 'fileuploaderror', e = params && params.id ? - '
  • ' + msg + '
  • ' : '
  • ' + msg + '
  • '; + '
  • ' + msg + '
  • ' : '
  • ' + msg + '
  • '; if ($error.find('ul').length === 0) { self._addError('
      ' + e + '
    '); } else { @@ -781,8 +1238,7 @@ } $error.fadeIn(800); self._raise(ev, [params, msg]); - self.$container.removeClass('file-input-new'); - addCss(self.$container, 'has-error'); + self._setValidationError('file-input-new'); return true; }, _showError: function (msg, params, event) { @@ -792,11 +1248,10 @@ self._addError(msg); $error.fadeIn(800); self._raise(ev, [params, msg]); - if (!self.isUploadable) { + if (!self.isAjaxUpload) { self._clearFileInput(); } - self.$container.removeClass('file-input-new'); - addCss(self.$container, 'has-error'); + self._setValidationError('file-input-new'); self.$btnUpload.attr('disabled', true); return true; }, @@ -810,12 +1265,11 @@ $error.fadeIn(800); self._raise('fileerror', [params, msg]); self._clearFileInput(); - addCss(self.$container, 'has-error'); + self._setValidationError(); }, - _parseError: function (jqXHR, errorThrown, fileName) { + _parseError: function (operation, jqXHR, errorThrown, fileName) { /** @namespace jqXHR.responseJSON */ - var self = this, errMsg = $.trim(errorThrown + ''), - dot = errMsg.slice(-1) === '.' ? '' : '.', + var self = this, errMsg = $.trim(errorThrown + ''), textPre, text = jqXHR.responseJSON !== undefined && jqXHR.responseJSON.error !== undefined ? jqXHR.responseJSON.error : jqXHR.responseText; if (self.cancelling && self.msgUploadAborted) { @@ -823,48 +1277,55 @@ } if (self.showAjaxErrorDetails && text) { text = $.trim(text.replace(/\n\s*\n/g, '\n')); - text = text.length > 0 ? '
    ' + text + '
    ' : ''; - errMsg += dot + text; - } else { - errMsg += dot; + textPre = text.length ? '
    ' + text + '
    ' : ''; + errMsg += errMsg ? textPre : text; + } + if (!errMsg) { + errMsg = self.msgAjaxError.replace('{operation}', operation); } self.cancelling = false; return fileName ? '' + fileName + ': ' + errMsg : errMsg; }, - _parseFileType: function (file) { - var self = this, isValid, vType, cat, i; - for (i = 0; i < defaultPreviewTypes.length; i += 1) { - cat = defaultPreviewTypes[i]; - isValid = ifSet(cat, self.fileTypeSettings, defaultFileTypeSettings[cat]); - vType = isValid(file.type, file.name) ? cat : ''; - if (!isEmpty(vType)) { + _parseFileType: function (type, name) { + var self = this, isValid, vType, cat, i, types = self.allowedPreviewTypes || []; + if (type === 'application/text-plain') { + return 'text'; + } + for (i = 0; i < types.length; i++) { + cat = types[i]; + isValid = self.fileTypeSettings[cat]; + vType = isValid(type, name) ? cat : ''; + if (!$h.isEmpty(vType)) { return vType; } } return 'other'; }, - _parseFilePreviewIcon: function (content, fname) { - var self = this, proceed, ext, icn = self.previewFileIcon; + _getPreviewIcon: function (fname) { + var self = this, ext, out = null; if (fname && fname.indexOf('.') > -1) { ext = fname.split('.').pop(); - if (self.previewFileIconSettings && self.previewFileIconSettings[ext]) { - icn = self.previewFileIconSettings[ext]; + if (self.previewFileIconSettings) { + out = self.previewFileIconSettings[ext] || self.previewFileIconSettings[ext.toLowerCase()] || null; } if (self.previewFileExtSettings) { $.each(self.previewFileExtSettings, function (key, func) { if (self.previewFileIconSettings[key] && func(ext)) { - icn = self.previewFileIconSettings[key]; + out = self.previewFileIconSettings[key]; + //noinspection UnnecessaryReturnStatementJS return; } - proceed = true; }); } } - if (content.indexOf('{previewFileIcon}') > -1) { - return content.replace(/\{previewFileIconClass}/g, self.previewFileIconClass).replace( - /\{previewFileIcon}/g, icn); + return out; + }, + _parseFilePreviewIcon: function (content, fname) { + var self = this, icn = self._getPreviewIcon(fname) || self.previewFileIcon, out = content; + if (out.indexOf('{previewFileIcon}') > -1) { + out = out.setTokens({'previewFileIconClass': self.previewFileIconClass, 'previewFileIcon': icn}); } - return content; + return out; }, _raise: function (event, params) { var self = this, e = $.Event(event); @@ -873,12 +1334,9 @@ } else { self.$element.trigger(e); } - if (e.isDefaultPrevented()) { + if (e.isDefaultPrevented() || e.result === false) { return false; } - if (!e.result) { - return e.result; - } switch (event) { // ignore these events case 'filebatchuploadcomplete': @@ -895,9 +1353,11 @@ case 'filecustomerror': case 'filesuccessremove': break; - // receive data response via `filecustomerror` event` + // receive data response via `filecustomerror` event` default: - self.ajaxAborted = e.result; + if (!self.ajaxAborted) { + self.ajaxAborted = e.result; + } break; } return true; @@ -930,52 +1390,85 @@ } }, _listen: function () { - var self = this, $el = self.$element, $form = $el.closest('form'), $cont = self.$container; - handler($el, 'change', $.proxy(self._change, self)); - if (self.showBrowse) { - handler(self.$btnFile, 'click', $.proxy(self._browse, self)); - } - handler($form, 'reset', $.proxy(self.reset, self)); - handler($cont.find('.fileinput-remove:not([disabled])'), 'click', $.proxy(self.clear, self)); - handler($cont.find('.fileinput-cancel'), 'click', $.proxy(self.cancel, self)); - self._initDragDrop(); - if (!self.isUploadable) { - handler($form, 'submit', $.proxy(self._submitForm, self)); - } - handler(self.$container.find('.fileinput-upload'), 'click', $.proxy(self._uploadClick, self)); - handler($(window), 'resize', function () { - self._listenFullScreen(screen.width === window.innerWidth && screen.height === window.innerHeight); - }); - handler($(document), 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', - function () { - self._listenFullScreen(checkFullScreen()); - }); - self._initClickable(); - }, - _initClickable: function () { - var self = this, $zone; - if (!self.isClickable) { - return; - } - $zone = self.isUploadable ? self.$dropZone : self.$preview.find('.file-default-preview'); - addCss($zone, 'clickable'); - $zone.attr('tabindex', -1); - handler($zone, 'click', function (e) { - var $target = $(e.target); - if (!$target.parents('.file-preview-thumbnails').length || $target.parents( - '.file-default-preview').length) { - self.$element.trigger('click'); - $zone.blur(); + var self = this, $el = self.$element, $form = self.$form, $cont = self.$container, fullScreenEvents; + self._handler($el, 'click', function(e) { + if ($el.hasClass('file-no-browse')) { + if ($el.data('zoneClicked')) { + $el.data('zoneClicked', false); + } else { + e.preventDefault(); + } } }); + self._handler($el, 'change', $.proxy(self._change, self)); + if (self.showBrowse) { + self._handler(self.$btnFile, 'click', $.proxy(self._browse, self)); + } + self._handler($cont.find('.fileinput-remove:not([disabled])'), 'click', $.proxy(self.clear, self)); + self._handler($cont.find('.fileinput-cancel'), 'click', $.proxy(self.cancel, self)); + self._initDragDrop(); + self._handler($form, 'reset', $.proxy(self.clear, self)); + if (!self.isAjaxUpload) { + self._handler($form, 'submit', $.proxy(self._submitForm, self)); + } + self._handler(self.$container.find('.fileinput-upload'), 'click', $.proxy(self._uploadClick, self)); + self._handler($(window), 'resize', function () { + self._listenFullScreen(screen.width === window.innerWidth && screen.height === window.innerHeight); + }); + fullScreenEvents = 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'; + self._handler($(document), fullScreenEvents, function () { + self._listenFullScreen($h.checkFullScreen()); + }); + self._autoFitContent(); + self._initClickable(); + self._refreshPreview(); + }, + _autoFitContent: function () { + var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + self = this, config = width < 400 ? (self.previewSettingsSmall || self.defaults.previewSettingsSmall) : + (self.previewSettings || self.defaults.previewSettings), sel; + $.each(config, function (cat, settings) { + sel = '.file-preview-frame .file-preview-' + cat; + self.$preview.find(sel + '.kv-preview-data,' + sel + ' .kv-preview-data').css(settings); + }); + }, + _scanDroppedItems: function (item, files, path) { + path = path || ""; + var self = this, i, dirReader, readDir, errorHandler = function (e) { + self._log('Error scanning dropped files!'); + self._log(e); + }; + if (item.isFile) { + item.file(function (file) { + files.push(file); + }, errorHandler); + } else { + if (item.isDirectory) { + dirReader = item.createReader(); + readDir = function () { + dirReader.readEntries(function (entries) { + if (entries && entries.length > 0) { + for (i = 0; i < entries.length; i++) { + self._scanDroppedItems(entries[i], files, path + item.name + "/"); + } + // recursively call readDir() again, since browser can only handle first 100 entries. + readDir(); + } + return null; + }, errorHandler); + }; + readDir(); + } + } + }, _initDragDrop: function () { var self = this, $zone = self.$dropZone; - if (self.isUploadable && self.dropZoneEnabled && self.showPreview) { - handler($zone, 'dragenter dragover', $.proxy(self._zoneDragEnter, self)); - handler($zone, 'dragleave', $.proxy(self._zoneDragLeave, self)); - handler($zone, 'drop', $.proxy(self._zoneDrop, self)); - handler($(document), 'dragenter dragover drop', self._zoneDragDropInit); + if (self.dropZoneEnabled && self.showPreview) { + self._handler($zone, 'dragenter dragover', $.proxy(self._zoneDragEnter, self)); + self._handler($zone, 'dragleave', $.proxy(self._zoneDragLeave, self)); + self._handler($zone, 'drop', $.proxy(self._zoneDrop, self)); + self._handler($(document), 'dragenter dragover drop', self._zoneDragDropInit); } }, _zoneDragDropInit: function (e) { @@ -990,7 +1483,7 @@ e.originalEvent.dataTransfer.dropEffect = 'none'; return; } - addCss(self.$dropZone, 'file-highlighted'); + $h.addCss(self.$dropZone, 'file-highlighted'); }, _zoneDragLeave: function (e) { var self = this; @@ -1001,22 +1494,52 @@ self.$dropZone.removeClass('file-highlighted'); }, _zoneDrop: function (e) { - var self = this; - e.preventDefault(); /** @namespace e.originalEvent.dataTransfer */ - if (self.isDisabled || isEmpty(e.originalEvent.dataTransfer.files)) { + var self = this, i, $el = self.$element, dataTransfer = e.originalEvent.dataTransfer, + files = dataTransfer.files, items = dataTransfer.items, folders = $h.getDragDropFolders(items), + processFiles = function () { + if (!self.isAjaxUpload) { + self.changeTriggered = true; + $el.get(0).files = files; + setTimeout(function () { + self.changeTriggered = false; + $el.trigger('change' + self.namespace); + }, 10); + } else { + self._change(e, files); + } + self.$dropZone.removeClass('file-highlighted'); + }; + e.preventDefault(); + if (self.isDisabled || $h.isEmpty(files)) { return; } - self._change(e, 'dragdrop'); - self.$dropZone.removeClass('file-highlighted'); + if (folders > 0) { + if (!self.isAjaxUpload) { + self._showFolderError(folders); + return; + } + files = []; + for (i = 0; i < items.length; i++) { + var item = items[i].webkitGetAsEntry(); + if (item) { + self._scanDroppedItems(item, files); + } + } + setTimeout(function () { + processFiles(); + }, 500); + } else { + processFiles(); + } }, _uploadClick: function (e) { var self = this, $btn = self.$container.find('.fileinput-upload'), $form, - isEnabled = !$btn.hasClass('disabled') && isEmpty($btn.attr('disabled')); + isEnabled = !$btn.hasClass('disabled') && $h.isEmpty($btn.attr('disabled')); if (e && e.isDefaultPrevented()) { return; } - if (!self.isUploadable) { + if (!self.isAjaxUpload) { if (isEnabled && $btn.attr('type') !== 'submit') { $form = $btn.closest('form'); // downgrade to normal form submit if possible @@ -1033,47 +1556,43 @@ } }, _submitForm: function () { - var self = this, $el = self.$element, files = $el.get(0).files; - if (files && self.minFileCount > 0 && self._getFileCount(files.length) < self.minFileCount) { - self._noFilesError({}); - return false; - } - return !self._abort({}); + var self = this; + return self._isFileSelectionValid() && !self._abort({}); }, _clearPreview: function () { - var self = this, $thumbs = !self.showUploadedThumbs ? self.$preview.find('.file-preview-frame') : - self.$preview.find('.file-preview-frame:not(.file-preview-success)'); - $thumbs.remove(); - if (!self.$preview.find('.file-preview-frame').length || !self.showPreview) { + var self = this, $p = self.$preview, + $thumbs = self.showUploadedThumbs ? self.getFrames(':not(.file-preview-success)') : self.getFrames(); + $thumbs.each(function () { + var $thumb = $(this); + $thumb.remove(); + $h.cleanZoomCache($p.find('#zoom-' + $thumb.attr('id'))); + }); + if (!self.getFrames().length || !self.showPreview) { self._resetUpload(); } self._validateDefaultPreview(); }, _initSortable: function () { - var self = this, $preview = self.$preview, $el, settings; - if (!window.KvSortable) { + var self = this, $el = self.$preview, settings, selector = '.' + $h.SORT_CSS, + rev = self.reversePreviewOrder; + if (!window.KvSortable || $el.find(selector).length === 0) { return; } - $el = $preview.find('.file-initial-thumbs'); //noinspection JSUnusedGlobalSymbols settings = { handle: '.drag-handle-init', dataIdAttr: 'data-preview-id', - draggable: '.file-preview-initial', + scroll: false, + draggable: selector, onSort: function (e) { - var oldIndex = e.oldIndex, newIndex = e.newIndex; - self.initialPreview = moveArray(self.initialPreview, oldIndex, newIndex); - self.initialPreviewConfig = moveArray(self.initialPreviewConfig, oldIndex, newIndex); - previewCache.init(self); - for (var i = 0; i < self.initialPreviewConfig.length; i++) { - if (self.initialPreviewConfig[i] !== null) { - var key = self.initialPreviewConfig[i].key; - var $frame = $(".kv-file-remove[data-key='" + key + "']"); - $frame = $frame.closest('.file-preview-frame'); - $frame.attr('data-fileindex', 'init_' + i); - $frame.data('fileindex', 'init_' + i); - } - } + var oldIndex = e.oldIndex, newIndex = e.newIndex, i = 0; + self.initialPreview = $h.moveArray(self.initialPreview, oldIndex, newIndex, rev); + self.initialPreviewConfig = $h.moveArray(self.initialPreviewConfig, oldIndex, newIndex, rev); + self.previewCache.init(); + self.getFrames('.file-preview-initial').each(function () { + $(this).attr('data-fileindex', 'init_' + i); + i++; + }); self._raise('filesorted', { previewId: $(e.item).attr('id'), 'oldIndex': oldIndex, @@ -1088,9 +1607,14 @@ $.extend(true, settings, self.fileActionSettings.dragSettings); $el.kvsortable(settings); }, + _setPreviewContent: function (content) { + var self = this; + self.$preview.html(content); + self._autoFitContent(); + }, _initPreview: function (isInit) { var self = this, cap = self.initialCaption || '', out; - if (!previewCache.count(self.id)) { + if (!self.previewCache.count()) { self._clearPreview(); if (isInit) { self._setCaption(cap); @@ -1099,12 +1623,13 @@ } return; } - out = previewCache.out(self.id); + out = self.previewCache.out(); cap = isInit && self.initialCaption ? self.initialCaption : out.caption; - self.$preview.html(out.content); + self._setPreviewContent(out.content); + self._setInitThumbAttr(); self._setCaption(cap); self._initSortable(); - if (!isEmpty(out.content)) { + if (!$h.isEmpty(out.content)) { self.$container.removeClass('file-input-new'); } }, @@ -1119,14 +1644,17 @@ }, _getModalContent: function () { var self = this; - return self._getLayoutTemplate('modal') - .replace(/\{heading}/g, self.msgZoomModalHeading) - .replace(/\{prev}/g, self._getZoomButton('prev')) - .replace(/\{next}/g, self._getZoomButton('next')) - .replace(/\{toggleheader}/g, self._getZoomButton('toggleheader')) - .replace(/\{fullscreen}/g, self._getZoomButton('fullscreen')) - .replace(/\{borderless}/g, self._getZoomButton('borderless')) - .replace(/\{close}/g, self._getZoomButton('close')); + return self._getLayoutTemplate('modal').setTokens({ + 'rtl': self.rtl ? ' kv-rtl' : '', + 'zoomFrameClass': self.frameClass, + 'heading': self.msgZoomModalHeading, + 'prev': self._getZoomButton('prev'), + 'next': self._getZoomButton('next'), + 'toggleheader': self._getZoomButton('toggleheader'), + 'fullscreen': self._getZoomButton('fullscreen'), + 'borderless': self._getZoomButton('borderless'), + 'close': self._getZoomButton('close') + }); }, _listenModalEvent: function (event) { var self = this, $modal = self.$modal, getParams = function (e) { @@ -1144,7 +1672,7 @@ $btnFull.removeClass('active').attr('aria-pressed', 'false'); if ($modal.hasClass('file-zoom-fullscreen')) { self._maximizeZoomDialog(); - if (checkFullScreen()) { + if ($h.checkFullScreen()) { $btnFull.addClass('active').attr('aria-pressed', 'true'); } else { $btnBord.addClass('active').attr('aria-pressed', 'true'); @@ -1154,30 +1682,39 @@ }); }, _initZoom: function () { - var self = this, $dialog, modalMain = self._getLayoutTemplate('modalMain'), modalId = '#' + MODAL_ID; + var self = this, $dialog, modalMain = self._getLayoutTemplate('modalMain'), modalId = '#' + $h.MODAL_ID; + if (!self.showPreview) { + return; + } self.$modal = $(modalId); if (!self.$modal || !self.$modal.length) { $dialog = $(document.createElement('div')).html(modalMain).insertAfter(self.$container); - self.$modal = $('#' + MODAL_ID).insertBefore($dialog); + self.$modal = $(modalId).insertBefore($dialog); $dialog.remove(); } + $h.initModal(self.$modal); self.$modal.html(self._getModalContent()); - self._listenModalEvent('show'); - self._listenModalEvent('shown'); - self._listenModalEvent('hide'); - self._listenModalEvent('hidden'); - self._listenModalEvent('loaded'); + $.each($h.MODAL_EVENTS, function (key, event) { + self._listenModalEvent(event); + }); }, _initZoomButtons: function () { var self = this, previewId = self.$modal.data('previewId') || '', $first, $last, - frames = self.$preview.find('.file-preview-frame').toArray(), len = frames.length, - $prev = self.$modal.find('.btn-prev'), $next = self.$modal.find('.btn-next'); - + thumbs = self.getFrames().toArray(), len = thumbs.length, $prev = self.$modal.find('.btn-prev'), + $next = self.$modal.find('.btn-next'); + if (thumbs.length < 2) { + $prev.hide(); + $next.hide(); + return; + } else { + $prev.show(); + $next.show(); + } if (!len) { return; } - $first = $(frames[0]); - $last = $(frames[len - 1]); + $first = $(thumbs[0]); + $last = $(thumbs[len - 1]); $prev.removeAttr('disabled'); $next.removeAttr('disabled'); if ($first.length && $first.attr('id') === previewId) { @@ -1208,7 +1745,7 @@ var self = this, $modal = self.$modal, $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'); if ($modal.hasClass('file-zoom-fullscreen')) { - toggleFullScreen(false); + $h.toggleFullScreen(false); if (!fullScreen) { if (!$btnFull.hasClass('active')) { $modal.removeClass('file-zoom-fullscreen'); @@ -1230,26 +1767,31 @@ self._maximizeZoomDialog(); return; } - toggleFullScreen(true); + $h.toggleFullScreen(true); } $modal.focus(); }, - _setZoomContent: function ($preview, animate) { - var self = this, $content, tmplt, body, title, $body, $dataEl, config, previewId = $preview.attr('id'), + _setZoomContent: function ($frame, animate) { + var self = this, $content, tmplt, body, title, $body, $dataEl, config, pid = $frame.attr('id'), $modal = self.$modal, $prev = $modal.find('.btn-prev'), $next = $modal.find('.btn-next'), $tmp, - $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'), - $btnTogh = $modal.find('.btn-toggleheader'); - tmplt = $preview.data('template') || 'generic'; - $content = $preview.find('.kv-file-content'); + $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'), cap, size, + $btnTogh = $modal.find('.btn-toggleheader'), $zoomPreview = self.$preview.find('#zoom-' + pid); + tmplt = $zoomPreview.attr('data-template') || 'generic'; + $content = $zoomPreview.find('.kv-file-content'); body = $content.length ? $content.html() : ''; - title = $preview.find('.file-footer-caption').text() || ''; - $modal.find('.kv-zoom-title').html(title); + cap = $frame.data('caption') || ''; + size = $frame.data('size') || ''; + title = cap + ' ' + size; + $modal.find('.kv-zoom-title').attr('title', $('
    ').html(title).text()).html(title); $body = $modal.find('.kv-zoom-body'); + $modal.removeClass('kv-single-content'); if (animate) { - $tmp = $body.clone().insertAfter($body); + $tmp = $body.addClass('file-thumb-loading').clone().insertAfter($body); $body.html(body).hide(); $tmp.fadeOut('fast', function () { - $body.fadeIn('fast'); + $body.fadeIn('fast', function () { + $body.removeClass('file-thumb-loading'); + }); $tmp.remove(); }); } else { @@ -1258,7 +1800,7 @@ config = self.previewZoomSettings[tmplt]; if (config) { $dataEl = $body.find('.kv-preview-data'); - addCss($dataEl, 'file-zoom-detail'); + $h.addCss($dataEl, 'file-zoom-detail'); $.each(config, function (key, value) { $dataEl.css(key, value); if (($dataEl.attr('width') && key === 'width') || ($dataEl.attr('height') && key === 'height')) { @@ -1266,20 +1808,24 @@ } }); } - $modal.data('previewId', previewId); - handler($prev, 'click', function () { - self._zoomSlideShow('prev', previewId); + $modal.data('previewId', pid); + var $img = $body.find('img'); + if ($img.length) { + $h.adjustOrientedImage($img, true); + } + self._handler($prev, 'click', function () { + self._zoomSlideShow('prev', pid); }); - handler($next, 'click', function () { - self._zoomSlideShow('next', previewId); + self._handler($next, 'click', function () { + self._zoomSlideShow('next', pid); }); - handler($btnFull, 'click', function () { + self._handler($btnFull, 'click', function () { self._resizeZoomDialog(true); }); - handler($btnBord, 'click', function () { + self._handler($btnBord, 'click', function () { self._resizeZoomDialog(false); }); - handler($btnTogh, 'click', function () { + self._handler($btnTogh, 'click', function () { var $header = $modal.find('.modal-header'), $floatBar = $modal.find('.modal-body .floating-buttons'), ht, $actions = $header.find('.kv-zoom-actions'), resize = function (height) { var $body = self.$modal.find('.kv-zoom-body'), h = self.zoomModalHeight; @@ -1305,143 +1851,82 @@ } $modal.focus(); }); - handler($modal, 'keydown', function (e) { + self._handler($modal, 'keydown', function (e) { var key = e.which || e.keyCode; if (key === 37 && !$prev.attr('disabled')) { - self._zoomSlideShow('prev', previewId); + self._zoomSlideShow('prev', pid); } if (key === 39 && !$next.attr('disabled')) { - self._zoomSlideShow('next', previewId); + self._zoomSlideShow('next', pid); } }); }, _zoomPreview: function ($btn) { - var self = this, $preview; + var self = this, $frame, $modal = self.$modal; if (!$btn.length) { throw 'Cannot zoom to detailed preview!'; } - self.$modal.html(self._getModalContent()); - $preview = $btn.closest('.file-preview-frame'); - self._setZoomContent($preview); - self.$modal.modal('show'); + $h.initModal($modal); + $modal.html(self._getModalContent()); + $frame = $btn.closest($h.FRAMES); + self._setZoomContent($frame); + $modal.modal('show'); self._initZoomButtons(); }, _zoomSlideShow: function (dir, previewId) { var self = this, $btn = self.$modal.find('.kv-zoom-actions .btn-' + dir), $targFrame, i, - frames = self.$preview.find('.file-preview-frame').toArray(), len = frames.length, out; + thumbs = self.getFrames().toArray(), len = thumbs.length, out; if ($btn.attr('disabled')) { return; } for (i = 0; i < len; i++) { - if ($(frames[i]).attr('id') === previewId) { + if ($(thumbs[i]).attr('id') === previewId) { out = dir === 'prev' ? i - 1 : i + 1; break; } } - if (out < 0 || out >= len || !frames[out]) { + if (out < 0 || out >= len || !thumbs[out]) { return; } - $targFrame = $(frames[out]); + $targFrame = $(thumbs[out]); if ($targFrame.length) { self._setZoomContent($targFrame, true); } self._initZoomButtons(); - self._raise('filezoom' + dir, { 'previewId': previewId, modal: self.$modal }); + self._raise('filezoom' + dir, {'previewId': previewId, modal: self.$modal}); }, _initZoomButton: function () { var self = this; self.$preview.find('.kv-file-zoom').each(function () { var $el = $(this); - handler($el, 'click', function () { + self._handler($el, 'click', function () { self._zoomPreview($el); }); }); }, - _initPreviewActions: function () { - var self = this, deleteExtraData = self.deleteExtraData || {}, - resetProgress = function () { - var hasFiles = self.isUploadable ? previewCache.count(self.id) : self.$element.get(0).files.length; - if (self.$preview.find('.kv-file-remove').length === 0 && !hasFiles) { - self.reset(); - self.initialCaption = ''; - } - }; - self._initZoomButton(); - self.$preview.find('.kv-file-remove').each(function () { - var $el = $(this), vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'); - if (isEmpty(vUrl) || vKey === undefined) { - return; + _inputFileCount: function() { + return this.$element.get(0).files.length; + }, + _refreshPreview: function() { + var self = this, files; + if (!self._inputFileCount() || !self.showPreview || !self.isPreviewable) { + return; + } + if (self.isAjaxUpload) { + files = self.getFileStack(); + self.filestack = []; + if (files.length) { + self._clearFileInput(); + } else { + files = self.$element.get(0).files; } - var $frame = $el.closest('.file-preview-frame'), cache = previewCache.data[self.id], - settings, params, index = $frame.data('fileindex'), config, extraData; - index = parseInt(index.replace('init_', '')); - config = isEmpty(cache.config) && isEmpty(cache.config[index]) ? null : cache.config[index]; - extraData = isEmpty(config) || isEmpty(config.extra) ? deleteExtraData : config.extra; - if (typeof extraData === "function") { - extraData = extraData(); - } - params = { id: $el.attr('id'), key: vKey, extra: extraData }; - settings = $.extend(true, {}, { - url: vUrl, - type: 'POST', - dataType: 'json', - data: $.extend(true, {}, { key: vKey }, extraData), - beforeSend: function (jqXHR) { - self.ajaxAborted = false; - self._raise('filepredelete', [vKey, jqXHR, extraData]); - if (self.ajaxAborted) { - jqXHR.abort(); - } else { - addCss($frame, 'file-uploading'); - addCss($el, 'disabled'); - } - }, - success: function (data, textStatus, jqXHR) { - var n, cap; - if (isEmpty(data) || isEmpty(data.error)) { - previewCache.init(self); - index = parseInt(($frame.data('fileindex')).replace('init_', '')); - previewCache.unset(self, index); - n = previewCache.count(self.id); - cap = n > 0 ? self._getMsgSelected(n) : ''; - self._raise('filedeleted', [vKey, jqXHR, extraData]); - self._setCaption(cap); - } else { - params.jqXHR = jqXHR; - params.response = data; - self._showError(data.error, params, 'filedeleteerror'); - $frame.removeClass('file-uploading'); - $el.removeClass('disabled'); - resetProgress(); - return; - } - $frame.removeClass('file-uploading').addClass('file-deleted'); - $frame.fadeOut('slow', function () { - self._clearObjects($frame); - $frame.remove(); - resetProgress(); - if (!n && self.getFileStack().length === 0) { - self._setCaption(''); - self.reset(); - } - }); - }, - error: function (jqXHR, textStatus, errorThrown) { - var errMsg = self._parseError(jqXHR, errorThrown); - params.jqXHR = jqXHR; - params.response = {}; - self._showError(errMsg, params, 'filedeleteerror'); - $frame.removeClass('file-uploading'); - resetProgress(); - } - }, self.ajaxDeleteSettings); - handler($el, 'click', function () { - if (!self._validateMinCount()) { - return false; - } - $.ajax(settings); - }); - }); + } else { + files = self.$element.get(0).files; + } + if (files && files.length) { + self.readFiles(files); + self._setFileDropZoneTitle(); + } }, _clearObjects: function ($el) { $el.find('video audio').each(function () { @@ -1454,32 +1939,25 @@ }, _clearFileInput: function () { var self = this, $el = self.$element, $srcFrm, $tmpFrm, $tmpEl; - self.fileInputCleared = true; - if (isEmpty($el.val())) { + if (!self._inputFileCount()) { return; } - // Fix for IE ver < 11, that does not clear file inputs. Requires a sequence of steps to prevent IE - // crashing but still allow clearing of the file input. - if (self.isIE9 || self.isIE10) { - $srcFrm = $el.closest('form'); - $tmpFrm = $(document.createElement('form')); - $tmpEl = $(document.createElement('div')); - $el.before($tmpEl); - if ($srcFrm.length) { - $srcFrm.after($tmpFrm); - } else { - $tmpEl.after($tmpFrm); - } - $tmpFrm.append($el).trigger('reset'); - $tmpEl.before($el).remove(); - $tmpFrm.remove(); - } else { // normal input clear behavior for other sane browsers - $el.val(''); + $srcFrm = $el.closest('form'); + $tmpFrm = $(document.createElement('form')); + $tmpEl = $(document.createElement('div')); + $el.before($tmpEl); + if ($srcFrm.length) { + $srcFrm.after($tmpFrm); + } else { + $tmpEl.after($tmpFrm); } + $tmpFrm.append($el).trigger('reset'); + $tmpEl.before($el).remove(); + $tmpFrm.remove(); }, _resetUpload: function () { var self = this; - self.uploadCache = { content: [], config: [], tags: [], append: true }; + self.uploadCache = {content: [], config: [], tags: [], append: true}; self.uploadCount = 0; self.uploadStatus = {}; self.uploadLog = []; @@ -1488,11 +1966,22 @@ self.totalImagesCount = 0; self.$btnUpload.removeAttr('disabled'); self._setProgress(0); - addCss(self.$progress, 'hide'); + self.$progress.hide(); self._resetErrors(false); self.ajaxAborted = false; self.ajaxRequests = []; self._resetCanvas(); + self.cacheInitialPreview = {}; + if (self.overwriteInitial) { + self.initialPreview = []; + self.initialPreviewConfig = []; + self.initialPreviewThumbTags = []; + self.previewCache.data = { + content: [], + config: [], + tags: [] + }; + } }, _resetCanvas: function () { var self = this; @@ -1502,13 +1991,14 @@ }, _hasInitialPreview: function () { var self = this; - return !self.overwriteInitial && previewCache.count(self.id); + return !self.overwriteInitial && self.previewCache.count(); }, _resetPreview: function () { var self = this, out, cap; - if (previewCache.count(self.id)) { - out = previewCache.out(self.id); - self.$preview.html(out.content); + if (self.previewCache.count()) { + out = self.previewCache.out(); + self._setPreviewContent(out.content); + self._setInitThumbAttr(); cap = self.initialCaption ? self.initialCaption : out.caption; self._setCaption(cap); } else { @@ -1526,10 +2016,10 @@ }, _validateDefaultPreview: function () { var self = this; - if (!self.showPreview || isEmpty(self.defaultPreviewContent)) { + if (!self.showPreview || $h.isEmpty(self.defaultPreviewContent)) { return; } - self.$preview.html('
    ' + self.defaultPreviewContent + '
    '); + self._setPreviewContent('
    ' + self.defaultPreviewContent + '
    '); self.$container.removeClass('file-input-new'); self._initClickable(); }, @@ -1541,8 +2031,9 @@ return; } if (self._hasInitialPreview()) { - out = previewCache.out(self.id); - self.$preview.html(out.content); + out = self.previewCache.out(); + self._setPreviewContent(out.content); + self._setInitThumbAttr(); self._setCaption(out.caption); self._initPreviewActions(); } else { @@ -1550,20 +2041,18 @@ } }, _getLayoutTemplate: function (t) { - var self = this, - template = ifSet(t, self.layoutTemplates, defaultLayoutTemplates[t]); - if (isEmpty(self.customLayoutTags)) { + var self = this, template = self.layoutTemplates[t]; + if ($h.isEmpty(self.customLayoutTags)) { return template; } - return replaceTags(template, self.customLayoutTags); + return $h.replaceTags(template, self.customLayoutTags); }, _getPreviewTemplate: function (t) { - var self = this, - template = ifSet(t, self.previewTemplates, defaultPreviewTemplates[t]); - if (isEmpty(self.customPreviewTags)) { + var self = this, template = self.previewTemplates[t]; + if ($h.isEmpty(self.customPreviewTags)) { return template; } - return replaceTags(template, self.customPreviewTags); + return $h.replaceTags(template, self.customPreviewTags); }, _getOutData: function (jqXHR, responseData, filesData) { var self = this; @@ -1585,9 +2074,17 @@ var self = this, strFiles = n === 1 ? self.fileSingle : self.filePlural; return n > 0 ? self.msgSelected.replace('{n}', n).replace('{files}', strFiles) : self.msgNoFilesSelected; }, + _getFrame: function (id) { + var self = this, $frame = $('#' + id); + if (!$frame.length) { + self._log('Invalid thumb frame with id: "' + id + '".'); + return null; + } + return $frame; + }, _getThumbs: function (css) { css = css || ''; - return this.$preview.find('.file-preview-frame:not(.file-preview-initial)' + css); + return this.getFrames(':not(.file-preview-initial)' + css); }, _getExtraData: function (previewId, index) { var self = this, data = self.uploadExtraData; @@ -1614,39 +2111,66 @@ } return xhrobj; }, + _initAjaxSettings: function () { + var self = this; + self._ajaxSettings = $.extend(true, {}, self.ajaxSettings); + self._ajaxDeleteSettings = $.extend(true, {}, self.ajaxDeleteSettings); + }, + _mergeAjaxCallback: function (funcName, srcFunc, type) { + var self = this, settings = self._ajaxSettings, flag = self.mergeAjaxCallbacks, targFunc; + if (type === 'delete') { + settings = self._ajaxDeleteSettings; + flag = self.mergeAjaxDeleteCallbacks; + } + targFunc = settings[funcName]; + if (flag && typeof targFunc === "function") { + if (flag === 'before') { + settings[funcName] = function () { + targFunc.apply(this, arguments); + srcFunc.apply(this, arguments); + }; + } else { + settings[funcName] = function () { + srcFunc.apply(this, arguments); + targFunc.apply(this, arguments); + }; + } + } else { + settings[funcName] = srcFunc; + } + }, _ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError, previewId, index) { var self = this, settings; - self._raise('filepreajax', [previewId, index]); + if (!self._raise('filepreajax', [previewId, index])) { + return; + } self._uploadExtra(previewId, index); + self._initAjaxSettings(); + self._mergeAjaxCallback('beforeSend', fnBefore); + self._mergeAjaxCallback('success', fnSuccess); + self._mergeAjaxCallback('complete', fnComplete); + self._mergeAjaxCallback('error', fnError); settings = $.extend(true, {}, { xhr: function () { var xhrobj = $.ajaxSettings.xhr(); return self._initXhr(xhrobj, previewId, self.getFileStack().length); }, - url: self.uploadUrl, + url: index && self.uploadUrlThumb ? self.uploadUrlThumb : self.uploadUrl, type: 'POST', dataType: 'json', data: self.formdata, cache: false, processData: false, - contentType: false, - beforeSend: fnBefore, - success: fnSuccess, - complete: fnComplete, - error: fnError - }, self.ajaxSettings); + contentType: false + }, self._ajaxSettings); self.ajaxRequests.push($.ajax(settings)); }, + _mergeArray: function (prop, content) { + var self = this, arr1 = $h.cleanArray(self[prop]), arr2 = $h.cleanArray(content); + self[prop] = arr1.concat(arr2); + }, _initUploadSuccess: function (out, $thumb, allFiles) { - var self = this, append, data, index, $newThumb, content, config, tags, i, - mergeArray = function (prop, content) { - if (!(self[prop] instanceof Array)) { - self[prop] = []; - } - if (content && content.length) { - self[prop] = self[prop].concat(content); - } - }; + var self = this, append, data, index, $div, $newCache, content, config, tags, i; if (!self.showPreview || typeof out !== 'object' || $.isEmptyObject(out)) { return; } @@ -1655,24 +2179,33 @@ content = out.initialPreview || []; config = out.initialPreviewConfig || []; tags = out.initialPreviewThumbTags || []; - append = out.append === undefined || out.append ? true : false; - if (content.length > 0 && !isArray(content)) { + append = out.append === undefined || out.append; + if (content.length > 0 && !$h.isArray(content)) { content = content.split(self.initialPreviewDelimiter); } - self.overwriteInitial = false; - mergeArray('initialPreview', content); - mergeArray('initialPreviewConfig', config); - mergeArray('initialPreviewThumbTags', tags); + self._mergeArray('initialPreview', content); + self._mergeArray('initialPreviewConfig', config); + self._mergeArray('initialPreviewThumbTags', tags); if ($thumb !== undefined) { if (!allFiles) { - index = previewCache.add(self.id, content, config[0], tags[0], append); - data = previewCache.get(self.id, index, false); - $newThumb = $(data).hide(); - $thumb.after($newThumb).fadeOut('slow', function () { - $newThumb.fadeIn('slow').css('display:inline-block'); + index = self.previewCache.add(content, config[0], tags[0], append); + data = self.previewCache.get(index, false); + $div = $(document.createElement('div')).html(data).hide().insertAfter($thumb); + $newCache = $div.find('.kv-zoom-cache'); + if ($newCache && $newCache.length) { + $newCache.insertAfter($thumb); + } + $thumb.fadeOut('slow', function () { + var $newThumb = $div.find('.file-preview-frame'); + if ($newThumb && $newThumb.length) { + $newThumb.insertBefore($thumb).fadeIn('slow').css('display:inline-block'); + } self._initPreviewActions(); self._clearFileInput(); + $h.cleanZoomCache(self.$preview.find('#zoom-' + $thumb.attr('id'))); $thumb.remove(); + $div.remove(); + self._initSortable(); }); } else { i = $thumb.attr('data-fileindex'); @@ -1682,7 +2215,7 @@ self.uploadCache.append = append; } } else { - previewCache.set(self.id, content, config, tags, append); + self.previewCache.set(content, config, tags, append); self._initPreview(); self._initPreviewActions(); } @@ -1693,18 +2226,20 @@ if (!self.showPreview) { return; } - self._getThumbs('.file-preview-success').each(function () { - var $thumb = $(this), $remove = $thumb.find('.kv-file-remove'); + self._getThumbs($h.FRAMES + '.file-preview-success').each(function () { + var $thumb = $(this), $preview = self.$preview, $remove = $thumb.find('.kv-file-remove'); $remove.removeAttr('disabled'); - handler($remove, 'click', function () { - var out = self._raise('filesuccessremove', [$thumb.attr('id'), $thumb.data('fileindex')]); - cleanMemory($thumb); + self._handler($remove, 'click', function () { + var id = $thumb.attr('id'), + out = self._raise('filesuccessremove', [id, $thumb.attr('data-fileindex')]); + $h.cleanMemory($thumb); if (out === false) { return; } $thumb.fadeOut('slow', function () { + $h.cleanZoomCache($preview.find('#zoom-' + id)); $thumb.remove(); - if (!self.$preview.find('.file-preview-frame').length) { + if (!self.getFrames().length) { self.reset(); } }); @@ -1732,68 +2267,114 @@ self.formdata.append(key, value); }); }, - _uploadSingle: function (i, files, allFiles) { + _uploadSingle: function (i, isBatch) { var self = this, total = self.getFileStack().length, formdata = new FormData(), outData, previewId = self.previewInitId + "-" + i, $thumb, chkComplete, $btnUpload, $btnDelete, - hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), - fnBefore, fnSuccess, fnComplete, fnError, updateUploadLog, params = { id: previewId, index: i }; + hasPostData = self.filestack.length > 0 || !$.isEmptyObject(self.uploadExtraData), uploadFailed, + $prog = $('#' + previewId).find('.file-thumb-progress'), fnBefore, fnSuccess, fnComplete, fnError, + updateUploadLog, params = {id: previewId, index: i}; self.formdata = formdata; if (self.showPreview) { $thumb = $('#' + previewId + ':not(.file-preview-initial)'); $btnUpload = $thumb.find('.kv-file-upload'); $btnDelete = $thumb.find('.kv-file-remove'); - $('#' + previewId).find('.file-thumb-progress').removeClass('hide'); + $prog.show(); } if (total === 0 || !hasPostData || ($btnUpload && $btnUpload.hasClass('disabled')) || self._abort(params)) { return; } updateUploadLog = function (i, previewId) { - self.updateStack(i, undefined); + if (!uploadFailed) { + self.updateStack(i, undefined); + } self.uploadLog.push(previewId); if (self._checkAsyncComplete()) { self.fileBatchCompleted = true; } }; chkComplete = function () { - var u = self.uploadCache; + var u = self.uploadCache, $initThumbs, i, j, len = 0, data = self.cacheInitialPreview; if (!self.fileBatchCompleted) { return; } + if (data && data.content) { + len = data.content.length; + } setTimeout(function () { + var triggerReset = self.getFileStack(true).length === 0; if (self.showPreview) { - previewCache.set(self.id, u.content, u.config, u.tags, u.append); + self.previewCache.set(u.content, u.config, u.tags, u.append); + if (len) { + for (i = 0; i < u.content.length; i++) { + j = i + len; + data.content[j] = u.content[i]; + //noinspection JSUnresolvedVariable + if (data.config.length) { + data.config[j] = u.config[i]; + } + if (data.tags.length) { + data.tags[j] = u.tags[i]; + } + } + self.initialPreview = $h.cleanArray(data.content); + self.initialPreviewConfig = $h.cleanArray(data.config); + self.initialPreviewThumbTags = $h.cleanArray(data.tags); + } else { + self.initialPreview = u.content; + self.initialPreviewConfig = u.config; + self.initialPreviewThumbTags = u.tags; + } + self.cacheInitialPreview = {}; if (self.hasInitData) { self._initPreview(); self._initPreviewActions(); } } - self.unlock(); - self._clearFileInput(); + self.unlock(triggerReset); + if (triggerReset) { + self._clearFileInput(); + } + $initThumbs = self.$preview.find('.file-preview-initial'); + if (self.uploadAsync && $initThumbs.length) { + $h.addCss($initThumbs, $h.SORT_CSS); + self._initSortable(); + } self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); self.uploadCount = 0; self.uploadStatus = {}; self.uploadLog = []; self._setProgress(101); + self.ajaxAborted = false; }, 100); }; fnBefore = function (jqXHR) { outData = self._getOutData(jqXHR); self.fileBatchCompleted = false; + if (!isBatch) { + self.ajaxAborted = false; + } if (self.showPreview) { if (!$thumb.hasClass('file-preview-success')) { self._setThumbStatus($thumb, 'Loading'); - addCss($thumb, 'file-uploading'); + $h.addCss($thumb, 'file-uploading'); } $btnUpload.attr('disabled', true); $btnDelete.attr('disabled', true); } - if (!allFiles) { + if (!isBatch) { self.lock(); } self._raise('filepreupload', [outData, previewId, i]); $.extend(true, params, outData); if (self._abort(params)) { jqXHR.abort(); + if (!isBatch) { + self._setThumbStatus($thumb, 'New'); + $thumb.removeClass('file-uploading'); + $btnUpload.removeAttr('disabled'); + $btnDelete.removeAttr('disabled'); + self.unlock(); + } self._setProgressCancelled(); } }; @@ -1802,24 +2383,30 @@ outData = self._getOutData(jqXHR, data); $.extend(true, params, outData); setTimeout(function () { - if (isEmpty(data) || isEmpty(data.error)) { + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { if (self.showPreview) { self._setThumbStatus($thumb, 'Success'); $btnUpload.hide(); - self._initUploadSuccess(data, $thumb, allFiles); + self._initUploadSuccess(data, $thumb, isBatch); + self._setProgress(101, $prog); } self._raise('fileuploaded', [outData, pid, i]); - if (!allFiles) { + if (!isBatch) { self.updateStack(i, undefined); } else { updateUploadLog(i, pid); } } else { + uploadFailed = true; self._showUploadError(data.error, params); - self._setPreviewError($thumb, i); - if (allFiles) { + self._setPreviewError($thumb, i, self.filestack[i], self.retryErrorUploads); + if (!self.retryErrorUploads) { + $btnUpload.hide(); + } + if (isBatch) { updateUploadLog(i, pid); } + self._setProgress(101, $('#' + pid).find('.file-thumb-progress'), self.msgUploadError); } }, 100); }; @@ -1829,9 +2416,8 @@ $btnUpload.removeAttr('disabled'); $btnDelete.removeAttr('disabled'); $thumb.removeClass('file-uploading'); - self._setProgress(101, $('#' + previewId).find('.file-thumb-progress')); } - if (!allFiles) { + if (!isBatch) { self.unlock(false); self._clearFileInput(); } else { @@ -1841,18 +2427,25 @@ }, 100); }; fnError = function (jqXHR, textStatus, errorThrown) { - var errMsg = self._parseError(jqXHR, errorThrown, (allFiles ? files[i].name : null)); + var op = self.ajaxOperations.uploadThumb, + errMsg = self._parseError(op, jqXHR, errorThrown, (isBatch && self.filestack[i].name ? self.filestack[i].name : null)); + uploadFailed = true; setTimeout(function () { - if (allFiles) { + if (isBatch) { updateUploadLog(i, previewId); } self.uploadStatus[previewId] = 100; - self._setPreviewError($thumb, i); + self._setPreviewError($thumb, i, self.filestack[i], self.retryErrorUploads); + if (!self.retryErrorUploads) { + $btnUpload.hide(); + } $.extend(true, params, self._getOutData(jqXHR)); + self._setProgress(101, $prog, self.msgAjaxProgressError.replace('{operation}', op)); + self._setProgress(101, $('#' + previewId).find('.file-thumb-progress'), self.msgUploadError); self._showUploadError(errMsg, params); }, 100); }; - formdata.append(self.uploadFileAttr, files[i], self.filenames[i]); + formdata.append(self.uploadFileAttr, self.filestack[i], self.filenames[i]); formdata.append('file_id', i); self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, previewId, i); }, @@ -1873,13 +2466,14 @@ fnBefore = function (jqXHR) { self.lock(); var outData = self._getOutData(jqXHR); + self.ajaxAborted = false; if (self.showPreview) { self._getThumbs().each(function () { var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), $btnDelete = $thumb.find('.kv-file-remove'); if (!$thumb.hasClass('file-preview-success')) { self._setThumbStatus($thumb, 'Loading'); - addCss($thumb, 'file-uploading'); + $h.addCss($thumb, 'file-uploading'); } $btnUpload.attr('disabled', true); $btnDelete.attr('disabled', true); @@ -1888,63 +2482,77 @@ self._raise('filebatchpreupload', [outData]); if (self._abort(outData)) { jqXHR.abort(); + self._getThumbs().each(function () { + var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), + $btnDelete = $thumb.find('.kv-file-remove'); + if ($thumb.hasClass('file-preview-loading')) { + self._setThumbStatus($thumb, 'New'); + $thumb.removeClass('file-uploading'); + } + $btnUpload.removeAttr('disabled'); + $btnDelete.removeAttr('disabled'); + }); self._setProgressCancelled(); } }; fnSuccess = function (data, textStatus, jqXHR) { /** @namespace data.errorkeys */ - var outData = self._getOutData(jqXHR, data), $thumbs = self._getThumbs(':not(.file-preview-error)'), key = 0, - keys = isEmpty(data) || isEmpty(data.errorkeys) ? [] : data.errorkeys; - if (isEmpty(data) || isEmpty(data.error)) { + var outData = self._getOutData(jqXHR, data), key = 0, + $thumbs = self._getThumbs(':not(.file-preview-success)'), + keys = $h.isEmpty(data) || $h.isEmpty(data.errorkeys) ? [] : data.errorkeys; + + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { self._raise('filebatchuploadsuccess', [outData]); setAllUploaded(); if (self.showPreview) { $thumbs.each(function () { - var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'); - $thumb.find('.kv-file-upload').hide(); + var $thumb = $(this); self._setThumbStatus($thumb, 'Success'); $thumb.removeClass('file-uploading'); - $btnUpload.removeAttr('disabled'); + $thumb.find('.kv-file-upload').hide().removeAttr('disabled'); }); self._initUploadSuccess(data); } else { self.reset(); } + self._setProgress(101); } else { if (self.showPreview) { $thumbs.each(function () { - var $thumb = $(this), $btnDelete = $thumb.find('.kv-file-remove'), - $btnUpload = $thumb.find('.kv-file-upload'); + var $thumb = $(this), i = $thumb.attr('data-fileindex'); $thumb.removeClass('file-uploading'); - $btnUpload.removeAttr('disabled'); - $btnDelete.removeAttr('disabled'); - if (keys.length === 0) { - self._setPreviewError($thumb); - return; - } - if ($.inArray(key, keys) !== -1) { - self._setPreviewError($thumb); + $thumb.find('.kv-file-upload').removeAttr('disabled'); + $thumb.find('.kv-file-remove').removeAttr('disabled'); + if (keys.length === 0 || $.inArray(key, keys) !== -1) { + self._setPreviewError($thumb, i, self.filestack[i], self.retryErrorUploads); + if (!self.retryErrorUploads) { + $thumb.find('.kv-file-upload').hide(); + self.updateStack(i, undefined); + } } else { $thumb.find('.kv-file-upload').hide(); self._setThumbStatus($thumb, 'Success'); - self.updateStack(key, undefined); + self.updateStack(i, undefined); + } + if (!$thumb.hasClass('file-preview-error') || self.retryErrorUploads) { + key++; } - key++; }); self._initUploadSuccess(data); } self._showUploadError(data.error, outData, 'filebatchuploaderror'); + self._setProgress(101, self.$progress, self.msgUploadError); } }; fnComplete = function () { - self._setProgress(101); self.unlock(); self._initSuccessThumbs(); self._clearFileInput(); self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); }; fnError = function (jqXHR, textStatus, errorThrown) { - var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown); + var outData = self._getOutData(jqXHR), op = self.ajaxOperations.uploadBatch, + errMsg = self._parseError(op, jqXHR, errorThrown); self._showUploadError(errMsg, outData, 'filebatchuploaderror'); self.uploadFileCount = total - 1; if (!self.showPreview) { @@ -1960,9 +2568,10 @@ self._getThumbs().removeClass('file-uploading'); self._getThumbs(' .kv-file-upload').removeAttr('disabled'); self._getThumbs(' .kv-file-delete').removeAttr('disabled'); + self._setProgress(101, self.$progress, self.msgAjaxProgressError.replace('{operation}', op)); }; $.each(files, function (key, data) { - if (!isEmpty(files[key])) { + if (!$h.isEmpty(files[key])) { self.formdata.append(self.uploadFileAttr, data, self.filenames[key]); } }); @@ -1988,44 +2597,69 @@ }; fnSuccess = function (data, textStatus, jqXHR) { var outData = self._getOutData(jqXHR, data); - if (isEmpty(data) || isEmpty(data.error)) { + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { self._raise('filebatchuploadsuccess', [outData]); self._clearFileInput(); self._initUploadSuccess(data); + self._setProgress(101); } else { self._showUploadError(data.error, outData, 'filebatchuploaderror'); } }; fnComplete = function () { - self._setProgress(101); self.unlock(); self._clearFileInput(); self._raise('filebatchuploadcomplete', [self.filestack, self._getExtraData()]); }; fnError = function (jqXHR, textStatus, errorThrown) { - var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown); + var outData = self._getOutData(jqXHR), op = self.ajaxOperations.uploadExtra, + errMsg = self._parseError(op, jqXHR, errorThrown); params.data = outData; self._showUploadError(errMsg, outData, 'filebatchuploaderror'); + self._setProgress(101, self.$progress, self.msgAjaxProgressError.replace('{operation}', op)); }; self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError); }, + _deleteFileIndex: function ($frame) { + var self = this, ind = $frame.attr('data-fileindex'), rev = self.reversePreviewOrder; + if (ind.substring(0, 5) === 'init_') { + ind = parseInt(ind.replace('init_', '')); + self.initialPreview = $h.spliceArray(self.initialPreview, ind, rev); + self.initialPreviewConfig = $h.spliceArray(self.initialPreviewConfig, ind, rev); + self.initialPreviewThumbTags = $h.spliceArray(self.initialPreviewThumbTags, ind, rev); + self.getFrames().each(function () { + var $nFrame = $(this), nInd = $nFrame.attr('data-fileindex'); + if (nInd.substring(0, 5) === 'init_') { + nInd = parseInt(nInd.replace('init_', '')); + if (nInd > ind) { + nInd--; + $nFrame.attr('data-fileindex', 'init_' + nInd); + } + } + }); + if (self.uploadAsync) { + self.cacheInitialPreview = self.getPreview(); + } + } + }, _initFileActions: function () { - var self = this; + var self = this, $preview = self.$preview; if (!self.showPreview) { return; } self._initZoomButton(); - self.$preview.find('.kv-file-remove').each(function () { - var $el = $(this), $frame = $el.closest('.file-preview-frame'), hasError, - id = $frame.attr('id'), ind = $frame.attr('data-fileindex'), n, cap, status; - handler($el, 'click', function () { + self.getFrames(' .kv-file-remove').each(function () { + var $el = $(this), $frame = $el.closest($h.FRAMES), hasError, id = $frame.attr('id'), + ind = $frame.attr('data-fileindex'), n, cap, status; + self._handler($el, 'click', function () { status = self._raise('filepreremove', [id, ind]); if (status === false || !self._validateMinCount()) { return false; } hasError = $frame.hasClass('file-preview-error'); - cleanMemory($frame); + $h.cleanMemory($frame); $frame.fadeOut('slow', function () { + $h.cleanZoomCache($preview.find('#zoom-' + id)); self.updateStack(ind, undefined); self._clearObjects($frame); $frame.remove(); @@ -2038,9 +2672,8 @@ }); } self._clearFileInput(); - var filestack = self.getFileStack(true), chk = previewCache.count(self.id), - len = filestack.length, - hasThumb = self.showPreview && self.$preview.find('.file-preview-frame').length; + var filestack = self.getFileStack(true), chk = self.previewCache.count(), + len = filestack.length, hasThumb = self.showPreview && self.getFrames().length; if (len === 0 && chk === 0 && !hasThumb) { self.reset(); } else { @@ -2052,147 +2685,1086 @@ }); }); }); - self.$preview.find('.kv-file-upload').each(function () { + self.getFrames(' .kv-file-upload').each(function () { var $el = $(this); - handler($el, 'click', function () { - var $frame = $el.closest('.file-preview-frame'), - ind = $frame.attr('data-fileindex'); - if (!$frame.hasClass('file-preview-error')) { - self._uploadSingle(ind, self.filestack, false); + self._handler($el, 'click', function () { + var $frame = $el.closest($h.FRAMES), ind = $frame.attr('data-fileindex'); + self.$progress.hide(); + if ($frame.hasClass('file-preview-error') && !self.retryErrorUploads) { + return; + } + self._uploadSingle(ind, false); + }); + }); + }, + _initPreviewActions: function () { + var self = this, $preview = self.$preview, deleteExtraData = self.deleteExtraData || {}, + btnRemove = $h.FRAMES + ' .kv-file-remove', settings = self.fileActionSettings, + origClass = settings.removeClass, errClass = settings.removeErrorClass, + resetProgress = function () { + var hasFiles = self.isAjaxUpload ? self.previewCache.count() : self._inputFileCount(); + if (!$preview.find($h.FRAMES).length && !hasFiles) { + self._setCaption(''); + self.reset(); + self.initialCaption = ''; + } + }; + self._initZoomButton(); + $preview.find(btnRemove).each(function () { + var $el = $(this), vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'), + fnBefore, fnSuccess, fnError; + if ($h.isEmpty(vUrl) || vKey === undefined) { + return; + } + var $frame = $el.closest($h.FRAMES), cache = self.previewCache.data, + settings, params, index = $frame.attr('data-fileindex'), config, extraData; + index = parseInt(index.replace('init_', '')); + config = $h.isEmpty(cache.config) && $h.isEmpty(cache.config[index]) ? null : cache.config[index]; + extraData = $h.isEmpty(config) || $h.isEmpty(config.extra) ? deleteExtraData : config.extra; + if (typeof extraData === "function") { + extraData = extraData(); + } + params = {id: $el.attr('id'), key: vKey, extra: extraData}; + fnBefore = function (jqXHR) { + self.ajaxAborted = false; + self._raise('filepredelete', [vKey, jqXHR, extraData]); + if (self._abort()) { + jqXHR.abort(); + } else { + $el.removeClass(errClass); + $h.addCss($frame, 'file-uploading'); + $h.addCss($el, 'disabled ' + origClass); + } + }; + fnSuccess = function (data, textStatus, jqXHR) { + var n, cap; + if (!$h.isEmpty(data) && !$h.isEmpty(data.error)) { + params.jqXHR = jqXHR; + params.response = data; + self._showError(data.error, params, 'filedeleteerror'); + $frame.removeClass('file-uploading'); + $el.removeClass('disabled ' + origClass).addClass(errClass); + resetProgress(); + return; + } + $frame.removeClass('file-uploading').addClass('file-deleted'); + $frame.fadeOut('slow', function () { + index = parseInt(($frame.attr('data-fileindex')).replace('init_', '')); + self.previewCache.unset(index); + self._deleteFileIndex($frame); + n = self.previewCache.count(); + cap = n > 0 ? self._getMsgSelected(n) : ''; + self._setCaption(cap); + self._raise('filedeleted', [vKey, jqXHR, extraData]); + $h.cleanZoomCache($preview.find('#zoom-' + $frame.attr('id'))); + self._clearObjects($frame); + $frame.remove(); + resetProgress(); + }); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + var op = self.ajaxOperations.deleteThumb, errMsg = self._parseError(op, jqXHR, errorThrown); + params.jqXHR = jqXHR; + params.response = {}; + self._showError(errMsg, params, 'filedeleteerror'); + $frame.removeClass('file-uploading'); + $el.removeClass('disabled ' + origClass).addClass(errClass); + resetProgress(); + }; + self._initAjaxSettings(); + self._mergeAjaxCallback('beforeSend', fnBefore, 'delete'); + self._mergeAjaxCallback('success', fnSuccess, 'delete'); + self._mergeAjaxCallback('error', fnError, 'delete'); + settings = $.extend(true, {}, { + url: vUrl, + type: 'POST', + dataType: 'json', + data: $.extend(true, {}, {key: vKey}, extraData) + }, self._ajaxDeleteSettings); + self._handler($el, 'click', function () { + if (!self._validateMinCount()) { + return false; + } + self.ajaxAborted = false; + self._raise('filebeforedelete', [vKey, extraData]); + //noinspection JSUnresolvedVariable,JSHint + if (self.ajaxAborted instanceof Promise) { + self.ajaxAborted.then(function (result) { + if (!result) { + $.ajax(settings); + } + }); + } else { + if (!self.ajaxAborted) { + $.ajax(settings); + } } }); }); }, _hideFileIcon: function () { - if (this.overwriteInitial) { - this.$captionContainer.find('.kv-caption-icon').hide(); + var self = this; + if (self.overwriteInitial) { + self.$captionContainer.removeClass('icon-visible'); } }, _showFileIcon: function () { - this.$captionContainer.find('.kv-caption-icon').show(); + var self = this; + $h.addCss(self.$captionContainer, 'icon-visible'); }, _getSize: function (bytes) { - var self = this, size = parseFloat(bytes); - if (!bytes || !size || isNaN(bytes) || isNaN(size)) { - return self._getLayoutTemplate('size').replace('{sizeText}', '0.00 KB'); + var self = this, size = parseFloat(bytes), i, func = self.fileSizeGetter, sizes, out; + if (!$.isNumeric(bytes) || !$.isNumeric(size)) { + return ''; } - var i, func = self.fileSizeGetter, sizes, out; if (typeof func === 'function') { - out = func(bytes); + out = func(size); } else { - i = Math.floor(Math.log(size) / Math.log(1024)); - sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - out = (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; + if (size === 0) { + out = '0.00 B'; + } else { + i = Math.floor(Math.log(size) / Math.log(1024)); + sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + out = (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; + } } return self._getLayoutTemplate('size').replace('{sizeText}', out); }, - _generatePreviewTemplate: function (cat, data, fname, ftype, previewId, isError, size, frameClass, foot, ind) { - var self = this, tmplt = self._getPreviewTemplate(cat), content, sText, css = frameClass || '', - config = ifSet(cat, self.previewSettings, defaultPreviewSettings[cat]), caption = self.slug(fname), - footer = foot || self._renderFileFooter(caption, size, config.width, isError); - ind = ind || previewId.slice(previewId.lastIndexOf('-') + 1); - tmplt = self._parseFilePreviewIcon(tmplt, fname); - if (cat === 'text' || cat === 'html') { - sText = cat === 'text' ? htmlEncode(data) : data; - content = tmplt.replace(/\{previewId}/g, previewId).replace(/\{caption}/g, caption) - .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) - .replace(/\{frameClass}/g, css).replace(/\{cat}/g, ftype) - .replace(/\{footer}/g, footer).replace(/\{fileindex}/g, ind) - .replace(/\{data}/g, sText).replace(/\{template}/g, cat); - } else { - content = tmplt.replace(/\{previewId}/g, previewId).replace(/\{caption}/g, caption) - .replace(/\{frameClass}/g, css).replace(/\{type}/g, ftype).replace(/\{fileindex}/g, ind) - .replace(/\{width}/g, config.width).replace(/\{height}/g, config.height) - .replace(/\{footer}/g, footer).replace(/\{data}/g, data).replace(/\{template}/g, cat); + _generatePreviewTemplate: function (cat, data, fname, ftype, previewId, isError, size, frameClass, foot, ind, templ) { + var self = this, caption = self.slug(fname), prevContent, zoomContent = '', styleAttribs = '', + screenW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + config = screenW < 400 ? (self.previewSettingsSmall[cat] || self.defaults.previewSettingsSmall[cat]) : + (self.previewSettings[cat] || self.defaults.previewSettings[cat]), + footer = foot || self._renderFileFooter(caption, size, 'auto', isError), + hasIconSetting = self._getPreviewIcon(fname), typeCss = 'type-default', + forcePrevIcon = hasIconSetting && self.preferIconicPreview, + forceZoomIcon = hasIconSetting && self.preferIconicZoomPreview, getContent; + if (config) { + $.each(config, function (key, val) { + styleAttribs += key + ':' + val + ';'; + }); } - return content; + getContent = function (c, d, zoom, frameCss) { + var id = zoom ? 'zoom-' + previewId : previewId, tmplt = self._getPreviewTemplate(c), + css = (frameClass || '') + ' ' + frameCss; + if (self.frameClass) { + css = self.frameClass + ' ' + css; + } + if (zoom) { + css = css.replace(' ' + $h.SORT_CSS, ''); + } + tmplt = self._parseFilePreviewIcon(tmplt, fname); + if (c === 'text') { + d = $h.htmlEncode(d); + } + if (cat === 'object' && !ftype) { + $.each(self.defaults.fileTypeSettings, function (key, func) { + if (key === 'object' || key === 'other') { + return; + } + if (func(fname, ftype)) { + typeCss = 'type-' + key; + } + }); + } + return tmplt.setTokens({ + 'previewId': id, + 'caption': caption, + 'frameClass': css, + 'type': ftype, + 'fileindex': ind, + 'typeCss': typeCss, + 'footer': footer, + 'data': d, + 'template': templ || cat, + 'style': styleAttribs ? 'style="' + styleAttribs + '"' : '' + }); + }; + ind = ind || previewId.slice(previewId.lastIndexOf('-') + 1); + if (self.fileActionSettings.showZoom) { + zoomContent = getContent((forceZoomIcon ? 'other' : cat), data, true, 'kv-zoom-thumb'); + } + zoomContent = '\n' + self._getLayoutTemplate('zoomCache').replace('{zoomContent}', zoomContent); + prevContent = getContent((forcePrevIcon ? 'other' : cat), data, false, 'kv-preview-thumb'); + return prevContent + zoomContent; + }, + _addToPreview: function ($preview, content) { + var self = this; + return self.reversePreviewOrder ? $preview.prepend(content) : $preview.append(content); }, _previewDefault: function (file, previewId, isDisabled) { - var self = this, $preview = self.$preview, $previewLive = $preview.find('.file-live-thumbs'); + var self = this, $preview = self.$preview; if (!self.showPreview) { return; } - var fname = file ? file.name : '', ftype = file ? file.type : '', content, - isError = isDisabled === true && !self.isUploadable, data = objUrl.createObjectURL(file); + var fname = file ? file.name : '', ftype = file ? file.type : '', content, size = file.size || 0, + caption = self.slug(fname), isError = isDisabled === true && !self.isAjaxUpload, + data = $h.objUrl.createObjectURL(file); self._clearDefaultPreview(); - content = self._generatePreviewTemplate('other', data, fname, ftype, previewId, isError, file.size); - if (!$previewLive.length) { - $previewLive = $(document.createElement('div')).addClass('file-live-thumbs').appendTo($preview); - } - $previewLive.append("\n" + content); - if (isDisabled === true && self.isUploadable) { + content = self._generatePreviewTemplate('other', data, fname, ftype, previewId, isError, size); + self._addToPreview($preview, content); + self._setThumbAttr(previewId, caption, size); + if (isDisabled === true && self.isAjaxUpload) { self._setThumbStatus($('#' + previewId), 'Error'); } }, - _previewFile: function (i, file, theFile, previewId, data) { + _previewFile: function (i, file, theFile, previewId, data, fileInfo) { if (!this.showPreview) { return; } - var self = this, cat = self._parseFileType(file), fname = file ? file.name : '', caption = self.slug(fname), - types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, $preview = self.$preview, - chkTypes = types && types.indexOf(cat) >= 0, $previewLive = $preview.find('.file-live-thumbs'), - iData = (cat === 'text' || cat === 'html' || cat === 'image') ? theFile.target.result : data, content, - chkMimes = mimes && mimes.indexOf(file.type) !== -1; - if (!$previewLive.length) { - $previewLive = $(document.createElement('div')).addClass('file-live-thumbs').appendTo($preview); - } + var self = this, fname = file ? file.name : '', ftype = fileInfo.type, caption = fileInfo.name, + cat = self._parseFileType(ftype, fname), types = self.allowedPreviewTypes, content, + mimes = self.allowedPreviewMimeTypes, $preview = self.$preview, fsize = file.size || 0, + chkTypes = types && types.indexOf(cat) >= 0, chkMimes = mimes && mimes.indexOf(ftype) !== -1, + iData = (cat === 'text' || cat === 'html' || cat === 'image') ? theFile.target.result : data; /** @namespace window.DOMPurify */ if (cat === 'html' && self.purifyHtml && window.DOMPurify) { iData = window.DOMPurify.sanitize(iData); } if (chkTypes || chkMimes) { - content = self._generatePreviewTemplate(cat, iData, fname, file.type, previewId, false, file.size); + content = self._generatePreviewTemplate(cat, iData, fname, ftype, previewId, false, fsize); self._clearDefaultPreview(); - $previewLive.append("\n" + content); - self._validateImage(i, previewId, caption, file.type); + self._addToPreview($preview, content); + var $img = $preview.find('#' + previewId + ' img'); + if ($img.length && self.autoOrientImage) { + $h.validateOrientation(file, function (value) { + if (!value) { + self._validateImage(previewId, caption, ftype, fsize, iData); + return; + } + var $zoomImg = $preview.find('#zoom-' + previewId + ' img'), css = 'rotate-' + value; + if (value > 4) { + css += ($img.width() > $img.height() ? ' is-portrait-gt4' : ' is-landscape-gt4'); + } + $h.addCss($img, css); + $h.addCss($zoomImg, css); + self._raise('fileimageoriented', {'$img': $img, 'file': file}); + self._validateImage(previewId, caption, ftype, fsize, iData); + $h.adjustOrientedImage($img); + }); + } else { + self._validateImage(previewId, caption, ftype, fsize, iData); + } } else { self._previewDefault(file, previewId); } + self._setThumbAttr(previewId, caption, fsize); self._initSortable(); }, - _slugDefault: function (text) { - return isEmpty(text) ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_'); + _setThumbAttr: function (id, caption, size) { + var self = this, $frame = $('#' + id); + if ($frame.length) { + size = size && size > 0 ? self._getSize(size) : ''; + $frame.data({'caption': caption, 'size': size}); + } }, - _readFiles: function (files) { + _setInitThumbAttr: function () { + var self = this, data = self.previewCache.data, len = self.previewCache.count(), config, + caption, size, previewId; + if (len === 0) { + return; + } + for (var i = 0; i < len; i++) { + config = data.config[i]; + previewId = self.previewInitId + '-' + 'init_' + i; + caption = $h.ifSet('caption', config, $h.ifSet('filename', config)); + size = $h.ifSet('size', config); + self._setThumbAttr(previewId, caption, size); + } + }, + _slugDefault: function (text) { + return $h.isEmpty(text) ? '' : String(text).replace(/[\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_'); + }, + _updateFileDetails: function (numFiles) { + var self = this, $el = self.$element, fileStack = self.getFileStack(), + name = ($h.isIE(9) && $h.findFileName($el.val())) || + ($el[0].files[0] && $el[0].files[0].name) || (fileStack.length && fileStack[0].name) || '', + label = self.slug(name), n = self.isAjaxUpload ? fileStack.length : numFiles, + nFiles = self.previewCache.count() + n, log = n === 1 ? label : self._getMsgSelected(nFiles); + if (self.isError) { + self.$previewContainer.removeClass('file-thumb-loading'); + self.$previewStatus.html(''); + self.$captionContainer.removeClass('icon-visible'); + } else { + self._showFileIcon(); + } + self._setCaption(log, self.isError); + self.$container.removeClass('file-input-new file-input-ajax-new'); + if (arguments.length === 1) { + self._raise('fileselect', [numFiles, label]); + } + if (self.previewCache.count()) { + self._initPreviewActions(); + } + }, + _setThumbStatus: function ($thumb, status) { + var self = this; + if (!self.showPreview) { + return; + } + var icon = 'indicator' + status, msg = icon + 'Title', + css = 'file-preview-' + status.toLowerCase(), + $indicator = $thumb.find('.file-upload-indicator'), + config = self.fileActionSettings; + $thumb.removeClass('file-preview-success file-preview-error file-preview-loading'); + if (status === 'Success') { + $thumb.find('.file-drag-handle').remove(); + } + $indicator.html(config[icon]); + $indicator.attr('title', config[msg]); + $thumb.addClass(css); + if (status === 'Error' && !self.retryErrorUploads) { + $thumb.find('.kv-file-upload').attr('disabled', true); + } + }, + _setProgressCancelled: function () { + var self = this; + self._setProgress(101, self.$progress, self.msgCancelled); + }, + _setProgress: function (p, $el, error) { + var self = this, pct = Math.min(p, 100), out, pctLimit = self.progressUploadThreshold, + t = p <= 100 ? self.progressTemplate : self.progressCompleteTemplate, + template = pct < 100 ? self.progressTemplate : (error ? self.progressErrorTemplate : t); + $el = $el || self.$progress; + if (!$h.isEmpty(template)) { + if (pctLimit && pct > pctLimit && p <= 100) { + out = template.setTokens({'percent': pctLimit, 'status': self.msgUploadThreshold}); + } else { + out = template.setTokens({'percent': pct, 'status': (p > 100 ? self.msgUploadEnd : pct + '%')}); + } + $el.html(out); + if (error) { + $el.find('[role="progressbar"]').html(error); + } + } + }, + _setFileDropZoneTitle: function () { + var self = this, $zone = self.$container.find('.file-drop-zone'), title = self.dropZoneTitle, strFiles; + if (self.isClickable) { + strFiles = $h.isEmpty(self.$element.attr('multiple')) ? self.fileSingle : self.filePlural; + title += self.dropZoneClickTitle.replace('{files}', strFiles); + } + $zone.find('.' + self.dropZoneTitleClass).remove(); + if (!self.showPreview || $zone.length === 0 || self.getFileStack().length > 0 || !self.dropZoneEnabled || + (!self.isAjaxUpload && self.$element.files)) { + return; + } + if ($zone.find($h.FRAMES).length === 0 && $h.isEmpty(self.defaultPreviewContent)) { + $zone.prepend('
    ' + title + '
    '); + } + self.$container.removeClass('file-input-new'); + $h.addCss(self.$container, 'file-input-ajax-new'); + }, + _setAsyncUploadStatus: function (previewId, pct, total) { + var self = this, sum = 0; + self._setProgress(pct, $('#' + previewId).find('.file-thumb-progress')); + self.uploadStatus[previewId] = pct; + $.each(self.uploadStatus, function (key, value) { + sum += value; + }); + self._setProgress(Math.floor(sum / total)); + }, + _validateMinCount: function () { + var self = this, len = self.isAjaxUpload ? self.getFileStack().length : self._inputFileCount(); + if (self.validateInitialCount && self.minFileCount > 0 && self._getFileCount(len - 1) < self.minFileCount) { + self._noFilesError({}); + return false; + } + return true; + }, + _getFileCount: function (fileCount) { + var self = this, addCount = 0; + if (self.validateInitialCount && !self.overwriteInitial) { + addCount = self.previewCache.count(); + fileCount += addCount; + } + return fileCount; + }, + _getFileId: function (file) { + var self = this, custom = self.generateFileId, relativePath; + if (typeof custom === 'function') { + return custom(file, event); + } + if (!file) { + return null; + } + /** @namespace file.webkitRelativePath */ + relativePath = String(file.webkitRelativePath || file.fileName || file.name || null); + if (!relativePath) { + return null; + } + return (file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')); + }, + _getFileName: function (file) { + return file && file.name ? this.slug(file.name) : undefined; + }, + _getFileIds: function (skipNull) { + var self = this; + return self.fileids.filter(function (n) { + return (skipNull ? n !== undefined : n !== undefined && n !== null); + }); + }, + _getFileNames: function (skipNull) { + var self = this; + return self.filenames.filter(function (n) { + return (skipNull ? n !== undefined : n !== undefined && n !== null); + }); + }, + _setPreviewError: function ($thumb, i, val, repeat) { + var self = this; + if (i !== undefined) { + self.updateStack(i, val); + } + if (!self.showPreview) { + return; + } + if (self.removeFromPreviewOnError && !repeat) { + $thumb.remove(); + return; + } else { + self._setThumbStatus($thumb, 'Error'); + } + self._refreshUploadButton($thumb, repeat); + }, + _refreshUploadButton: function ($thumb, repeat) { + var self = this, $btn = $thumb.find('.kv-file-upload'), cfg = self.fileActionSettings, + icon = cfg.uploadIcon, title = cfg.uploadTitle; + if (!$btn.length) { + return; + } + if (repeat) { + icon = cfg.uploadRetryIcon; + title = cfg.uploadRetryTitle; + } + $btn.attr('title', title).html(icon); + }, + _checkDimensions: function (i, chk, $img, $thumb, fname, type, params) { + var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', limit = self[tag + 'Image' + type], + $imgEl, isValid; + if ($h.isEmpty(limit) || !$img.length) { + return; + } + $imgEl = $img[0]; + dim = (type === 'Width') ? $imgEl.naturalWidth || $imgEl.width : $imgEl.naturalHeight || $imgEl.height; + isValid = chk === 'Small' ? dim >= limit : dim <= limit; + if (isValid) { + return; + } + msg = self['msgImage' + type + chk].setTokens({'name': fname, 'size': limit}); + self._showUploadError(msg, params); + self._setPreviewError($thumb, i, null); + }, + _validateImage: function (previewId, fname, ftype, fsize, iData) { + var self = this, $preview = self.$preview, params, w1, w2, $thumb = $preview.find("#" + previewId), + i = $thumb.attr('data-fileindex'), $img = $thumb.find('img'), exifObject; + fname = fname || 'Untitled'; + $img.one('load', function () { + w1 = $thumb.width(); + w2 = $preview.width(); + if (w1 > w2) { + $img.css('width', '100%'); + } + params = {ind: i, id: previewId}; + self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Width', params); + self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Height', params); + if (!self.resizeImage) { + self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Width', params); + self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params); + } + self._raise('fileimageloaded', [previewId]); + try { + exifObject = window.piexif ? window.piexif.load(iData) : null; + } catch (err) { + exifObject = null; + } + self.loadedImages.push({ + ind: i, + img: $img, + thumb: $thumb, + pid: previewId, + typ: ftype, + siz: fsize, + validated: false, + imgData: iData, + exifObj: exifObject + }); + $thumb.data('exif', exifObject); + self._validateAllImages(); + }).one('error', function () { + self._raise('fileimageloaderror', [previewId]); + }).each(function () { + if (this.complete) { + $(this).trigger('load'); + } else { + if (this.error) { + $(this).trigger('error'); + } + } + }); + }, + _validateAllImages: function () { + var self = this, i, counter = {val: 0}, numImgs = self.loadedImages.length, config, + fsize, minSize = self.resizeIfSizeMoreThan; + if (numImgs !== self.totalImagesCount) { + return; + } + self._raise('fileimagesloaded'); + if (!self.resizeImage) { + return; + } + for (i = 0; i < self.loadedImages.length; i++) { + config = self.loadedImages[i]; + if (config.validated) { + continue; + } + fsize = config.siz; + if (fsize && fsize > minSize * 1000) { + self._getResizedImage(config, counter, numImgs); + } + self.loadedImages[i].validated = true; + } + }, + _getResizedImage: function (config, counter, numImgs) { + var self = this, img = $(config.img)[0], width = img.naturalWidth, height = img.naturalHeight, blob, + ratio = 1, maxWidth = self.maxImageWidth || width, maxHeight = self.maxImageHeight || height, + isValidImage = !!(width && height), chkWidth, chkHeight, canvas = self.imageCanvas, dataURI, + context = self.imageCanvasContext, type = config.typ, pid = config.pid, ind = config.ind, + $thumb = config.thumb, throwError, msg, exifObj = config.exifObj, exifStr; + throwError = function (msg, params, ev) { + if (self.isAjaxUpload) { + self._showUploadError(msg, params, ev); + } else { + self._showError(msg, params, ev); + } + self._setPreviewError($thumb, ind); + }; + if (!self.filestack[ind] || !isValidImage || (width <= maxWidth && height <= maxHeight)) { + if (isValidImage && self.filestack[ind]) { + self._raise('fileimageresized', [pid, ind]); + } + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized'); + } + if (!isValidImage) { + throwError(self.msgImageResizeError, {id: pid, 'index': ind}, 'fileimageresizeerror'); + return; + } + } + type = type || self.resizeDefaultImageType; + chkWidth = width > maxWidth; + chkHeight = height > maxHeight; + if (self.resizePreference === 'width') { + ratio = chkWidth ? maxWidth / width : (chkHeight ? maxHeight / height : 1); + } else { + ratio = chkHeight ? maxHeight / height : (chkWidth ? maxWidth / width : 1); + } + self._resetCanvas(); + width *= ratio; + height *= ratio; + canvas.width = width; + canvas.height = height; + try { + context.drawImage(img, 0, 0, width, height); + dataURI = canvas.toDataURL(type, self.resizeQuality); + if (exifObj) { + exifStr = window.piexif.dump(exifObj); + dataURI = window.piexif.insert(exifStr, dataURI); + } + blob = $h.dataURI2Blob(dataURI); + self.filestack[ind] = blob; + self._raise('fileimageresized', [pid, ind]); + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized', [undefined, undefined]); + } + if (!(blob instanceof Blob)) { + throwError(self.msgImageResizeError, {id: pid, 'index': ind}, 'fileimageresizeerror'); + } + } + catch (err) { + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized', [undefined, undefined]); + } + msg = self.msgImageResizeException.replace('{errors}', err.message); + throwError(msg, {id: pid, 'index': ind}, 'fileimageresizeexception'); + } + }, + _initBrowse: function ($container) { + var self = this, $el = self.$element; + if (self.showBrowse) { + self.$btnFile = $container.find('.btn-file').append($el); + } else { + $el.appendTo($container).attr('tabindex', -1); + $h.addCss($el, 'file-no-browse'); + } + }, + _initClickable: function () { + var self = this, $zone; + if (!self.isClickable) { + return; + } + $zone = self.isAjaxUpload ? self.$dropZone : self.$preview.find('.file-default-preview'); + $h.addCss($zone, 'clickable'); + $zone.attr('tabindex', -1); + self._handler($zone, 'click', function (e) { + var $tar = $(e.target), $el = self.$element; + if (!$(self.elErrorContainer + ':visible').length && + (!$tar.parents('.file-preview-thumbnails').length || $tar.parents('.file-default-preview').length)) { + self.$element.data('zoneClicked', true).trigger('click'); + $zone.blur(); + } + }); + }, + _initCaption: function () { + var self = this, cap = self.initialCaption || ''; + if (self.overwriteInitial || $h.isEmpty(cap)) { + self.$caption.val(''); + return false; + } + self._setCaption(cap); + return true; + }, + _setCaption: function (content, isError) { + var self = this, title, out, icon, n, cap, stack = self.getFileStack(); + if (!self.$caption.length) { + return; + } + self.$captionContainer.removeClass('icon-visible'); + if (isError) { + title = $('
    ' + self.msgValidationError + '
    ').text(); + n = stack.length; + if (n) { + cap = n === 1 && stack[0] ? self._getFileNames()[0] : self._getMsgSelected(n); + } else { + cap = self._getMsgSelected(self.msgNo); + } + out = $h.isEmpty(content) ? cap : content; + icon = '' + self.msgValidationErrorIcon + ''; + } else { + if ($h.isEmpty(content)) { + return; + } + title = $('
    ' + content + '
    ').text(); + out = title; + icon = self._getLayoutTemplate('fileIcon'); + } + self.$captionContainer.addClass('icon-visible'); + self.$caption.attr('title', title).val(out); + self.$captionIcon.html(icon); + }, + _createContainer: function () { + var self = this, attribs = {"class": 'file-input file-input-new' + (self.rtl ? ' kv-rtl' : '')}, + $container = $(document.createElement("div")).attr(attribs).html(self._renderMain()); + $container.insertBefore(self.$element); + self._initBrowse($container); + if (self.theme) { + $container.addClass('theme-' + self.theme); + } + return $container; + }, + _refreshContainer: function () { + var self = this, $container = self.$container, $el = self.$element; + $el.insertAfter($container); + $container.html(self._renderMain()); + self._initBrowse($container); + self._validateDisabled(); + }, + _validateDisabled: function () { + var self = this; + self.$caption.attr({readonly: self.isDisabled}); + }, + _renderMain: function () { + var self = this, + dropCss = self.dropZoneEnabled ? ' file-drop-zone' : 'file-drop-disabled', + close = !self.showClose ? '' : self._getLayoutTemplate('close'), + preview = !self.showPreview ? '' : self._getLayoutTemplate('preview') + .setTokens({'class': self.previewClass, 'dropClass': dropCss}), + css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass, + caption = self.captionTemplate.setTokens({'class': css + ' kv-fileinput-caption'}); + return self.mainTemplate.setTokens({ + 'class': self.mainClass + (!self.showBrowse && self.showCaption ? ' no-browse' : ''), + 'preview': preview, + 'close': close, + 'caption': caption, + 'upload': self._renderButton('upload'), + 'remove': self._renderButton('remove'), + 'cancel': self._renderButton('cancel'), + 'browse': self._renderButton('browse') + }); + + }, + _renderButton: function (type) { + var self = this, tmplt = self._getLayoutTemplate('btnDefault'), css = self[type + 'Class'], + title = self[type + 'Title'], icon = self[type + 'Icon'], label = self[type + 'Label'], + status = self.isDisabled ? ' disabled' : '', btnType = 'button'; + switch (type) { + case 'remove': + if (!self.showRemove) { + return ''; + } + break; + case 'cancel': + if (!self.showCancel) { + return ''; + } + css += ' kv-hidden'; + break; + case 'upload': + if (!self.showUpload) { + return ''; + } + if (self.isAjaxUpload && !self.isDisabled) { + tmplt = self._getLayoutTemplate('btnLink').replace('{href}', self.uploadUrl); + } else { + btnType = 'submit'; + } + break; + case 'browse': + if (!self.showBrowse) { + return ''; + } + tmplt = self._getLayoutTemplate('btnBrowse'); + break; + default: + return ''; + } + + css += type === 'browse' ? ' btn-file' : ' fileinput-' + type + ' fileinput-' + type + '-button'; + if (!$h.isEmpty(label)) { + label = ' ' + label + ''; + } + return tmplt.setTokens({ + 'type': btnType, 'css': css, 'title': title, 'status': status, 'icon': icon, 'label': label + }); + }, + _renderThumbProgress: function () { + var self = this; + return '
    ' + + self.progressTemplate.setTokens({'percent': '0', 'status': self.msgUploadBegin}) + + '
    '; + }, + _renderFileFooter: function (caption, size, width, isError) { + var self = this, config = self.fileActionSettings, rem = config.showRemove, drg = config.showDrag, + upl = config.showUpload, zoom = config.showZoom, out, + template = self._getLayoutTemplate('footer'), tInd = self._getLayoutTemplate('indicator'), + ind = isError ? config.indicatorError : config.indicatorNew, + title = isError ? config.indicatorErrorTitle : config.indicatorNewTitle, + indicator = tInd.setTokens({'indicator': ind, 'indicatorTitle': title}); + size = self._getSize(size); + if (self.isAjaxUpload) { + out = template.setTokens({ + 'actions': self._renderFileActions(upl, false, rem, zoom, drg, false, false, false), + 'caption': caption, + 'size': size, + 'width': width, + 'progress': self._renderThumbProgress(), + 'indicator': indicator + }); + } else { + out = template.setTokens({ + 'actions': self._renderFileActions(false, false, false, zoom, drg, false, false, false), + 'caption': caption, + 'size': size, + 'width': width, + 'progress': '', + 'indicator': indicator + }); + } + out = $h.replaceTags(out, self.previewThumbTags); + return out; + }, + _renderFileActions: function (showUpl, showDwn, showDel, showZoom, showDrag, disabled, url, key, isInit, dUrl, dFile) { + if (!showUpl && !showDwn && !showDel && !showZoom && !showDrag) { + return ''; + } + var self = this, vUrl = url === false ? '' : ' data-url="' + url + '"', + vKey = key === false ? '' : ' data-key="' + key + '"', btnDelete = '', btnUpload = '', btnDownload = '', + btnZoom = '', btnDrag = '', css, template = self._getLayoutTemplate('actions'), + config = self.fileActionSettings, + otherButtons = self.otherActionButtons.setTokens({'dataKey': vKey, 'key': key}), + removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; + if (showDel) { + btnDelete = self._getLayoutTemplate('actionDelete').setTokens({ + 'removeClass': removeClass, + 'removeIcon': config.removeIcon, + 'removeTitle': config.removeTitle, + 'dataUrl': vUrl, + 'dataKey': vKey, + 'key': key + }); + } + if (showUpl) { + btnUpload = self._getLayoutTemplate('actionUpload').setTokens({ + 'uploadClass': config.uploadClass, + 'uploadIcon': config.uploadIcon, + 'uploadTitle': config.uploadTitle + }); + } + if (showDwn) { + btnDownload = self._getLayoutTemplate('actionDownload').setTokens({ + 'downloadClass': config.downloadClass, + 'downloadIcon': config.downloadIcon, + 'downloadTitle': config.downloadTitle, + 'downloadUrl': dUrl || self.initialPreviewDownloadUrl + }); + btnDownload = btnDownload.setTokens({'filename': dFile, 'key': key}); + } + if (showZoom) { + btnZoom = self._getLayoutTemplate('actionZoom').setTokens({ + 'zoomClass': config.zoomClass, + 'zoomIcon': config.zoomIcon, + 'zoomTitle': config.zoomTitle + }); + } + if (showDrag && isInit) { + css = 'drag-handle-init ' + config.dragClass; + btnDrag = self._getLayoutTemplate('actionDrag').setTokens({ + 'dragClass': css, + 'dragTitle': config.dragTitle, + 'dragIcon': config.dragIcon + }); + } + return template.setTokens({ + 'delete': btnDelete, + 'upload': btnUpload, + 'download': btnDownload, + 'zoom': btnZoom, + 'drag': btnDrag, + 'other': otherButtons + }); + }, + _browse: function (e) { + var self = this; + self._raise('filebrowse'); + if (e && e.isDefaultPrevented()) { + return; + } + if (self.isError && !self.isAjaxUpload) { + self.clear(); + } + self.$captionContainer.focus(); + }, + _filterDuplicate: function (file, files, fileIds) { + var self = this, fileId = self._getFileId(file); + + if (fileId && fileIds && fileIds.indexOf(fileId) > -1) { + return; + } + if (!fileIds) { + fileIds = []; + } + files.push(file); + fileIds.push(fileId); + }, + _change: function (e) { + var self = this; + if (self.changeTriggered) { + return; + } + var $el = self.$element, isDragDrop = arguments.length > 1, isAjaxUpload = self.isAjaxUpload, + tfiles = [], files = isDragDrop ? arguments[1] : $el.get(0).files, total, + maxCount = !isAjaxUpload && $h.isEmpty($el.attr('multiple')) ? 1 : self.maxFileCount, + len, ctr = self.filestack.length, isSingleUpload = $h.isEmpty($el.attr('multiple')), + flagSingle = (isSingleUpload && ctr > 0), fileIds = self._getFileIds(), + throwError = function (mesg, file, previewId, index) { + var p1 = $.extend(true, {}, self._getOutData({}, {}, files), {id: previewId, index: index}), + p2 = {id: previewId, index: index, file: file, files: files}; + return isAjaxUpload ? self._showUploadError(mesg, p1) : self._showError(mesg, p2); + }, + maxCountCheck = function (n, m) { + var msg = self.msgFilesTooMany.replace('{m}', m).replace('{n}', n); + self.isError = throwError(msg, null, null, null); + self.$captionContainer.removeClass('icon-visible'); + self._setCaption('', true); + self.$container.removeClass('file-input-new file-input-ajax-new'); + }; + self.reader = null; + self._resetUpload(); + self._hideFileIcon(); + if (self.dropZoneEnabled) { + self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); + } + if (isAjaxUpload) { + $.each(files, function (vKey, vFile) { + self._filterDuplicate(vFile, tfiles, fileIds); + }); + } else { + if (e.target && e.target.files === undefined) { + files = e.target.value ? [{name: e.target.value.replace(/^.+\\/, '')}] : []; + } else { + files = e.target.files || {}; + } + tfiles = files; + } + if ($h.isEmpty(tfiles) || tfiles.length === 0) { + if (!isAjaxUpload) { + self.clear(); + } + self._raise('fileselectnone'); + return; + } + self._resetErrors(); + len = tfiles.length; + total = self._getFileCount(isAjaxUpload ? (self.getFileStack().length + len) : len); + if (maxCount > 0 && total > maxCount) { + if (!self.autoReplace || len > maxCount) { + maxCountCheck((self.autoReplace && len > maxCount ? len : total), maxCount); + return; + } + if (total > maxCount) { + self._resetPreviewThumbs(isAjaxUpload); + } + } else { + if (!isAjaxUpload || flagSingle) { + self._resetPreviewThumbs(false); + if (flagSingle) { + self.clearStack(); + } + } else { + if (isAjaxUpload && ctr === 0 && (!self.previewCache.count() || self.overwriteInitial)) { + self._resetPreviewThumbs(true); + } + } + } + if (self.isPreviewable) { + self.readFiles(tfiles); + } else { + self._updateFileDetails(1); + } + }, + _abort: function (params) { + var self = this, data; + if (self.ajaxAborted && typeof self.ajaxAborted === "object" && self.ajaxAborted.message !== undefined) { + data = $.extend(true, {}, self._getOutData(), params); + data.abortData = self.ajaxAborted.data || {}; + data.abortMessage = self.ajaxAborted.message; + self._setProgress(101, self.$progress, self.msgCancelled); + self._showUploadError(self.ajaxAborted.message, data, 'filecustomerror'); + self.cancel(); + return true; + } + return !!self.ajaxAborted; + }, + _resetFileStack: function () { + var self = this, i = 0, newstack = [], newnames = [], newids = []; + self._getThumbs().each(function () { + var $thumb = $(this), ind = $thumb.attr('data-fileindex'), file = self.filestack[ind], + pid = $thumb.attr('id'); + if (ind === '-1' || ind === -1) { + return; + } + if (file !== undefined) { + newstack[i] = file; + newnames[i] = self._getFileName(file); + newids[i] = self._getFileId(file); + $thumb.attr({'id': self.previewInitId + '-' + i, 'data-fileindex': i}); + i++; + } else { + $thumb.attr({'id': 'uploaded-' + $h.uniqId(), 'data-fileindex': '-1'}); + } + self.$preview.find('#zoom-' + pid).attr({ + 'id': 'zoom-' + $thumb.attr('id'), + 'data-fileindex': $thumb.attr('data-fileindex') + }); + }); + self.filestack = newstack; + self.filenames = newnames; + self.fileids = newids; + }, + _isFileSelectionValid: function (cnt) { + var self = this; + cnt = cnt || 0; + if (self.required && !self.getFilesCount()) { + self.$errorContainer.html(''); + self._showUploadError(self.msgFileRequired); + return false; + } + if (self.minFileCount > 0 && self._getFileCount(cnt) < self.minFileCount) { + self._noFilesError({}); + return false; + } + return true; + }, + clearStack: function () { + var self = this; + self.filestack = []; + self.filenames = []; + self.fileids = []; + return self.$element; + }, + updateStack: function (i, file) { + var self = this; + self.filestack[i] = file; + self.filenames[i] = self._getFileName(file); + self.fileids[i] = file && self._getFileId(file) || null; + return self.$element; + }, + addToStack: function (file) { + var self = this; + self.filestack.push(file); + self.filenames.push(self._getFileName(file)); + self.fileids.push(self._getFileId(file)); + return self.$element; + }, + getFileStack: function (skipNull) { + var self = this; + return self.filestack.filter(function (n) { + return (skipNull ? n !== undefined : n !== undefined && n !== null); + }); + }, + getFilesCount: function () { + var self = this, len = self.isAjaxUpload ? self.getFileStack().length : self._inputFileCount(); + return self._getFileCount(len); + }, + readFiles: function (files) { this.reader = new FileReader(); var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader, $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading, msgProgress = self.msgProgress, previewInitId = self.previewInitId, numFiles = files.length, settings = self.fileTypeSettings, ctr = self.filestack.length, readFile, + fileTypes = self.allowedFileTypes, typLen = fileTypes ? fileTypes.length : 0, + fileExt = self.allowedFileExtensions, strExt = $h.isEmpty(fileExt) ? '' : fileExt.join(', '), maxPreviewSize = self.maxFilePreviewSize && parseFloat(self.maxFilePreviewSize), canPreview = $preview.length && (!maxPreviewSize || isNaN(maxPreviewSize)), throwError = function (msg, file, previewId, index) { - var p1 = $.extend(true, {}, self._getOutData({}, {}, files), { id: previewId, index: index }), - p2 = { id: previewId, index: index, file: file, files: files }; + var p1 = $.extend(true, {}, self._getOutData({}, {}, files), {id: previewId, index: index}), + p2 = {id: previewId, index: index, file: file, files: files}, $thumb; self._previewDefault(file, previewId, true); - if (self.isUploadable) { + if (self.isAjaxUpload) { self.addToStack(undefined); setTimeout(function () { readFile(index + 1); }, 100); + } else { + numFiles = 0; } self._initFileActions(); + $thumb = $('#' + previewId); + $thumb.find('.kv-file-upload').hide(); if (self.removeFromPreviewOnError) { - $('#' + previewId).remove(); + $thumb.remove(); } - return self.isUploadable ? self._showUploadError(msg, p1) : self._showError(msg, p2); + self.isError = self.isAjaxUpload ? self._showUploadError(msg, p1) : self._showError(msg, p2); + self._updateFileDetails(numFiles); }; self.loadedImages = []; self.totalImagesCount = 0; $.each(files, function (key, file) { - var func = self.fileTypeSettings.image || defaultFileTypeSettings.image; + var func = self.fileTypeSettings.image; if (func && func(file.type)) { self.totalImagesCount++; } }); readFile = function (i) { - if (isEmpty($el.attr('multiple'))) { + if ($h.isEmpty($el.attr('multiple'))) { numFiles = 1; } if (i >= numFiles) { - if (self.isUploadable && self.filestack.length > 0) { + if (self.isAjaxUpload && self.filestack.length > 0) { self._raise('filebatchselected', [self.getFileStack()]); } else { self._raise('filebatchselected', [files]); @@ -2201,63 +3773,93 @@ $status.html(''); return; } - var node = ctr + i, previewId = previewInitId + "-" + node, isText, isImage, file = files[i], fSizeKB, - caption = file.name ? self.slug(file.name) : '', fileSize = (file.size || 0) / 1000, checkFile, - fileExtExpr = '', previewData = objUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk, - fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), - fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '); - + var node = ctr + i, previewId = previewInitId + "-" + node, file = files[i], fSizeKB, j, msg, + fnText = settings.text, fnImage = settings.image, fnHtml = settings.html, typ, chk, typ1, typ2, + caption = file && file.name ? self.slug(file.name) : '', fileSize = (file && file.size || 0) / 1000, + fileExtExpr = '', previewData = file ? $h.objUrl.createObjectURL(file) : null, fileCount = 0, strTypes = '', + func, knownTypes = 0, isText, isHtml, isImage, txtFlag, processFileLoaded = function () { + var msg = msgProgress.setTokens({ + 'index': i + 1, + 'files': numFiles, + 'percent': 50, + 'name': caption + }); + setTimeout(function () { + $status.html(msg); + self._updateFileDetails(numFiles); + readFile(i + 1); + }, 100); + self._raise('fileloaded', [file, previewId, i, reader]); + }; + if (!file) { + return; + } + if (typLen > 0) { + for (j = 0; j < typLen; j++) { + typ1 = fileTypes[j]; + typ2 = self.msgFileTypes[typ1] || typ1; + strTypes += j === 0 ? typ2 : ', ' + typ2; + } + } if (caption === false) { readFile(i + 1); return; } if (caption.length === 0) { - msg = self.msgInvalidFileName.replace('{name}', htmlEncode(file.name)); - self.isError = throwError(msg, file, previewId, i); + msg = self.msgInvalidFileName.replace('{name}', $h.htmlEncode(file.name)); + throwError(msg, file, previewId, i); return; } - if (!isEmpty(fileExt)) { + if (!$h.isEmpty(fileExt)) { fileExtExpr = new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); } fSizeKB = fileSize.toFixed(2); if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { - msg = self.msgSizeTooLarge.replace('{name}', caption).replace('{size}', fSizeKB) - .replace('{maxSize}', self.maxFileSize); - self.isError = throwError(msg, file, previewId, i); + msg = self.msgSizeTooLarge.setTokens({ + 'name': caption, + 'size': fSizeKB, + 'maxSize': self.maxFileSize + }); + throwError(msg, file, previewId, i); return; } - if (self.minFileSize !== null && fileSize <= getNum(self.minFileSize)) { - msg = self.msgSizeTooSmall.replace('{name}', caption).replace('{size}', fSizeKB) - .replace('{minSize}', self.minFileSize); - self.isError = throwError(msg, file, previewId, i); + if (self.minFileSize !== null && fileSize <= $h.getNum(self.minFileSize)) { + msg = self.msgSizeTooSmall.setTokens({ + 'name': caption, + 'size': fSizeKB, + 'minSize': self.minFileSize + }); + throwError(msg, file, previewId, i); return; } - if (!isEmpty(fileTypes) && isArray(fileTypes)) { + if (!$h.isEmpty(fileTypes) && $h.isArray(fileTypes)) { for (j = 0; j < fileTypes.length; j += 1) { typ = fileTypes[j]; - checkFile = settings[typ]; - chk = (checkFile !== undefined && checkFile(file.type, caption)); - fileCount += isEmpty(chk) ? 0 : chk.length; + func = settings[typ]; + fileCount += !func || (typeof func !== 'function') ? 0 : (func(file.type, file.name) ? 1 : 0); } if (fileCount === 0) { - msg = self.msgInvalidFileType.replace('{name}', caption).replace('{types}', strTypes); - self.isError = throwError(msg, file, previewId, i); + msg = self.msgInvalidFileType.setTokens({'name': caption, 'types': strTypes}); + throwError(msg, file, previewId, i); return; } } - if (fileCount === 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) { - chk = compare(caption, fileExtExpr); - fileCount += isEmpty(chk) ? 0 : chk.length; + if (fileCount === 0 && !$h.isEmpty(fileExt) && $h.isArray(fileExt) && !$h.isEmpty(fileExtExpr)) { + chk = $h.compare(caption, fileExtExpr); + fileCount += $h.isEmpty(chk) ? 0 : chk.length; if (fileCount === 0) { - msg = self.msgInvalidFileExtension.replace('{name}', caption).replace('{extensions}', strExt); - self.isError = throwError(msg, file, previewId, i); + msg = self.msgInvalidFileExtension.setTokens({'name': caption, 'extensions': strExt}); + throwError(msg, file, previewId, i); return; } } if (!self.showPreview) { - self.addToStack(file); + if (self.isAjaxUpload) { + self.addToStack(file); + } setTimeout(function () { readFile(i + 1); + self._updateFileDetails(numFiles); }, 100); self._raise('fileloaded', [file, previewId, i, reader]); return; @@ -2272,42 +3874,82 @@ return; } if ($preview.length && FileReader !== undefined) { + isText = fnText(file.type, caption); + isHtml = fnHtml(file.type, caption); + isImage = fnImage(file.type, caption); $status.html(msgLoading.replace('{index}', i + 1).replace('{files}', numFiles)); $container.addClass('file-thumb-loading'); reader.onerror = function (evt) { self._errorHandler(evt, caption); }; reader.onload = function (theFile) { - self._previewFile(i, file, theFile, previewId, previewData); + var hex, fileInfo, uint, byte, bytes = [], contents, mime, readTextImage = function (textFlag) { + var newReader = new FileReader(); + newReader.onerror = function (theFileNew) { + self._errorHandler(theFileNew, caption); + }; + newReader.onload = function (theFileNew) { + self._previewFile(i, file, theFileNew, previewId, previewData, fileInfo); + self._initFileActions(); + processFileLoaded(); + }; + if (textFlag) { + newReader.readAsText(file, self.textEncoding); + } else { + newReader.readAsDataURL(file); + } + }; + fileInfo = {'name': caption, 'type': file.type}; + $.each(settings, function (key, func) { + if (key !== 'object' && key !== 'other' && func(file.type, caption)) { + knownTypes++; + } + }); + if (knownTypes === 0) {// auto detect mime types from content if no known file types detected + uint = new Uint8Array(theFile.target.result); + for (j = 0; j < uint.length; j++) { + byte = uint[j].toString(16); + bytes.push(byte); + } + hex = bytes.join('').toLowerCase().substring(0, 8); + mime = $h.getMimeType(hex, '', ''); + if ($h.isEmpty(mime)) { // look for ascii text content + contents = $h.arrayBuffer2String(reader.result); + mime = $h.isSvg(contents) ? 'image/svg+xml' : $h.getMimeType(hex, contents, file.type); + } + fileInfo = {'name': caption, 'type': mime}; + isText = fnText(mime, ''); + isHtml = fnHtml(mime, ''); + isImage = fnImage(mime, ''); + txtFlag = isText || isHtml; + if (txtFlag || isImage) { + readTextImage(txtFlag); + return; + } + } + self._previewFile(i, file, theFile, previewId, previewData, fileInfo); self._initFileActions(); - }; - reader.onloadend = function () { - msg = msgProgress.replace('{index}', i + 1).replace('{files}', numFiles) - .replace('{percent}', 50).replace('{name}', caption); - setTimeout(function () { - $status.html(msg); - self._updateFileDetails(numFiles); - readFile(i + 1); - }, 100); - self._raise('fileloaded', [file, previewId, i, reader]); + processFileLoaded(); }; reader.onprogress = function (data) { if (data.lengthComputable) { var fact = (data.loaded / data.total) * 100, progress = Math.ceil(fact); - msg = msgProgress.replace('{index}', i + 1).replace('{files}', numFiles) - .replace('{percent}', progress).replace('{name}', caption); + msg = msgProgress.setTokens({ + 'index': i + 1, + 'files': numFiles, + 'percent': progress, + 'name': caption + }); setTimeout(function () { $status.html(msg); }, 100); } }; - isText = ifSet('text', settings, defaultFileTypeSettings.text); - isImage = ifSet('image', settings, defaultFileTypeSettings.image); - if (isText(file.type, caption)) { + if (isText || isHtml) { reader.readAsText(file, self.textEncoding); } else { - if (isImage(file.type, caption)) { + if (isImage) { reader.readAsDataURL(file); } else { reader.readAsArrayBuffer(file); @@ -2327,612 +3969,15 @@ readFile(0); self._updateFileDetails(numFiles, false); }, - _updateFileDetails: function (numFiles) { - var self = this, $el = self.$element, fileStack = self.getFileStack(), - name = (isIE(9) && findFileName($el.val())) || - ($el[0].files[0] && $el[0].files[0].name) || (fileStack.length && fileStack[0].name) || '', - label = self.slug(name), n = self.isUploadable ? fileStack.length : numFiles, - nFiles = previewCache.count(self.id) + n, log = n > 1 ? self._getMsgSelected(nFiles) : label; - if (self.isError) { - self.$previewContainer.removeClass('file-thumb-loading'); - self.$previewStatus.html(''); - self.$captionContainer.find('.kv-caption-icon').hide(); - } else { - self._showFileIcon(); - } - self._setCaption(log, self.isError); - self.$container.removeClass('file-input-new file-input-ajax-new'); - if (arguments.length === 1) { - self._raise('fileselect', [numFiles, label]); - } - if (previewCache.count(self.id)) { - self._initPreviewActions(); - } - }, - _setThumbStatus: function ($thumb, status) { - var self = this; - if (!self.showPreview) { - return; - } - var icon = 'indicator' + status, msg = icon + 'Title', - css = 'file-preview-' + status.toLowerCase(), - $indicator = $thumb.find('.file-upload-indicator'), - config = self.fileActionSettings; - $thumb.removeClass('file-preview-success file-preview-error file-preview-loading'); - if (status === 'Error') { - $thumb.find('.kv-file-upload').attr('disabled', true); - } - if (status === 'Success') { - $thumb.find('.file-drag-handle').remove(); - $indicator.css('margin-left', 0); - } - $indicator.html(config[icon]); - $indicator.attr('title', config[msg]); - $thumb.addClass(css); - }, - _setProgressCancelled: function () { - var self = this; - self._setProgress(101, self.$progress, self.msgCancelled); - }, - _setProgress: function (p, $el, error) { - var self = this, pct = Math.min(p, 100), template = pct < 100 ? self.progressTemplate : - (error ? self.progressErrorTemplate : (p <= 100 ? self.progressTemplate : self.progressCompleteTemplate)), - pctLimit = self.progressUploadThreshold; - $el = $el || self.$progress; - if (!isEmpty(template)) { - if (pctLimit && pct > pctLimit && p <= 100) { - var out = template.replace('{percent}', pctLimit).replace('{percent}', pctLimit).replace('{percent}%', self.msgUploadThreshold); - $el.html(out); - } else { - $el.html(template.replace(/\{percent}/g, pct)); - } - if (error) { - $el.find('[role="progressbar"]').html(error); - } - } - }, - _setFileDropZoneTitle: function () { - var self = this, $zone = self.$container.find('.file-drop-zone'), title = self.dropZoneTitle, strFiles; - if (self.isClickable) { - strFiles = isEmpty(self.$element.attr('multiple')) ? self.fileSingle : self.filePlural; - title += self.dropZoneClickTitle.replace('{files}', strFiles); - } - $zone.find('.' + self.dropZoneTitleClass).remove(); - if (!self.isUploadable || !self.showPreview || $zone.length === 0 || self.getFileStack().length > 0 || !self.dropZoneEnabled) { - return; - } - if ($zone.find('.file-preview-frame').length === 0 && isEmpty(self.defaultPreviewContent)) { - $zone.prepend('
    ' + title + '
    '); - } - self.$container.removeClass('file-input-new'); - addCss(self.$container, 'file-input-ajax-new'); - }, - _setAsyncUploadStatus: function (previewId, pct, total) { - var self = this, sum = 0; - self._setProgress(pct, $('#' + previewId).find('.file-thumb-progress')); - self.uploadStatus[previewId] = pct; - $.each(self.uploadStatus, function (key, value) { - sum += value; - }); - self._setProgress(Math.floor(sum / total)); - - }, - _validateMinCount: function () { - var self = this, len = self.isUploadable ? self.getFileStack().length : self.$element.get(0).files.length; - if (self.validateInitialCount && self.minFileCount > 0 && self._getFileCount(len - 1) < self.minFileCount) { - self._noFilesError({}); - return false; - } - return true; - }, - _getFileCount: function (fileCount) { - var self = this, addCount = 0; - if (self.validateInitialCount && !self.overwriteInitial) { - addCount = previewCache.count(self.id); - fileCount += addCount; - } - return fileCount; - }, - _getFileName: function (file) { - return file && file.name ? this.slug(file.name) : undefined; - }, - _getFileNames: function (skipNull) { - var self = this; - return self.filenames.filter(function (n) { - return (skipNull ? n !== undefined : n !== undefined && n !== null); - }); - }, - _setPreviewError: function ($thumb, i, val) { - var self = this; - if (i !== undefined) { - self.updateStack(i, val); - } - if (self.removeFromPreviewOnError) { - $thumb.remove(); - } else { - self._setThumbStatus($thumb, 'Error'); - } - }, - _checkDimensions: function (i, chk, $img, $thumb, fname, type, params) { - var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', limit = self[tag + 'Image' + type], - $imgEl, isValid; - if (isEmpty(limit) || !$img.length) { - return; - } - $imgEl = $img[0]; - dim = (type === 'Width') ? $imgEl.naturalWidth || $imgEl.width : $imgEl.naturalHeight || $imgEl.height; - isValid = chk === 'Small' ? dim >= limit : dim <= limit; - if (isValid) { - return; - } - msg = self['msgImage' + type + chk].replace('{name}', fname).replace('{size}', limit); - self._showUploadError(msg, params); - self._setPreviewError($thumb, i, null); - }, - _validateImage: function (i, previewId, fname, ftype) { - var self = this, $preview = self.$preview, params, w1, w2, - $thumb = $preview.find("#" + previewId), $img = $thumb.find('img'); - fname = fname || 'Untitled'; - if (!$img.length) { - return; - } - handler($img, 'load', function () { - w1 = $thumb.width(); - w2 = $preview.width(); - if (w1 > w2) { - $img.css('width', '100%'); - $thumb.css('width', '97%'); - } - params = { ind: i, id: previewId }; - self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Width', params); - self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Height', params); - if (!self.resizeImage) { - self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Width', params); - self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params); - } - self._raise('fileimageloaded', [previewId]); - self.loadedImages.push({ ind: i, img: $img, thumb: $thumb, pid: previewId, typ: ftype }); - self._validateAllImages(); - }); - }, - _validateAllImages: function () { - var self = this, i, config, $img, $thumb, pid, ind, params = {}, errFunc; - if (self.loadedImages.length !== self.totalImagesCount) { - return; - } - self._raise('fileimagesloaded'); - if (!self.resizeImage) { - return; - } - errFunc = self.isUploadable ? self._showUploadError : self._showError; - var counter = { val: 0 }; - for (i = 0; i < self.loadedImages.length; i++) { - config = self.loadedImages[i]; - $img = config.img; - $thumb = config.thumb; - pid = config.pid; - ind = config.ind; - params = { id: pid, 'index': ind }; - if (!self._getResizedImage($img[0], config.typ, pid, ind, counter, self.loadedImages.length)) { - errFunc(self.msgImageResizeError, params, 'fileimageresizeerror'); - self._setPreviewError($thumb, ind); - } - } - }, - _getResizedImage: function (image, type, pid, ind, counter, num_imgs) { - var self = this, width = image.naturalWidth, height = image.naturalHeight, ratio = 1, - maxWidth = self.maxImageWidth || width, maxHeight = self.maxImageHeight || height, - isValidImage = (width && height), chkWidth, chkHeight, - canvas = self.imageCanvas, context = self.imageCanvasContext; - if (!isValidImage) { - counter.val++; - if (counter.val === num_imgs) { - self._raise('fileimagesresized'); - } - return false; - } - if (width === maxWidth && height === maxHeight) { - self._raise('fileimageresized', [pid, ind]); - counter.val++; - if (counter.val === num_imgs) { - self._raise('fileimagesresized'); - } - return true; - } - type = type || self.resizeDefaultImageType; - chkWidth = width > maxWidth; - chkHeight = height > maxHeight; - if (self.resizePreference === 'width') { - ratio = chkWidth ? maxWidth / width : (chkHeight ? maxHeight / height : 1); - } else { - ratio = chkHeight ? maxHeight / height : (chkWidth ? maxWidth / width : 1); - } - self._resetCanvas(); - width *= ratio; - height *= ratio; - canvas.width = width; - canvas.height = height; - try { - context.drawImage(image, 0, 0, width, height); - canvas.toBlob(function (blob) { - self.filestack[ind] = blob; - self._raise('fileimageresized', [pid, ind]); - counter.val++; - if (counter.val === num_imgs) { - self._raise('fileimagesresized', [undefined, undefined]); - } - }, type, self.resizeQuality); - return true; - } - catch (err) { - counter.val++; - if (counter.val === num_imgs) { - self._raise('fileimagesresized', [undefined, undefined]); - } - return false; - } - }, - _initBrowse: function ($container) { - var self = this; - if (self.showBrowse) { - self.$btnFile = $container.find('.btn-file'); - self.$btnFile.append(self.$element); - } else { - self.$element.hide(); - } - }, - _initCaption: function () { - var self = this, cap = self.initialCaption || ''; - if (self.overwriteInitial || isEmpty(cap)) { - self.$caption.html(''); - return false; - } - self._setCaption(cap); - return true; - }, - _setCaption: function (content, isError) { - var self = this, title, out, n, cap, stack = self.getFileStack(); - if (!self.$caption.length) { - return; - } - if (isError) { - title = $('
    ' + self.msgValidationError + '
    ').text(); - n = stack.length; - if (n) { - cap = n === 1 && stack[0] ? self._getFileNames()[0] : self._getMsgSelected(n); - } else { - cap = self._getMsgSelected(self.msgNo); - } - out = '' + self.msgValidationErrorIcon + - (isEmpty(content) ? cap : content) + ''; - } else { - if (isEmpty(content)) { - return; - } - title = $('
    ' + content + '
    ').text(); - out = self._getLayoutTemplate('fileIcon') + title; - } - self.$caption.html(out); - self.$caption.attr('title', title); - self.$captionContainer.find('.file-caption-ellipsis').attr('title', title); - }, - _createContainer: function () { - var self = this, $container = $(document.createElement("div")) - .attr({ "class": 'file-input file-input-new' }) - .html(self._renderMain()); - self.$element.before($container); - self._initBrowse($container); - if (self.theme) { - $container.addClass('theme-' + self.theme); - } - return $container; - }, - _refreshContainer: function () { - var self = this, $container = self.$container; - $container.before(self.$element); - $container.html(self._renderMain()); - self._initBrowse($container); - }, - _renderMain: function () { - var self = this, dropCss = (self.isUploadable && self.dropZoneEnabled) ? ' file-drop-zone' : 'file-drop-disabled', - close = !self.showClose ? '' : self._getLayoutTemplate('close'), - preview = !self.showPreview ? '' : self._getLayoutTemplate('preview') - .replace(/\{class}/g, self.previewClass) - .replace(/\{dropClass}/g, dropCss), - css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass, - caption = self.captionTemplate.replace(/\{class}/g, css + ' kv-fileinput-caption'); - return self.mainTemplate.replace(/\{class}/g, self.mainClass + - (!self.showBrowse && self.showCaption ? ' no-browse' : '')) - .replace(/\{preview}/g, preview) - .replace(/\{close}/g, close) - .replace(/\{caption}/g, caption) - .replace(/\{upload}/g, self._renderButton('upload')) - .replace(/\{remove}/g, self._renderButton('remove')) - .replace(/\{cancel}/g, self._renderButton('cancel')) - .replace(/\{browse}/g, self._renderButton('browse')); - }, - _renderButton: function (type) { - var self = this, tmplt = self._getLayoutTemplate('btnDefault'), css = self[type + 'Class'], - title = self[type + 'Title'], icon = self[type + 'Icon'], label = self[type + 'Label'], - status = self.isDisabled ? ' disabled' : '', btnType = 'button'; - switch (type) { - case 'remove': - if (!self.showRemove) { - return ''; - } - break; - case 'cancel': - if (!self.showCancel) { - return ''; - } - css += ' hide'; - break; - case 'upload': - if (!self.showUpload) { - return ''; - } - if (self.isUploadable && !self.isDisabled) { - tmplt = self._getLayoutTemplate('btnLink').replace('{href}', self.uploadUrl); - } else { - btnType = 'submit'; - } - break; - case 'browse': - if (!self.showBrowse) { - return ''; - } - tmplt = self._getLayoutTemplate('btnBrowse'); - break; - default: - return ''; - } - - css += type === 'browse' ? ' btn-file' : ' fileinput-' + type + ' fileinput-' + type + '-button'; - if (!isEmpty(label)) { - label = ' ' + label + ''; - } - return tmplt.replace('{type}', btnType).replace('{css}', css).replace('{title}', title) - .replace('{status}', status).replace('{icon}', icon).replace('{label}', label); - }, - _renderThumbProgress: function () { - return '
    ' + this.progressTemplate.replace(/\{percent}/g, - '0') + '
    '; - }, - _renderFileFooter: function (caption, size, width, isError) { - var self = this, config = self.fileActionSettings, rem = config.showRemove, drg = config.showDrag, - upl = config.showUpload, zoom = config.showZoom, out, template = self._getLayoutTemplate('footer'), - indicator = isError ? config.indicatorError : config.indicatorNew, - title = isError ? config.indicatorErrorTitle : config.indicatorNewTitle; - size = self._getSize(size); - if (self.isUploadable) { - out = template.replace(/\{actions}/g, self._renderFileActions(upl, rem, zoom, drg, false, false, false)) - .replace(/\{caption}/g, caption).replace(/\{size}/g, size).replace(/\{width}/g, width) - .replace(/\{progress}/g, self._renderThumbProgress()).replace(/\{indicator}/g, indicator) - .replace(/\{indicatorTitle}/g, title); - } else { - out = template.replace(/\{actions}/g, - self._renderFileActions(false, false, zoom, drg, false, false, false)) - .replace(/\{caption}/g, caption).replace(/\{size}/g, size).replace(/\{width}/g, width) - .replace(/\{progress}/g, '').replace(/\{indicator}/g, indicator) - .replace(/\{indicatorTitle}/g, title); - } - out = replaceTags(out, self.previewThumbTags); - return out; - }, - _renderFileActions: function (showUpload, showDelete, showZoom, showDrag, disabled, url, key, isInit) { - if (!showUpload && !showDelete && !showZoom && !showDrag) { - return ''; - } - var self = this, - vUrl = url === false ? '' : ' data-url="' + url + '"', - vKey = key === false ? '' : ' data-key="' + key + '"', - btnDelete = '', btnUpload = '', btnZoom = '', btnDrag = '', css, - template = self._getLayoutTemplate('actions'), config = self.fileActionSettings, - otherButtons = self.otherActionButtons.replace(/\{dataKey}/g, vKey), - removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; - if (showDelete) { - btnDelete = self._getLayoutTemplate('actionDelete') - .replace(/\{removeClass}/g, removeClass) - .replace(/\{removeIcon}/g, config.removeIcon) - .replace(/\{removeTitle}/g, config.removeTitle) - .replace(/\{dataUrl}/g, vUrl) - .replace(/\{dataKey}/g, vKey); - } - if (showUpload) { - btnUpload = self._getLayoutTemplate('actionUpload') - .replace(/\{uploadClass}/g, config.uploadClass) - .replace(/\{uploadIcon}/g, config.uploadIcon) - .replace(/\{uploadTitle}/g, config.uploadTitle); - } - if (showZoom) { - btnZoom = self._getLayoutTemplate('actionZoom') - .replace(/\{zoomClass}/g, config.zoomClass) - .replace(/\{zoomIcon}/g, config.zoomIcon) - .replace(/\{zoomTitle}/g, config.zoomTitle); - } - if (showDrag && isInit) { - css = 'drag-handle-init ' + config.dragClass; - btnDrag = self._getLayoutTemplate('actionDrag').replace(/\{dragClass}/g, css) - .replace(/\{dragTitle}/g, config.dragTitle) - .replace(/\{dragIcon}/g, config.dragIcon); - } - return template.replace(/\{delete}/g, btnDelete) - .replace(/\{upload}/g, btnUpload) - .replace(/\{zoom}/g, btnZoom) - .replace(/\{drag}/g, btnDrag) - .replace(/\{other}/g, otherButtons); - }, - _browse: function (e) { - var self = this; - self._raise('filebrowse'); - if (e && e.isDefaultPrevented()) { - return; - } - if (self.isError && !self.isUploadable) { - self.clear(); - } - self.$captionContainer.focus(); - }, - _change: function (e) { - var self = this, $el = self.$element; - if (!self.isUploadable && isEmpty($el.val()) && self.fileInputCleared) { // IE 11 fix - self.fileInputCleared = false; - return; - } - self.fileInputCleared = false; - var tfiles, msg, total, isDragDrop = arguments.length > 1, isAjaxUpload = self.isUploadable, i = 0, f, n, len, - files = isDragDrop ? e.originalEvent.dataTransfer.files : $el.get(0).files, ctr = self.filestack.length, - isSingleUpload = isEmpty($el.attr('multiple')), flagSingle = (isSingleUpload && ctr > 0), folders = 0, - throwError = function (mesg, file, previewId, index) { - var p1 = $.extend(true, {}, self._getOutData({}, {}, files), { id: previewId, index: index }), - p2 = { id: previewId, index: index, file: file, files: files }; - return self.isUploadable ? self._showUploadError(mesg, p1) : self._showError(mesg, p2); - }; - self.reader = null; - self._resetUpload(); - self._hideFileIcon(); - if (self.isUploadable) { - self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); - } - if (isDragDrop) { - tfiles = []; - while (files[i]) { - f = files[i]; - if (!f.type && f.size % 4096 === 0) { - folders++; - } else { - tfiles.push(f); - } - i++; - } - } else { - if (e.target.files === undefined) { - tfiles = e.target && e.target.value ? [ - { name: e.target.value.replace(/^.+\\/, '') } - ] : []; - } else { - tfiles = e.target.files; - } - } - if (isEmpty(tfiles) || tfiles.length === 0) { - if (!isAjaxUpload) { - self.clear(); - } - self._showFolderError(folders); - self._raise('fileselectnone'); - return; - } - self._resetErrors(); - len = tfiles.length; - total = self._getFileCount(self.isUploadable ? (self.getFileStack().length + len) : len); - if (self.maxFileCount > 0 && total > self.maxFileCount) { - if (!self.autoReplace || len > self.maxFileCount) { - n = (self.autoReplace && len > self.maxFileCount) ? len : total; - msg = self.msgFilesTooMany.replace('{m}', self.maxFileCount).replace('{n}', n); - self.isError = throwError(msg, null, null, null); - self.$captionContainer.find('.kv-caption-icon').hide(); - self._setCaption('', true); - self.$container.removeClass('file-input-new file-input-ajax-new'); - return; - } - if (total > self.maxFileCount) { - self._resetPreviewThumbs(isAjaxUpload); - } - } else { - if (!isAjaxUpload || flagSingle) { - self._resetPreviewThumbs(false); - if (flagSingle) { - self.clearStack(); - } - } else { - if (isAjaxUpload && ctr === 0 && (!previewCache.count(self.id) || self.overwriteInitial)) { - self._resetPreviewThumbs(true); - } - } - } - if (self.isPreviewable) { - self._readFiles(tfiles); - } else { - self._updateFileDetails(1); - } - self._showFolderError(folders); - }, - _abort: function (params) { - var self = this, data; - if (self.ajaxAborted && typeof self.ajaxAborted === "object" && self.ajaxAborted.message !== undefined) { - data = $.extend(true, {}, self._getOutData(), params); - data.abortData = self.ajaxAborted.data || {}; - data.abortMessage = self.ajaxAborted.message; - self.cancel(); - self._setProgress(101, self.$progress, self.msgCancelled); - self._showUploadError(self.ajaxAborted.message, data, 'filecustomerror'); - return true; - } - return false; - }, - _resetFileStack: function () { - var self = this, i = 0, newstack = [], newnames = []; - self._getThumbs().each(function () { - var $thumb = $(this), ind = $thumb.attr('data-fileindex'), - file = self.filestack[ind]; - if (ind === -1) { - return; - } - if (file !== undefined) { - newstack[i] = file; - newnames[i] = self._getFileName(file); - $thumb.attr({ - 'id': self.previewInitId + '-' + i, - 'data-fileindex': i - }); - i++; - } else { - $thumb.attr({ - 'id': 'uploaded-' + uniqId(), - 'data-fileindex': '-1' - }); - } - }); - self.filestack = newstack; - self.filenames = newnames; - }, - clearStack: function () { - var self = this; - self.filestack = []; - self.filenames = []; - return self.$element; - }, - updateStack: function (i, file) { - var self = this; - self.filestack[i] = file; - self.filenames[i] = self._getFileName(file); - return self.$element; - }, - addToStack: function (file) { - var self = this; - self.filestack.push(file); - self.filenames.push(self._getFileName(file)); - return self.$element; - }, - getFileStack: function (skipNull) { - var self = this; - return self.filestack.filter(function (n) { - return (skipNull ? n !== undefined : n !== undefined && n !== null); - }); - }, - getFilesCount: function () { - var self = this, len = self.isUploadable ? self.getFileStack().length : self.$element.get(0).files.length; - return self._getFileCount(len); - }, lock: function () { var self = this; self._resetErrors(); self.disable(); if (self.showRemove) { - addCss(self.$container.find('.fileinput-remove'), 'hide'); + self.$container.find('.fileinput-remove').hide(); } if (self.showCancel) { - self.$container.find('.fileinput-cancel').removeClass('hide'); + self.$container.find('.fileinput-cancel').show(); } self._raise('filelock', [self.filestack, self._getExtraData()]); return self.$element; @@ -2944,10 +3989,10 @@ } self.enable(); if (self.showCancel) { - addCss(self.$container.find('.fileinput-cancel'), 'hide'); + self.$container.find('.fileinput-cancel').hide(); } if (self.showRemove) { - self.$container.find('.fileinput-remove').removeClass('hide'); + self.$container.find('.fileinput-remove').show(); } if (reset) { self._resetFileStack(); @@ -2977,15 +4022,17 @@ }, clear: function () { var self = this, cap; + if (!self._raise('fileclear')) { + return; + } self.$btnUpload.removeAttr('disabled'); self._getThumbs().find('video,audio,img').each(function () { - cleanMemory($(this)); + $h.cleanMemory($(this)); }); + self._clearFileInput(); self._resetUpload(); self.clearStack(); - self._clearFileInput(); self._resetErrors(true); - self._raise('fileclear'); if (self._hasInitialPreview()) { self._showFileIcon(); self._resetPreview(); @@ -2995,19 +4042,18 @@ self._getThumbs().each(function () { self._clearObjects($(this)); }); - if (self.isUploadable) { - previewCache.data[self.id] = {}; + if (self.isAjaxUpload) { + self.previewCache.data = {}; } self.$preview.html(''); cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? self.initialCaption : ''; - self.$caption.html(cap); - self.$caption.attr('title', ''); - addCss(self.$container, 'file-input-new'); + self.$caption.attr('title', '').val(cap); + $h.addCss(self.$container, 'file-input-new'); self._validateDefaultPreview(); } - if (self.$container.find('.file-preview-frame').length === 0) { + if (self.$container.find($h.FRAMES).length === 0) { if (!self._initCaption()) { - self.$captionContainer.find('.kv-caption-icon').hide(); + self.$captionContainer.removeClass('icon-visible'); } } self._hideFileIcon(); @@ -3018,16 +4064,18 @@ }, reset: function () { var self = this; + if (!self._raise('filereset')) { + return; + } self._resetPreview(); self.$container.find('.fileinput-filename').text(''); - self._raise('filereset'); - addCss(self.$container, 'file-input-new'); - if (self.$preview.find('.file-preview-frame').length || self.isUploadable && self.dropZoneEnabled) { + $h.addCss(self.$container, 'file-input-new'); + if (self.getFrames().length || self.dropZoneEnabled) { self.$container.removeClass('file-input-new'); } - self._setFileDropZoneTitle(); self.clearStack(); self.formdata = {}; + self._setFileDropZoneTitle(); return self.$element; }, disable: function () { @@ -3036,9 +4084,9 @@ self._raise('filedisabled'); self.$element.attr('disabled', 'disabled'); self.$container.find(".kv-fileinput-caption").addClass("file-caption-disabled"); - self.$container.find(".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").attr( - "disabled", - true); + self.$container.find(".fileinput-remove, .fileinput-upload, .file-preview-frame button") + .attr("disabled", true); + $h.addCss(self.$container.find('.btn-file'), 'disabled'); self._initDragDrop(); return self.$element; }, @@ -3048,23 +4096,24 @@ self._raise('fileenabled'); self.$element.removeAttr('disabled'); self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled"); - self.$container.find( - ".btn-file, .fileinput-remove, .fileinput-upload, .file-preview-frame button").removeAttr("disabled"); + self.$container.find(".fileinput-remove, .fileinput-upload, .file-preview-frame button") + .removeAttr("disabled"); + self.$container.find('.btn-file').removeClass('disabled'); self._initDragDrop(); return self.$element; }, upload: function () { - var self = this, totLen = self.getFileStack().length, params = {}, - i, outData, len, hasExtraData = !$.isEmptyObject(self._getExtraData()); - if (self.minFileCount > 0 && self._getFileCount(totLen) < self.minFileCount) { - self._noFilesError(params); - return; - } - if (!self.isUploadable || self.isDisabled || (totLen === 0 && !hasExtraData)) { + var self = this, totLen = self.getFileStack().length, i, outData, len, + hasExtraData = !$.isEmptyObject(self._getExtraData()); + if (!self.isAjaxUpload || self.isDisabled || !self._isFileSelectionValid(totLen)) { return; } self._resetUpload(); - self.$progress.removeClass('hide'); + if (totLen === 0 && !hasExtraData) { + self._showUploadError(self.msgUploadEmpty); + return; + } + self.$progress.show(); self.uploadCount = 0; self.uploadStatus = {}; self.uploadLog = []; @@ -3080,16 +4129,20 @@ outData = self._getOutData(); self._raise('filebatchpreupload', [outData]); self.fileBatchCompleted = false; - self.uploadCache = { content: [], config: [], tags: [], append: true }; + self.uploadCache = {content: [], config: [], tags: [], append: true}; self.uploadAsyncCount = self.getFileStack().length; for (i = 0; i < len; i++) { self.uploadCache.content[i] = null; self.uploadCache.config[i] = null; self.uploadCache.tags[i] = null; } + self.$preview.find('.file-preview-initial').removeClass($h.SORT_CSS); + self._initSortable(); + self.cacheInitialPreview = self.getPreview(); + for (i = 0; i < len; i++) { - if (self.filestack[i] !== undefined) { - self._uploadSingle(i, self.filestack, true); + if (self.filestack[i]) { + self._uploadSingle(i, true); } } return; @@ -3098,26 +4151,68 @@ return self.$element; }, destroy: function () { - var self = this, $cont = self.$container; - $cont.find('.file-drop-zone').off(); - self.$element.insertBefore($cont).off(NAMESPACE).removeData(); + var self = this, $form = self.$form, $cont = self.$container, $el = self.$element, ns = self.namespace; + $(document).off(ns); + $(window).off(ns); + if ($form && $form.length) { + $form.off(ns); + } + if (self.isAjaxUpload) { + self._clearFileInput(); + } + self._cleanup(); + self._initPreviewCache(); + $el.insertBefore($cont).off(ns).removeData(); $cont.off().remove(); - return self.$element; + return $el; }, refresh: function (options) { var self = this, $el = self.$element; - options = options ? $.extend(true, {}, self.options, options) : self.options; - self.destroy(); - $el.fileinput(options); - if ($el.val()) { - $el.trigger('change.fileinput'); + if (typeof options !== 'object' || $h.isEmpty(options)) { + options = self.options; + } else { + options = $.extend(true, {}, self.options, options); } + self._init(options, true); + self._listen(); return $el; + }, + zoom: function (frameId) { + var self = this, $frame = self._getFrame(frameId), $modal = self.$modal; + if (!$frame) { + return; + } + $h.initModal($modal); + $modal.html(self._getModalContent()); + self._setZoomContent($frame); + $modal.modal('show'); + self._initZoomButtons(); + }, + getExif: function (frameId) { + var self = this, $frame = self._getFrame(frameId); + return $frame && $frame.data('exif') || null; + }, + getFrames: function (cssFilter) { + var self = this, $frames; + cssFilter = cssFilter || ''; + $frames = self.$preview.find($h.FRAMES + cssFilter); + if (self.reversePreviewOrder) { + $frames = $($frames.get().reverse()); + } + return $frames; + }, + getPreview: function () { + var self = this; + return { + content: self.initialPreview, + config: self.initialPreviewConfig, + tags: self.initialPreviewThumbTags + }; } }; $.fn.fileinput = function (option) { - if (!hasFileAPISupport() && !isIE(9)) { + if (!$h.hasFileAPISupport() && !$h.isIE(9)) { return; } var args = Array.apply(null, arguments), retvals = []; @@ -3125,17 +4220,16 @@ this.each(function () { var self = $(this), data = self.data('fileinput'), options = typeof option === 'object' && option, theme = options.theme || self.data('theme'), l = {}, t = {}, - lang = options.language || self.data('language') || 'en', opts; + lang = options.language || self.data('language') || $.fn.fileinput.defaults.language || 'en', opt; if (!data) { if (theme) { t = $.fn.fileinputThemes[theme] || {}; } - if (lang !== 'en' && !isEmpty($.fn.fileinputLocales[lang])) { + if (lang !== 'en' && !$h.isEmpty($.fn.fileinputLocales[lang])) { l = $.fn.fileinputLocales[lang] || {}; } - opts = $.extend(true, {}, $.fn.fileinput.defaults, t, $.fn.fileinputLocales.en, l, options, - self.data()); - data = new FileInput(this, opts); + opt = $.extend(true, {}, $.fn.fileinput.defaults, t, $.fn.fileinputLocales.en, l, options, self.data()); + data = new FileInput(this, opt); self.data('fileinput', data); } @@ -3165,8 +4259,14 @@ showUploadedThumbs: true, browseOnZoneClick: false, autoReplace: false, + autoOrientImage: true, // for JPEG images based on EXIF orientation tag + required: false, + rtl: false, + hideThumbnailContent: false, + generateFileId: null, previewClass: '', captionClass: '', + frameClass: 'krajee-default', mainClass: 'file-caption-main', mainTemplate: null, purifyHtml: true, @@ -3180,13 +4280,11 @@ initialPreviewThumbTags: [], previewThumbTags: {}, initialPreviewShowDelete: true, + initialPreviewDownloadUrl: '', removeFromPreviewOnError: false, deleteUrl: '', deleteExtraData: {}, overwriteInitial: true, - layoutTemplates: defaultLayoutTemplates, - previewTemplates: defaultPreviewTemplates, - previewZoomSettings: defaultPreviewZoomSettings, previewZoomButtonIcons: { prev: '', next: '', @@ -3198,20 +4296,20 @@ previewZoomButtonClasses: { prev: 'btn btn-navigate', next: 'btn btn-navigate', - toggleheader: 'btn btn-default btn-header-toggle', - fullscreen: 'btn btn-default', - borderless: 'btn btn-default', - close: 'btn btn-default' + toggleheader: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + fullscreen: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + borderless: 'btn btn-sm btn-kv btn-default btn-outline-secondary', + close: 'btn btn-sm btn-kv btn-default btn-outline-secondary' }, - allowedPreviewTypes: defaultPreviewTypes, + preferIconicPreview: false, + preferIconicZoomPreview: false, + allowedPreviewTypes: undefined, allowedPreviewMimeTypes: null, allowedFileTypes: null, allowedFileExtensions: null, defaultPreviewContent: null, customLayoutTags: {}, customPreviewTags: {}, - previewSettings: defaultPreviewSettings, - fileTypeSettings: defaultFileTypeSettings, previewFileIcon: '', previewFileIconClass: 'file-other-icon', previewFileIconSettings: {}, @@ -3220,12 +4318,13 @@ browseIcon: ' ', browseClass: 'btn btn-primary', removeIcon: '', - removeClass: 'btn btn-default', + removeClass: 'btn btn-default btn-secondary', cancelIcon: '', - cancelClass: 'btn btn-default', + cancelClass: 'btn btn-default btn-secondary', uploadIcon: '', - uploadClass: 'btn btn-default', + uploadClass: 'btn btn-default btn-secondary', uploadUrl: null, + uploadUrlThumb: null, uploadAsync: true, uploadExtraData: {}, zoomModalHeight: 480, @@ -3237,6 +4336,7 @@ resizePreference: 'width', resizeQuality: 0.92, resizeDefaultImageType: 'image/jpeg', + resizeIfSizeMoreThan: 0, // in KB minFileSize: 0, maxFileSize: 0, maxFilePreviewSize: 25600, // 25 MB @@ -3246,10 +4346,10 @@ msgValidationErrorClass: 'text-danger', msgValidationErrorIcon: ' ', msgErrorClass: 'file-error-message', - progressThumbClass: "progress-bar progress-bar-success progress-bar-striped active", - progressClass: "progress-bar progress-bar-success progress-bar-striped active", - progressCompleteClass: "progress-bar progress-bar-success", - progressErrorClass: "progress-bar progress-bar-danger", + progressThumbClass: "progress-bar bg-success progress-bar-success progress-bar-striped active", + progressClass: "progress-bar bg-success progress-bar-success progress-bar-striped active", + progressCompleteClass: "progress-bar bg-success progress-bar-success", + progressErrorClass: "progress-bar bg-danger progress-bar-danger", progressUploadThreshold: 99, previewFileType: 'image', elCaptionContainer: null, @@ -3258,7 +4358,7 @@ elPreviewImage: null, elPreviewStatus: null, elErrorContainer: null, - errorCloseButton: '×', + errorCloseButton: $h.closeButton('kv-error-close'), slugCallback: null, dropZoneEnabled: true, dropZoneTitleClass: 'file-drop-zone-title', @@ -3267,7 +4367,11 @@ textEncoding: 'UTF-8', ajaxSettings: {}, ajaxDeleteSettings: {}, - showAjaxErrorDetails: true + showAjaxErrorDetails: true, + mergeAjaxCallbacks: false, + mergeAjaxDeleteCallbacks: false, + retryErrorUploads: true, + reversePreviewOrder: false }; $.fn.fileinputLocales.en = { @@ -3283,7 +4387,9 @@ msgNo: 'No', msgNoFilesSelected: 'No files selected', msgCancelled: 'Cancelled', + msgPlaceholder: 'Select {files}...', msgZoomModalHeading: 'Detailed Preview', + msgFileRequired: 'You must select a file to upload.', msgSizeTooSmall: 'File "{name}" ({size} KB) is too small and must be larger than {minSize} KB.', msgSizeTooLarge: 'File "{name}" ({size} KB) exceeds maximum allowed upload size of {maxSize} KB.', msgFilesTooLess: 'You must select at least {n} {files} to upload.', @@ -3296,8 +4402,22 @@ msgInvalidFileName: 'Invalid or unsupported characters in file name "{name}".', msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', + msgFileTypes: { + 'image': 'image', + 'html': 'HTML', + 'text': 'text', + 'video': 'video', + 'audio': 'audio', + 'flash': 'flash', + 'pdf': 'PDF', + 'object': 'object' + }, msgUploadAborted: 'The file upload was aborted', msgUploadThreshold: 'Processing...', + msgUploadBegin: 'Initializing...', + msgUploadEnd: 'Done', + msgUploadEmpty: 'No valid data available for upload.', + msgUploadError: 'Error', msgValidationError: 'Validation Error', msgLoading: 'Loading file {index} of {files} …', msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', @@ -3309,6 +4429,14 @@ msgImageHeightLarge: 'Height of image file "{name}" cannot exceed {size} px.', msgImageResizeError: 'Could not get the image dimensions to resize.', msgImageResizeException: 'Error while resizing the image.
    {errors}
    ', + msgAjaxError: 'Something went wrong with the {operation} operation. Please try again later!', + msgAjaxProgressError: '{operation} failed', + ajaxOperations: { + deleteThumb: 'file delete', + uploadThumb: 'file upload', + uploadBatch: 'batch file upload', + uploadExtra: 'form data upload' + }, dropZoneTitle: 'Drag & drop files here …', dropZoneClickTitle: '
    (or click to select {files})', previewZoomButtonTitles: { diff --git a/Bootstrap.Admin/wwwroot/js/fileinput.min.js b/Bootstrap.Admin/wwwroot/js/fileinput.min.js index 4f56c0a1..03cc9cf2 100644 --- a/Bootstrap.Admin/wwwroot/js/fileinput.min.js +++ b/Bootstrap.Admin/wwwroot/js/fileinput.min.js @@ -1,12 +1,12 @@ /*! - * bootstrap-fileinput v4.3.6 + * bootstrap-fileinput v4.4.9 * http://plugins.krajee.com/file-input * * Author: Kartik Visweswaran - * Copyright: 2014 - 2016, Kartik Visweswaran, Krajee.com + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com * * Licensed under the BSD 3-Clause * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md - */!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(window.jQuery)}(function(a){"use strict";a.fn.fileinputLocales={},a.fn.fileinputThemes={};var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea,fa,ga,ha,ia,ja,ka,la,ma,na,oa;b=".fileinput",c="kvFileinputModal",d='style="width:{width};height:{height};"',e='\n\n\n\n\n\n',f='
    \n{previewFileIcon}\n
    ',g=window.URL||window.webkitURL,h=function(a,b,c){return void 0!==a&&(c?a===b:a.match(b))},i=function(a){if("Microsoft Internet Explorer"!==navigator.appName)return!1;if(10===a)return new RegExp("msie\\s"+a,"i").test(navigator.userAgent);var c,b=document.createElement("div");return b.innerHTML="",c=b.getElementsByTagName("i").length,document.body.appendChild(b),b.parentNode.removeChild(b),c},j=function(a,c,d,e){var f=e?c:c.split(" ").join(b+" ")+b;a.off(f).on(f,d)},k={data:{},init:function(a){var b=a.initialPreview,c=a.id;b.length>0&&!ea(b)&&(b=b.split(a.initialPreviewDelimiter)),k.data[c]={content:b,config:a.initialPreviewConfig,tags:a.initialPreviewThumbTags,delimiter:a.initialPreviewDelimiter,previewFileType:a.initialPreviewFileType,previewAsData:a.initialPreviewAsData,template:a.previewGenericTemplate,showZoom:a.fileActionSettings.showZoom,showDrag:a.fileActionSettings.showDrag,getSize:function(b){return a._getSize(b)},parseTemplate:function(b,c,d,e,f,g,h){var i=" file-preview-initial";return a._generatePreviewTemplate(b,c,d,e,f,!1,null,i,g,h)},msg:function(b){return a._getMsgSelected(b)},initId:a.previewInitId,footer:a._getLayoutTemplate("footer").replace(/\{progress}/g,a._renderThumbProgress()),isDelete:a.initialPreviewShowDelete,caption:a.initialCaption,actions:function(b,c,d,e,f,g,h){return a._renderFileActions(b,c,d,e,f,g,h,!0)}}},fetch:function(a){return k.data[a].content.filter(function(a){return null!==a})},count:function(a,b){return k.data[a]&&k.data[a].content?b?k.data[a].content.length:k.fetch(a).length:0},get:function(b,c,d){var j,l,n,o,p,q,e="init_"+c,f=k.data[b],g=f.config[c],h=f.content[c],i=f.initId+"-"+e,m=" file-preview-initial",r=fa("previewAsData",g,f.previewAsData);return d=void 0===d||d,h?(g&&g.frameClass&&(m+=" "+g.frameClass),r?(n=f.previewAsData?fa("type",g,f.previewFileType||"generic"):"generic",o=fa("caption",g),p=k.footer(b,c,d,g&&g.size||null),q=fa("filetype",g,n),j=f.parseTemplate(n,h,o,q,i,p,e,null)):j=f.template.replace(/\{previewId}/g,i).replace(/\{frameClass}/g,m).replace(/\{fileindex}/g,e).replace(/\{content}/g,f.content[c]).replace(/\{template}/g,fa("type",g,f.previewFileType)).replace(/\{footer}/g,k.footer(b,c,d,g&&g.size||null)),f.tags.length&&f.tags[c]&&(j=ia(j,f.tags[c])),da(g)||da(g.frameAttr)||(l=a(document.createElement("div")).html(j),l.find(".file-preview-initial").attr(g.frameAttr),j=l.html(),l.remove()),j):""},add:function(b,c,d,e,f){var h,g=a.extend(!0,{},k.data[b]);return ea(c)||(c=c.split(g.delimiter)),f?(h=g.content.push(c)-1,g.config[h]=d,g.tags[h]=e):(h=c.length-1,g.content=c,g.config=d,g.tags=e),k.data[b]=g,h},set:function(b,c,d,e,f){var h,i,g=a.extend(!0,{},k.data[b]);if(c&&c.length&&(ea(c)||(c=c.split(g.delimiter)),i=c.filter(function(a){return null!==a}),i.length)){if(void 0===g.content&&(g.content=[]),void 0===g.config&&(g.config=[]),void 0===g.tags&&(g.tags=[]),f){for(h=0;h'+b+"
    ",caption:d}},footer:function(a,b,c,d){var e=k.data[a];if(c=void 0===c||c,0===e.config.length||da(e.config[b]))return"";var f=e.config[b],g=fa("caption",f),h=fa("width",f,"auto"),i=fa("url",f,!1),j=fa("key",f,null),l=fa("showDelete",f,!0),m=fa("showZoom",f,e.showZoom),n=fa("showDrag",f,e.showDrag),o=i===!1&&c,p=e.isDelete?e.actions(!1,l,m,n,o,i,j):"",q=e.footer.replace(/\{actions}/g,p);return q.replace(/\{caption}/g,g).replace(/\{size}/g,e.getSize(d)).replace(/\{width}/g,h).replace(/\{indicator}/g,"").replace(/\{indicatorTitle}/g,"")}},l=function(a,b){return b=b||0,"number"==typeof a?a:("string"==typeof a&&(a=parseFloat(a)),isNaN(a)?b:a)},m=function(){return!(!window.File||!window.FileReader)},n=function(){var a=document.createElement("div");return!i(9)&&(void 0!==a.draggable||void 0!==a.ondragstart&&void 0!==a.ondrop)},o=function(){return m()&&window.FormData},p=function(a,b){a.removeClass(b).addClass(b)},X={showRemove:!0,showUpload:!0,showZoom:!0,showDrag:!0,removeIcon:'',removeClass:"btn btn-xs btn-default",removeTitle:"Remove file",uploadIcon:'',uploadClass:"btn btn-xs btn-default",uploadTitle:"Upload file",zoomIcon:'',zoomClass:"btn btn-xs btn-default",zoomTitle:"View Details",dragIcon:'',dragClass:"text-info",dragTitle:"Move / Rearrange",dragSettings:{},indicatorNew:'',indicatorSuccess:'',indicatorError:'',indicatorLoading:'',indicatorNewTitle:"Not uploaded yet",indicatorSuccessTitle:"Uploaded",indicatorErrorTitle:"Upload Error",indicatorLoadingTitle:"Uploading ..."},q='{preview}\n
    \n
    \n {caption}\n
    \n {remove}\n {cancel}\n {upload}\n {browse}\n
    \n
    ',r='{preview}\n
    \n{remove}\n{cancel}\n{upload}\n{browse}\n',s='
    \n {close}
    \n
    \n
    \n
    \n
    \n
    \n
    ',u='
    ×
    \n',t='',v='
    \n
    \n
    \n',w='',x='{icon} {label}',y='
    {icon} {label}
    ',z='',A='\n',B='
    \n
    \n {percent}%\n
    \n
    ',C="
    ({sizeText})",D='',E='
    \n \n {drag}\n
    {indicator}
    \n
    \n
    ',F='\n',G='',H='',I='{dragIcon}',J='
    \n',L=J+' title="{caption}" '+d+'>
    \n',M="
    {footer}\n
    \n",N="{content}\n",O='
    {data}
    \n",P='{caption}\n",Q='\n",R='\n",S='\n",T='\n'+e+" "+f+"\n\n",U='\n\n'+e+" "+f+"\n\n",V='\n',W='
    \n'+f+"\n
    \n",Y={main1:q,main2:r,preview:s,close:u,fileIcon:t,caption:v,modalMain:z,modal:A,progress:B,size:C,footer:D,actions:E,actionDelete:F,actionUpload:G,actionZoom:H,actionDrag:I,btnDefault:w,btnLink:x,btnBrowse:y},Z={generic:K+N+M,html:K+O+M,image:K+P+M,text:K+Q+M,video:L+R+M,audio:L+S+M,flash:L+T+M,object:L+U+M,pdf:L+V+M,other:L+W+M},_=["image","html","text","video","audio","flash","pdf","object"],ba={image:{width:"auto",height:"160px"},html:{width:"213px",height:"160px"},text:{width:"213px",height:"160px"},video:{width:"213px",height:"160px"},audio:{width:"213px",height:"80px"},flash:{width:"213px",height:"160px"},object:{width:"160px",height:"160px"},pdf:{width:"160px",height:"160px"},other:{width:"160px",height:"160px"}},$={image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:{width:"100%",height:"100%","min-height":"480px"},text:{width:"100%",height:"100%","min-height":"480px"},video:{width:"auto",height:"100%","max-width":"100%"},audio:{width:"100%",height:"30px"},flash:{width:"auto",height:"480px"},object:{width:"auto",height:"100%","min-height":"480px"},pdf:{width:"100%",height:"100%","min-height":"480px"},other:{width:"auto",height:"100%","min-height":"480px"}},ca={image:function(a,b){return h(a,"image.*")||h(b,/\.(gif|png|jpe?g)$/i)},html:function(a,b){return h(a,"text/html")||h(b,/\.(htm|html)$/i)},text:function(a,b){return h(a,"text.*")||h(b,/\.(xml|javascript)$/i)||h(b,/\.(txt|md|csv|nfo|ini|json|php|js|css)$/i)},video:function(a,b){return h(a,"video.*")&&(h(a,/(ogg|mp4|mp?g|webm|3gp)$/i)||h(b,/\.(og?|mp4|webm|mp?g|3gp)$/i))},audio:function(a,b){return h(a,"audio.*")&&(h(b,/(ogg|mp3|mp?g|wav)$/i)||h(b,/\.(og?|mp3|mp?g|wav)$/i))},flash:function(a,b){return h(a,"application/x-shockwave-flash",!0)||h(b,/\.(swf)$/i)},pdf:function(a,b){return h(a,"application/pdf",!0)||h(b,/\.(pdf)$/i)},object:function(){return!0},other:function(){return!0}},da=function(b,c){return void 0===b||null===b||0===b.length||c&&""===a.trim(b)},ea=function(a){return Array.isArray(a)||"[object Array]"===Object.prototype.toString.call(a)},fa=function(a,b,c){return c=c||"",b&&"object"==typeof b&&a in b?b[a]:c},aa=function(b,c,d){return da(b)||da(b[c])?d:a(b[c])},ga=function(){return Math.round((new Date).getTime()+100*Math.random())},ha=function(a){return a.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},ia=function(b,c){var d=b;return c?(a.each(c,function(a,b){"function"==typeof b&&(b=b()),d=d.split(a).join(b)}),d):d},ja=function(a){var b=a.is("img")?a.attr("src"):a.find("source").attr("src");g.revokeObjectURL(b)},ka=function(a){var b=a.lastIndexOf("/");return b===-1&&(b=a.lastIndexOf("\\")),a.split(a.substring(b,b+1)).pop()},la=function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement},ma=function(a){a&&!la()?document.documentElement.requestFullscreen?document.documentElement.requestFullscreen():document.documentElement.msRequestFullscreen?document.documentElement.msRequestFullscreen():document.documentElement.mozRequestFullScreen?document.documentElement.mozRequestFullScreen():document.documentElement.webkitRequestFullscreen&&document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT):document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()},na=function(a,b,c){if(c>=a.length)for(var d=c-a.length;d--+1;)a.push(void 0);return a.splice(c,0,a.splice(b,1)[0]),a},oa=function(b,c){var d=this;d.$element=a(b),d._validate()&&(d.isPreviewable=m(),d.isIE9=i(9),d.isIE10=i(10),d.isPreviewable||d.isIE9?(d._init(c),d._listen()):d.$element.removeClass("file-loading"))},oa.prototype={constructor:oa,_init:function(b){var e,c=this,d=c.$element;a.each(b,function(a,b){switch(a){case"minFileCount":case"maxFileCount":case"maxFileSize":c[a]=l(b);break;default:c[a]=b}}),c.fileInputCleared=!1,c.fileBatchCompleted=!0,c.isPreviewable||(c.showPreview=!1),c.uploadFileAttr=da(d.attr("name"))?"file_data":d.attr("name"),c.reader=null,c.formdata={},c.clearStack(),c.uploadCount=0,c.uploadStatus={},c.uploadLog=[],c.uploadAsyncCount=0,c.loadedImages=[],c.totalImagesCount=0,c.ajaxRequests=[],c.isError=!1,c.ajaxAborted=!1,c.cancelling=!1,e=c._getLayoutTemplate("progress"),c.progressTemplate=e.replace("{class}",c.progressClass),c.progressCompleteTemplate=e.replace("{class}",c.progressCompleteClass),c.progressErrorTemplate=e.replace("{class}",c.progressErrorClass),c.dropZoneEnabled=n()&&c.dropZoneEnabled,c.isDisabled=c.$element.attr("disabled")||c.$element.attr("readonly"),c.isUploadable=o()&&!da(c.uploadUrl),c.isClickable=c.browseOnZoneClick&&c.showPreview&&(c.isUploadable&&c.dropZoneEnabled||!da(c.defaultPreviewContent)),c.slug="function"==typeof b.slugCallback?b.slugCallback:c._slugDefault,c.mainTemplate=c.showCaption?c._getLayoutTemplate("main1"):c._getLayoutTemplate("main2"),c.captionTemplate=c._getLayoutTemplate("caption"),c.previewGenericTemplate=c._getPreviewTemplate("generic"),c.resizeImage&&(c.maxImageWidth||c.maxImageHeight)&&(c.imageCanvas=document.createElement("canvas"),c.imageCanvasContext=c.imageCanvas.getContext("2d")),da(c.$element.attr("id"))&&c.$element.attr("id",ga()),void 0===c.$container?c.$container=c._createContainer():c._refreshContainer(),c.$dropZone=c.$container.find(".file-drop-zone"),c.$progress=c.$container.find(".kv-upload-progress"),c.$btnUpload=c.$container.find(".fileinput-upload"),c.$captionContainer=aa(b,"elCaptionContainer",c.$container.find(".file-caption")),c.$caption=aa(b,"elCaptionText",c.$container.find(".file-caption-name")),c.$previewContainer=aa(b,"elPreviewContainer",c.$container.find(".file-preview")),c.$preview=aa(b,"elPreviewImage",c.$container.find(".file-preview-thumbnails")),c.$previewStatus=aa(b,"elPreviewStatus",c.$container.find(".file-preview-status")),c.$errorContainer=aa(b,"elErrorContainer",c.$previewContainer.find(".kv-fileinput-error")),da(c.msgErrorClass)||p(c.$errorContainer,c.msgErrorClass),c.$errorContainer.hide(),c.fileActionSettings=a.extend(!0,X,b.fileActionSettings),c.previewInitId="preview-"+ga(),c.id=c.$element.attr("id"),k.init(c),c._initPreview(!0),c._initPreviewActions(),c.options=b,c._setFileDropZoneTitle(),c.$element.removeClass("file-loading"),c.$element.attr("disabled")&&c.disable(),c._initZoom()},_validate:function(){var b,a=this;return"file"===a.$element.attr("type")||(b='

    Invalid Input Type

    You must set an input type = file for bootstrap-fileinput plugin to initialize.
    ',a.$element.after(b),!1)},_errorsExist:function(){var c,b=this;return!!b.$errorContainer.find("li").length||(c=a(document.createElement("div")).html(b.$errorContainer.html()),c.find("span.kv-error-close").remove(),c.find("ul").remove(),!!a.trim(c.text()).length)},_errorHandler:function(a,b){var c=this,d=a.target.error;d.code===d.NOT_FOUND_ERR?c._showError(c.msgFileNotFound.replace("{name}",b)):d.code===d.SECURITY_ERR?c._showError(c.msgFileSecured.replace("{name}",b)):d.code===d.NOT_READABLE_ERR?c._showError(c.msgFileNotReadable.replace("{name}",b)):d.code===d.ABORT_ERR?c._showError(c.msgFilePreviewAborted.replace("{name}",b)):c._showError(c.msgFilePreviewError.replace("{name}",b))},_addError:function(a){var b=this,c=b.$errorContainer;a&&c.length&&(c.html(b.errorCloseButton+a),j(c.find(".kv-error-close"),"click",function(){c.fadeOut("slow")}))},_resetErrors:function(a){var b=this,c=b.$errorContainer;b.isError=!1,b.$container.removeClass("has-error"),c.html(""),a?c.fadeOut("slow"):c.hide()},_showFolderError:function(a){var d,b=this,c=b.$errorContainer;a&&(d=b.msgFoldersNotAllowed.replace(/\{n}/g,a),b._addError(d),p(b.$container,"has-error"),c.fadeIn(800),b._raise("filefoldererror",[a,d]))},_showUploadError:function(a,b,c){var d=this,e=d.$errorContainer,f=c||"fileuploaderror",g=b&&b.id?'
  • '+a+"
  • ":"
  • "+a+"
  • ";return 0===e.find("ul").length?d._addError("
      "+g+"
    "):e.find("ul").append(g),e.fadeIn(800),d._raise(f,[b,a]),d.$container.removeClass("file-input-new"),p(d.$container,"has-error"),!0},_showError:function(a,b,c){var d=this,e=d.$errorContainer,f=c||"fileerror";return b=b||{},b.reader=d.reader,d._addError(a),e.fadeIn(800),d._raise(f,[b,a]),d.isUploadable||d._clearFileInput(),d.$container.removeClass("file-input-new"),p(d.$container,"has-error"),d.$btnUpload.attr("disabled",!0),!0},_noFilesError:function(a){var b=this,c=b.minFileCount>1?b.filePlural:b.fileSingle,d=b.msgFilesTooLess.replace("{n}",b.minFileCount).replace("{files}",c),e=b.$errorContainer;b._addError(d),b.isError=!0,b._updateFileDetails(0),e.fadeIn(800),b._raise("fileerror",[a,d]),b._clearFileInput(),p(b.$container,"has-error")},_parseError:function(b,c,d){var e=this,f=a.trim(c+""),g="."===f.slice(-1)?"":".",h=void 0!==b.responseJSON&&void 0!==b.responseJSON.error?b.responseJSON.error:b.responseText;return e.cancelling&&e.msgUploadAborted&&(f=e.msgUploadAborted),e.showAjaxErrorDetails&&h?(h=a.trim(h.replace(/\n\s*\n/g,"\n")),h=h.length>0?"
    "+h+"
    ":"",f+=g+h):f+=g,e.cancelling=!1,d?""+d+": "+f:f},_parseFileType:function(a){var c,d,e,f,b=this;for(f=0;f<_.length;f+=1)if(e=_[f],c=fa(e,b.fileTypeSettings,ca[e]),d=c(a.type,a.name)?e:"",!da(d))return d;return"other"},_parseFilePreviewIcon:function(b,c){var e,f,d=this,g=d.previewFileIcon;return c&&c.indexOf(".")>-1&&(f=c.split(".").pop(),d.previewFileIconSettings&&d.previewFileIconSettings[f]&&(g=d.previewFileIconSettings[f]),d.previewFileExtSettings&&a.each(d.previewFileExtSettings,function(a,b){return d.previewFileIconSettings[a]&&b(f)?void(g=d.previewFileIconSettings[a]):void(e=!0)})),b.indexOf("{previewFileIcon}")>-1?b.replace(/\{previewFileIconClass}/g,d.previewFileIconClass).replace(/\{previewFileIcon}/g,g):b},_raise:function(b,c){var d=this,e=a.Event(b);if(void 0!==c?d.$element.trigger(e,c):d.$element.trigger(e),e.isDefaultPrevented())return!1;if(!e.result)return e.result;switch(b){case"filebatchuploadcomplete":case"filebatchuploadsuccess":case"fileuploaded":case"fileclear":case"filecleared":case"filereset":case"fileerror":case"filefoldererror":case"fileuploaderror":case"filebatchuploaderror":case"filedeleteerror":case"filecustomerror":case"filesuccessremove":break;default:d.ajaxAborted=e.result}return!0},_listenFullScreen:function(a){var d,e,b=this,c=b.$modal;c&&c.length&&(d=c&&c.find(".btn-fullscreen"),e=c&&c.find(".btn-borderless"),d.length&&e.length&&(d.removeClass("active").attr("aria-pressed","false"),e.removeClass("active").attr("aria-pressed","false"),a?d.addClass("active").attr("aria-pressed","true"):e.addClass("active").attr("aria-pressed","true"),c.hasClass("file-zoom-fullscreen")?b._maximizeZoomDialog():a?b._maximizeZoomDialog():e.removeClass("active").attr("aria-pressed","false")))},_listen:function(){var b=this,c=b.$element,d=c.closest("form"),e=b.$container;j(c,"change",a.proxy(b._change,b)),b.showBrowse&&j(b.$btnFile,"click",a.proxy(b._browse,b)),j(d,"reset",a.proxy(b.reset,b)),j(e.find(".fileinput-remove:not([disabled])"),"click",a.proxy(b.clear,b)),j(e.find(".fileinput-cancel"),"click",a.proxy(b.cancel,b)),b._initDragDrop(),b.isUploadable||j(d,"submit",a.proxy(b._submitForm,b)),j(b.$container.find(".fileinput-upload"),"click",a.proxy(b._uploadClick,b)),j(a(window),"resize",function(){b._listenFullScreen(screen.width===window.innerWidth&&screen.height===window.innerHeight)}),j(a(document),"webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange",function(){b._listenFullScreen(la())}),b._initClickable()},_initClickable:function(){var c,b=this;b.isClickable&&(c=b.isUploadable?b.$dropZone:b.$preview.find(".file-default-preview"),p(c,"clickable"),c.attr("tabindex",-1),j(c,"click",function(d){var e=a(d.target);e.parents(".file-preview-thumbnails").length&&!e.parents(".file-default-preview").length||(b.$element.trigger("click"),c.blur())}))},_initDragDrop:function(){var b=this,c=b.$dropZone;b.isUploadable&&b.dropZoneEnabled&&b.showPreview&&(j(c,"dragenter dragover",a.proxy(b._zoneDragEnter,b)),j(c,"dragleave",a.proxy(b._zoneDragLeave,b)),j(c,"drop",a.proxy(b._zoneDrop,b)),j(a(document),"dragenter dragover drop",b._zoneDragDropInit))},_zoneDragDropInit:function(a){a.stopPropagation(),a.preventDefault()},_zoneDragEnter:function(b){var c=this,d=a.inArray("Files",b.originalEvent.dataTransfer.types)>-1;return c._zoneDragDropInit(b),c.isDisabled||!d?(b.originalEvent.dataTransfer.effectAllowed="none",void(b.originalEvent.dataTransfer.dropEffect="none")):void p(c.$dropZone,"file-highlighted")},_zoneDragLeave:function(a){var b=this;b._zoneDragDropInit(a),b.isDisabled||b.$dropZone.removeClass("file-highlighted")},_zoneDrop:function(a){var b=this;a.preventDefault(),b.isDisabled||da(a.originalEvent.dataTransfer.files)||(b._change(a,"dragdrop"),b.$dropZone.removeClass("file-highlighted"))},_uploadClick:function(a){var d,b=this,c=b.$container.find(".fileinput-upload"),e=!c.hasClass("disabled")&&da(c.attr("disabled"));if(!a||!a.isDefaultPrevented()){if(!b.isUploadable)return void(e&&"submit"!==c.attr("type")&&(d=c.closest("form"),d.length&&d.trigger("submit"),a.preventDefault()));a.preventDefault(),e&&b.upload()}},_submitForm:function(){var a=this,b=a.$element,c=b.get(0).files;return c&&a.minFileCount>0&&a._getFileCount(c.length)