UNPKG

nest-parrot

Version:
1,105 lines (1,099 loc) 124 kB
/*! * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014 - 2015 * @version 4.2.8 * * File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced * features including the FileReader API. * * The plugin drastically enhances the HTML file input to preview multiple files on the client before * upload. In addition it provides the ability to preview content of images, text, videos, audio, html, * flash and other objects. It also offers the ability to upload and delete files using AJAX, and add * files in batches (i.e. preview, append, or remove before upload). * * Author: Kartik Visweswaran * Copyright: 2015, Kartik Visweswaran, Krajee.com * For more JQuery plugins visit http://plugins.krajee.com * For more Yii related demos visit http://demos.krajee.com */ (function (factory) { "use strict"; if (typeof define === 'function' && define.amd) { // jshint ignore:line // AMD. Register as an anonymous module. define(['jquery'], factory); // jshint ignore:line } else { // noinspection JSUnresolvedVariable if (typeof module === 'object' && module.exports) { // jshint ignore:line // Node/CommonJS // noinspection JSUnresolvedVariable module.exports = factory(require('jquery')); // jshint ignore:line } else { // Browser globals factory(window.jQuery); } } }(function ($) { "use strict"; $.fn.fileinputLocales = {}; var isIE, isEdge, handler, previewCache, getNum, hasFileAPISupport, hasDragDropSupport, hasFileUploadSupport, addCss, STYLE_SETTING, OBJECT_PARAMS, DEFAULT_PREVIEW, defaultFileActionSettings, tMain1, tMain2, tPreview, tIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse, tModal, tProgress, tFooter, tActions, tActionDelete, tActionUpload, tZoom, tGeneric, tHtml, tImage, tText, tVideo, tAudio, tFlash, tObject, tOther, defaultLayoutTemplates, defaultPreviewTemplates, defaultPreviewTypes, defaultPreviewSettings, defaultFileTypeSettings, isEmpty, isArray, isSet, getElement, uniqId, htmlEncode, replaceTags, objUrl, FileInput; 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 = "<!--[if IE " + ver + "]> <i></i> <![endif]-->"; status = div.getElementsByTagName("i").length; document.body.appendChild(div); div.parentNode.removeChild(div); return status; }; isEdge = function () { return new RegExp('Edge\/[0-9]+', 'i').test(navigator.userAgent); }; handler = function ($el, event, callback, skipNS) { var ev = skipNS ? event : event + '.fileinput'; $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); } previewCache.data[id] = { content: content, config: obj.initialPreviewConfig, tags: obj.initialPreviewThumbTags, delimiter: obj.initialPreviewDelimiter, template: obj.previewGenericTemplate, msg: function (n) { return obj.getMsgSelected(n); }, initId: obj.previewInitId, footer: obj.getLayoutTemplate('footer').replace(/\{progress}/g, obj.renderThumbProgress()), isDelete: obj.initialPreviewShowDelete, caption: obj.initialCaption, actions: function (showUpload, showDelete, disabled, url, key) { return obj.renderFileActions(showUpload, showDelete, disabled, url, key); } }; }, 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], previewId = data.initId + '-' + ind, out, $tmp, frameClass = ' file-preview-initial'; /** @namespace config.frameClass */ /** @namespace config.frameAttr */ isDisabled = isDisabled === undefined ? true : isDisabled; if (data.content[i] === null) { return ''; } if (!isEmpty(config) && !isEmpty(config.frameClass)) { frameClass += ' ' + config.frameClass; } out = data.template .replace(/\{previewId}/g, previewId) .replace(/\{frameClass}/g, frameClass) .replace(/\{fileindex}/g, ind) .replace(/\{content}/g, data.content[i]) .replace(/\{footer}/g, previewCache.footer(id, i, isDisabled)); 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; }, 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; 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 (id, index) { var chk = previewCache.count(id); if (!chk) { return; } if (chk === 1) { previewCache.data[id].content = []; previewCache.data[id].config = []; return; } previewCache.data[id].content[index] = null; previewCache.data[id].config[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) { 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 = isSet('caption', config) ? config.caption : '', width = isSet('width', config) ? config.width : 'auto', url = isSet('url', config) ? config.url : false, key = isSet('key', config) ? config.key : null, disabled = (url === false) && isDisabled, actions = data.isDelete ? data.actions(false, true, disabled, url, key) : '', footer = data.footer.replace(/\{actions}/g, actions); return footer.replace(/\{caption}/g, caption) .replace(/\{width}/g, width) .replace(/\{indicator}/g, '') .replace(/\{indicatorTitle}/g, ''); } }; 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) && !isEdge() && // Fix for MS Edge drag & drop support bug (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); }; hasFileUploadSupport = function () { return hasFileAPISupport() && window.FormData; }; addCss = function ($el, css) { $el.removeClass(css).addClass(css); }; STYLE_SETTING = 'style="width:{width};height:{height};"'; OBJECT_PARAMS = ' <param name="controller" value="true" />\n' + ' <param name="allowFullScreen" value="true" />\n' + ' <param name="allowScriptAccess" value="always" />\n' + ' <param name="autoPlay" value="false" />\n' + ' <param name="autoStart" value="false" />\n' + ' <param name="quality" value="high" />\n'; DEFAULT_PREVIEW = '<div class="file-preview-other">\n' + ' <span class="{previewFileIconClass}">{previewFileIcon}</span>\n' + '</div>'; defaultFileActionSettings = { removeIcon: '<i class="glyphicon glyphicon-trash text-danger"></i>', removeClass: 'btn btn-xs btn-default', removeTitle: 'Remove file', uploadIcon: '<i class="glyphicon glyphicon-upload text-info"></i>', uploadClass: 'btn btn-xs btn-default', uploadTitle: 'Upload file', indicatorNew: '<i class="glyphicon glyphicon-hand-down text-warning"></i>', indicatorSuccess: '<i class="glyphicon glyphicon-ok-sign text-success"></i>', indicatorError: '<i class="glyphicon glyphicon-exclamation-sign text-danger"></i>', indicatorLoading: '<i class="glyphicon glyphicon-hand-up text-muted"></i>', indicatorNewTitle: 'Not uploaded yet', indicatorSuccessTitle: 'Uploaded', indicatorErrorTitle: 'Upload Error', indicatorLoadingTitle: 'Uploading ...' }; tMain1 = '{preview}\n' + '<div class="kv-upload-progress hide"></div>\n' + '<div class="input-group {class}">\n' + ' {caption}\n' + ' <div class="input-group-btn">\n' + ' {remove}\n' + ' {cancel}\n' + ' {upload}\n' + ' {browse}\n' + ' </div>\n' + '</div>'; tMain2 = '{preview}\n<div class="kv-upload-progress hide"></div>\n{remove}\n{cancel}\n{upload}\n{browse}\n'; tPreview = '<div class="file-preview {class}">\n' + ' {close}' + ' <div class="{dropClass}">\n' + ' <div class="file-preview-thumbnails">\n' + ' </div>\n' + ' <div class="clearfix"></div>' + ' <div class="file-preview-status text-center text-success"></div>\n' + ' <div class="kv-fileinput-error"></div>\n' + ' </div>\n' + '</div>'; tClose = '<div class="close fileinput-remove">&times;</div>\n'; tIcon = '<span class="glyphicon glyphicon-file kv-caption-icon"></span>'; tCaption = '<div tabindex="500" class="form-control file-caption {class}">\n' + ' <div class="file-caption-name"></div>\n' + '</div>\n'; //noinspection HtmlUnknownAttribute tBtnDefault = '<button type="{type}" tabindex="500" title="{title}" class="{css}" {status}>{icon}{label}</button>'; tBtnLink = '<a href="{href}" tabindex="500" title="{title}" class="{css}" {status}>{icon}{label}</a>'; tBtnBrowse = '<div tabindex="500" class="{css}" {status}>{icon}{label}</div>'; tModal = '<div id="{id}" class="file-preview-detail-modal modal fade" tabindex="-1">\n' + ' <div class="modal-dialog modal-lg">\n' + ' <div class="modal-content">\n' + ' <div class="modal-header">\n' + ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>\n' + ' <h3 class="modal-title">{heading} <small>{title}</small></h3>\n' + ' </div>\n' + ' <div class="modal-body">\n' + ' <pre>{body}</pre>\n' + ' </div>\n' + ' </div>\n' + ' </div>\n' + '</div>'; tProgress = '<div class="progress">\n' + ' <div class="{class}" role="progressbar"' + ' aria-valuenow="{percent}" aria-valuemin="0" aria-valuemax="100" style="width:{percent}%;">\n' + ' {percent}%\n' + ' </div>\n' + '</div>'; tFooter = '<div class="file-thumbnail-footer">\n' + ' <div class="file-footer-caption" title="{caption}">{caption}</div>\n' + ' {progress} {actions}\n' + '</div>'; tActions = '<div class="file-actions">\n' + ' <div class="file-footer-buttons">\n' + ' {upload}{delete}{other}' + ' </div>\n' + ' <div class="file-upload-indicator" title="{indicatorTitle}">{indicator}</div>\n' + ' <div class="clearfix"></div>\n' + '</div>'; tActionDelete = '<button type="button" class="kv-file-remove {removeClass}" ' + 'title="{removeTitle}"{dataUrl}{dataKey}>{removeIcon}</button>\n'; tActionUpload = '<button type="button" class="kv-file-upload {uploadClass}" title="{uploadTitle}">' + ' {uploadIcon}\n</button>\n'; tZoom = '<button type="button" class="btn btn-default btn-xs btn-block" title="{zoomTitle}: {caption}" onclick="{dialog}">\n' + ' {zoomInd}\n' + '</button>\n'; tGeneric = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + ' {content}\n' + ' {footer}\n' + '</div>\n'; tHtml = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + ' <object class="file-object" data="{data}" type="{type}" width="{width}" height="{height}">\n' + ' ' + DEFAULT_PREVIEW + '\n' + ' </object>\n' + ' {footer}\n' + '</div>'; tImage = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + ' <img src="{data}" class="file-preview-image" title="{caption}" alt="{caption}" ' + STYLE_SETTING + '>\n' + ' {footer}\n' + '</div>\n'; tText = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}">\n' + ' <pre class="file-preview-text" title="{caption}" ' + STYLE_SETTING + '>{data}</pre>\n' + ' {zoom}\n' + ' {footer}\n' + '</div>'; tVideo = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + ' title="{caption}" ' + STYLE_SETTING + '>\n' + ' <video width="{width}" height="{height}" controls>\n' + ' <source src="{data}" type="{type}">\n' + ' ' + DEFAULT_PREVIEW + '\n' + ' </video>\n' + ' {footer}\n' + '</div>\n'; tAudio = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + ' title="{caption}" ' + STYLE_SETTING + '>\n' + ' <audio controls>\n' + ' <source src="' + '{data}' + '" type="{type}">\n' + ' ' + DEFAULT_PREVIEW + '\n' + ' </audio>\n' + ' {footer}\n' + '</div>'; tFlash = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + ' title="{caption}" ' + STYLE_SETTING + '>\n' + ' <object class="file-object" type="application/x-shockwave-flash" width="{width}" height="{height}" data="{data}">\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + ' </object>\n' + ' {footer}\n' + '</div>\n'; tObject = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + ' title="{caption}" ' + STYLE_SETTING + '>\n' + ' <object class="file-object" data="{data}" type="{type}" width="{width}" height="{height}">\n' + ' <param name="movie" value="{caption}" />\n' + OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + ' </object>\n' + ' {footer}\n' + '</div>'; tOther = '<div class="file-preview-frame{frameClass}" id="{previewId}" data-fileindex="{fileindex}"' + ' title="{caption}" ' + STYLE_SETTING + '>\n' + ' <div class="file-preview-other-frame">\n' + ' ' + DEFAULT_PREVIEW + '\n' + ' </div>\n' + ' <div class="file-preview-other-footer">{footer}</div>\n' + '</div>'; defaultLayoutTemplates = { main1: tMain1, main2: tMain2, preview: tPreview, close: tClose, zoom: tZoom, icon: tIcon, caption: tCaption, modal: tModal, progress: tProgress, footer: tFooter, actions: tActions, actionDelete: tActionDelete, actionUpload: tActionUpload, btnDefault: tBtnDefault, btnLink: tBtnLink, btnBrowse: tBtnBrowse }; defaultPreviewTemplates = { generic: tGeneric, html: tHtml, image: tImage, text: tText, video: tVideo, audio: tAudio, flash: tFlash, object: tObject, other: tOther }; defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'object']; defaultPreviewSettings = { image: {width: "auto", height: "160px"}, html: {width: "213px", height: "160px"}, text: {width: "160px", height: "136px"}, video: {width: "213px", height: "160px"}, audio: {width: "213px", height: "80px"}, flash: {width: "213px", height: "160px"}, object: {width: "160px", height: "160px"}, other: {width: "160px", height: "160px"} }; defaultFileTypeSettings = { image: function (vType, vName) { return (vType !== undefined) ? vType.match('image.*') : vName.match(/\.(gif|png|jpe?g)$/i); }, html: function (vType, vName) { return (vType !== undefined) ? vType === 'text/html' : vName.match(/\.(htm|html)$/i); }, text: function (vType, vName) { return (vType !== undefined && vType.match('text.*')) || vName.match(/\.(txt|md|csv|nfo|ini|json|php|js|css)$/i); }, video: function (vType, vName) { return (vType !== undefined && vType.match(/\.video\/(ogg|mp4|webm|3gp)$/i)) || vName.match(/\.(og?|mp4|webm|3gp)$/i); }, audio: function (vType, vName) { return (vType !== undefined && vType.match(/\.audio\/(ogg|mp3|wav)$/i)) || vName.match(/\.(ogg|mp3|wav)$/i); }, flash: function (vType, vName) { return (vType !== undefined && vType === 'application/x-shockwave-flash') || vName.match(/\.(swf)$/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]'; }; isSet = function (needle, haystack) { return (typeof haystack === 'object' && needle in haystack); }; 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, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); }; 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; }; //noinspection JSUnresolvedVariable objUrl = window.URL || window.webkitURL; FileInput = function (element, options) { var self = this; self.$element = $(element); if (!self.validate()) { return; } self.isPreviewable = hasFileAPISupport(); self.isIE9 = isIE(9); self.isIE10 = isIE(10); if (self.isPreviewable || self.isIE9) { self.init(options); self.listen(); } else { self.$element.removeClass('file-loading'); } }; FileInput.prototype = { constructor: FileInput, validate: function () { var self = this, $exception; if (self.$element.attr('type') === 'file') { return true; } $exception = '<div class="help-block alert alert-warning">' + '<h4>Invalid Input Type</h4>' + 'You must set an input <code>type = file</code> for <b>bootstrap-fileinput</b> plugin to initialize.' + '</div>'; self.$element.after($exception); return false; }, 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'; self.reader = null; self.formdata = {}; self.clearStack(); self.uploadCount = 0; self.uploadStatus = {}; self.uploadLog = []; self.uploadAsyncCount = 0; self.loadedImages = []; self.totalImagesCount = 0; self.ajaxRequests = []; self.isError = false; self.ajaxAborted = false; self.cancelling = false; t = self.getLayoutTemplate('progress'); self.progressTemplate = t.replace('{class}', self.progressClass); self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); self.dropZoneEnabled = hasDragDropSupport() && self.dropZoneEnabled; self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); self.isUploadable = hasFileUploadSupport() && !isEmpty(self.uploadUrl); 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)) { self.imageCanvas = document.createElement('canvas'); self.imageCanvasContext = self.imageCanvas.getContext('2d'); } if (isEmpty(self.$element.attr('id'))) { self.$element.attr('id', uniqId()); } if (self.$container === undefined) { self.$container = self.createContainer(); } else { self.refreshContainer(); } 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); } self.$errorContainer.hide(); self.fileActionSettings = $.extend(defaultFileActionSettings, options.fileActionSettings); self.previewInitId = "preview-" + uniqId(); self.id = self.$element.attr('id'); previewCache.init(self); self.initPreview(true); self.initPreviewDeletes(); self.options = options; self.setFileDropZoneTitle(); self.$element.removeClass('file-loading'); if (self.$element.attr('disabled')) { self.disable(); } }, parseError: function (jqXHR, errorThrown, fileName) { var self = this, errMsg = $.trim(errorThrown + ''), dot = errMsg.slice(-1) === '.' ? '' : '.', text = jqXHR.responseJSON !== undefined && jqXHR.responseJSON.error !== undefined ? jqXHR.responseJSON.error : jqXHR.responseText; if (self.cancelling && self.msgUploadAborted) { errMsg = self.msgUploadAborted; } if (self.showAjaxErrorDetails && text) { text = $.trim(text.replace(/\n\s*\n/g, '\n')); text = text.length > 0 ? '<pre>' + text + '</pre>' : ''; errMsg += dot + text; } else { errMsg += dot; } self.cancelling = false; return fileName ? '<b>' + fileName + ': </b>' + errMsg : errMsg; }, raise: function (event, params) { var self = this, e = $.Event(event); if (params !== undefined) { self.$element.trigger(e, params); } else { self.$element.trigger(e); } if (e.isDefaultPrevented()) { return false; } if (!e.result) { return e.result; } switch (event) { // ignore these events 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; // receive data response via `filecustomerror` event` default: self.ajaxAborted = e.result; break; } return true; }, getLayoutTemplate: function (t) { var self = this, template = isSet(t, self.layoutTemplates) ? self.layoutTemplates[t] : defaultLayoutTemplates[t]; if (isEmpty(self.customLayoutTags)) { return template; } return replaceTags(template, self.customLayoutTags); }, getPreviewTemplate: function (t) { var self = this, template = isSet(t, self.previewTemplates) ? self.previewTemplates[t] : defaultPreviewTemplates[t]; if (isEmpty(self.customPreviewTags)) { return template; } return replaceTags(template, self.customPreviewTags); }, parseFilePreviewIcon: function (content, fname) { var self = this, ext, icn = self.previewFileIcon; if (fname && fname.indexOf('.') > -1) { ext = fname.split('.').pop(); if (self.previewFileIconSettings && self.previewFileIconSettings[ext]) { icn = self.previewFileIconSettings[ext]; } if (self.previewFileExtSettings) { $.each(self.previewFileExtSettings, function (key, func) { if (self.previewFileIconSettings[key] && func(ext)) { icn = self.previewFileIconSettings[key]; } }); } } if (content.indexOf('{previewFileIcon}') > -1) { return content.replace(/\{previewFileIconClass}/g, self.previewFileIconClass).replace(/\{previewFileIcon}/g, icn); } return content; }, getOutData: function (jqXHR, responseData, filesData) { var self = this; jqXHR = jqXHR || {}; responseData = responseData || {}; filesData = filesData || self.filestack.slice(0) || {}; return { form: self.formdata, files: filesData, filenames: self.filenames, extra: self.getExtraData(), response: responseData, reader: self.reader, jqXHR: jqXHR }; }, listen: function () { var self = this, $el = self.$element, $cap = self.$captionContainer, $btnFile = self.$btnFile, $form = $el.closest('form'), $cont = self.$container; handler($el, 'change', $.proxy(self.change, self)); handler($btnFile, 'click', function () { self.raise('filebrowse'); if (self.isError && !self.isUploadable) { self.clear(); } $cap.focus(); }); 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)); if (self.isUploadable && self.dropZoneEnabled && self.showPreview) { self.initDragDrop(); } if (!self.isUploadable) { handler($form, 'submit', $.proxy(self.submitForm, self)); } handler(self.$container.find('.fileinput-upload'), 'click', function (e) { var $btn = $(this), $form, isEnabled = !$btn.hasClass('disabled') && isEmpty($btn.attr('disabled')); if (!self.isUploadable) { if (isEnabled && $btn.attr('type') !== 'submit') { $form = $btn.closest('form'); // downgrade to normal form submit if possible if ($form.length) { $form.trigger('submit'); } e.preventDefault(); } return; } e.preventDefault(); if (isEnabled) { self.upload(); } }); }, 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({}); }, abort: function (params) { var self = this, data; if (self.ajaxAborted && typeof self.ajaxAborted === "object" && self.ajaxAborted.message !== undefined) { data = $.extend(self.getOutData(), params); data.abortData = self.ajaxAborted.data || {}; data.abortMessage = self.ajaxAborted.message; self.cancel(); self.setProgress(100); self.showUploadError(self.ajaxAborted.message, data, 'filecustomerror'); return true; } return false; }, noFilesError: function (params) { var self = this, label = self.minFileCount > 1 ? self.filePlural : self.fileSingle, msg = self.msgFilesTooLess.replace('{n}', self.minFileCount).replace('{files}', label), $error = self.$errorContainer; self.addError(msg); self.isError = true; self.updateFileDetails(0); $error.fadeIn(800); self.raise('fileerror', [params]); self.clearFileInput(); addCss(self.$container, 'has-error'); }, setProgress: function (p, $el) { var self = this, pct = Math.min(p, 100), template = pct < 100 ? self.progressTemplate : self.progressCompleteTemplate; $el = $el || self.$progress; if (!isEmpty(template)) { $el.html(template.replace(/\{percent}/g, pct)); } }, lock: function () { var self = this; self.resetErrors(); self.disable(); if (self.showRemove) { addCss(self.$container.find('.fileinput-remove'), 'hide'); } if (self.showCancel) { self.$container.find('.fileinput-cancel').removeClass('hide'); } self.raise('filelock', [self.filestack, self.getExtraData()]); }, unlock: function (reset) { var self = this; if (reset === undefined) { reset = true; } self.enable(); if (self.showCancel) { addCss(self.$container.find('.fileinput-cancel'), 'hide'); } if (self.showRemove) { self.$container.find('.fileinput-remove').removeClass('hide'); } if (reset) { self.resetFileStack(); } self.raise('fileunlock', [self.filestack, self.getExtraData()]); }, 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; }, destroy: function () { var self = this, $cont = self.$container; $cont.find('.file-drop-zone').off(); self.$element.insertBefore($cont).off('.fileinput').removeData(); $cont.off().remove(); }, refresh: function (options) { var self = this, $el = self.$element; options = options ? $.extend(self.options, options) : self.options; self.destroy(); $el.fileinput(options); if ($el.val()) { $el.trigger('change.fileinput'); } }, initDragDrop: function () { var self = this, $zone = self.$container.find('.file-drop-zone'), allEvents = 'dragenter.fileinput dragover.fileinput drop.fileinput'; handler($zone, 'dragenter.fileinput dragover.fileinput', function (e) { var hasFiles = $.inArray('Files', e.originalEvent.dataTransfer.types) > -1; e.stopPropagation(); e.preventDefault(); if (self.isDisabled || !hasFiles) { e.originalEvent.dataTransfer.effectAllowed = 'none'; e.originalEvent.dataTransfer.dropEffect = 'none'; return; } addCss($(this), 'file-highlighted'); }, true); handler($zone, 'dragleave', function (e) { e.stopPropagation(); e.preventDefault(); if (self.isDisabled) { return; } $(this).removeClass('file-highlighted'); }); handler($zone, 'drop', function (e) { e.preventDefault(); /** @namespace e.originalEvent.dataTransfer */ if (self.isDisabled || isEmpty(e.originalEvent.dataTransfer.files)) { return; } self.change(e, 'dragdrop'); $(this).removeClass('file-highlighted'); }); handler($(document), allEvents, function (e) { e.stopPropagation(); e.preventDefault(); }, true); }, setFileDropZoneTitle: function () { var self = this, $zone = self.$container.find('.file-drop-zone'); $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('<div class="' + self.dropZoneTitleClass + '">' + self.dropZoneTitle + '</div>'); } self.$container.removeClass('file-input-new'); addCss(self.$container, 'file-input-ajax-new'); }, errorsExist: function () { var self = this, $err; if (self.$errorContainer.find('li').length) { return true; } $err = $(document.createElement('div')).html(self.$errorContainer.html()); $err.find('span.kv-error-close').remove(); $err.find('ul').remove(); return $.trim($err.text()).length ? true : false; }, getMsgSelected: function (n) { var self = this, strFiles = n === 1 ? self.fileSingle : self.filePlural; return self.msgSelected.replace('{n}', n).replace('{files}', strFiles); }, renderThumbProgress: function () { return '<div class="file-thumb-progress hide">' + this.progressTemplate.replace(/\{percent}/g, '0') + '</div>'; }, renderFileFooter: function (caption, width) { var self = this, config = self.fileActionSettings, footer, out, template = self.getLayoutTemplate('footer'); if (self.isUploadable) { footer = template.replace(/\{actions}/g, self.renderFileActions(true, true, false, false, false)); out = footer.replace(/\{caption}/g, caption) .replace(/\{width}/g, width) .replace(/\{progress}/g, self.renderThumbProgress()) .replace(/\{indicator}/g, config.indicatorNew) .replace(/\{indicatorTitle}/g, config.indicatorNewTitle); } else { out = template.replace(/\{actions}/g, '') .replace(/\{caption}/g, caption) .replace(/\{progress}/g, '') .replace(/\{width}/g, width) .replace(/\{indicator}/g, '') .replace(/\{indicatorTitle}/g, ''); } out = replaceTags(out, self.previewThumbTags); return out; }, renderFileActions: function (showUpload, showDelete, disabled, url, key) { if (!showUpload && !showDelete) { return ''; } var self = this, vUrl = url === false ? '' : ' data-url="' + url + '"', vKey = key === false ? '' : ' data-key="' + key + '"', btnDelete = self.getLayoutTemplate('actionDelete'), btnUpload = '', template = self.getLayoutTemplate('actions'), otherButtons = self.otherActionButtons.replace(/\{dataKey}/g, vKey), config = self.fileActionSettings, removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; btnDelete = btnDelete .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); } return template .replace(/\{delete}/g, btnDelete) .replace(/\{upload}/g, btnUpload) .replace(/\{other}/g, otherButtons); }, 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); } $indicator.html(config[icon]); $indicator.attr('title', config[msg]); $thumb.addClass(css); }, 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) { self.resetUpload(); } self.validateDefaultPreview(); }, initPreview: function (isInit) { var self = this, cap = self.initialCaption || '', out; if (!previewCache.count(self.id)) { self.clearPreview(); if (isInit) { self.setCaption(cap); } else { self.initCaption(); } return; } out = previewCache.out(self.id); cap = isInit && self.initialCaption ? self.initialCaption : out.caption; self.$preview.html(out.content); self.setCaption(cap); if (!isEmpty(out.content)) { self.$container.removeClass('file-input-new'); } }, initPreviewDeletes: 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.$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; } 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({ url: vUrl, type: 'POST', dataType: 'json', data: $.extend({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.unset(self.id, 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);