diff --git a/LICENSE b/LICENSE
index 2c1a276..1365220 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014 Jordan Kasper
+Copyright (c) 2012-2015 Jordan Kasper, 2015 eusonlito, 2015 Alexei Ivashkevich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 4bc1ea9..ffe93ff 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ SimpleFilePreview is a jQuery plug-in that allows for pre-form submission
file previews on images and icon previews for non-images. The syntax is extremely
simple and the UI allows for easy CSS styling.
-**Requires jQuery 1.7+**
+**Requires: jQuery 1.9.1+, Bootstrap 3.3.4+ (for progressbar only), jQuery UI 1.11.4+ (for dialog only), context.js 1.0.1+ (for link open only)**
### Main Features
@@ -47,6 +47,71 @@ simple and the UI allows for easy CSS styling.
(default value includes most common file types in this format:
{'png': 'preview_png.png', ...}
'limit': INTEGER On multiple files, set a limit
+'removeMessage': {
+ 'prefix': STRING Prefix for remove message
+ (defaults to "Remove")
+ 'stub': STRING Stub instead of the file name for remove message
+} (defaults to "this file")
+'radio': { Display the radio buttons (if necessary) to mark one of the files (only multiple mode)
+ (defaults to null (no display the radio buttons))
+ 'name': STRING Name of input element
+ (defaults to null)
+ 'checkedItem': STRING Preselect radio button
+} (defaults to null)
+'readOnly': BOOLEAN Display with no possibility of modification
+ (defaults to false)
+'ajaxUpload': { Upload file via AJAX
+ (defaults to null)
+ 'url': STRING URL for upload file
+ (defaults to null)
+ 'progressbar': BOOLEAN Progressbar for upload file (required Bootstrap)
+ (defaults to false)
+ 'success': FUNCTION Callback for ajax success function
+ (defaults to null)
+ 'error': FUNCTION Callback for ajax error function
+ (defaults to null)
+ 'compose': FUNCTION Callback for before send FormData customization
+} (defaults to null)
+'beforeRemove' Callback for before remove element
+ (defaults to null)
+'removeDialog': { Dialog for remove file (required jQuery UI)
+ (defaults to null)
+ 'id': STRING Dialog Id
+ (defaults to "***_remove_dialog")
+ 'title': STRING Title dialog
+ (defaults to "Remove")
+ 'text': STRING Body text
+ (defaults to "Are you sure?")
+ 'ok': STRING Text for OK button
+ (defaults to "Ok")
+ 'cancel': STRING Text for Cancel button
+ (defaults to "Cancel")
+}
+'contextMenu': { Context menu for select file source
+ (defaults to null)
+ 'fileText': STRING Text for file option
+ (defaults to "Open file")
+ 'linkText': STRING Text for link option
+ (defaults to "Open link")
+ 'id': STRING Dialog Id
+ (defaults to "***_openlink_dialog")
+ 'title': STRING Title dialog
+ (defaults to "Open link")
+ 'text': STRING Body text
+ (defaults to "Please enter link to file or image")
+ 'optionName': STRING Name of input link to image or file
+ (defaults to "Link")
+ 'ok': STRING Text for OK button
+ (defaults to "Ok")
+ 'cancel': STRING Text for Cancel button
+ (defaults to "Cancel")
+ 'inputName': STRING Name of hidden input element
+ (defaults to "input_openlink_dialog")
+}
+'parentSelector': STRING Parent selector for component
+ (defaults to null)
+
+
```
### Basic Usage
@@ -73,8 +138,12 @@ $('input[type=file]').simpleFilePreview();
You can see [more examples](http://jordankasper.com/jquery/preview/examples) on my personal site.
-### Tested Browsers
+### Tested Browsers
* Firefox 16+
* Internet Explorer 8+
* Chrome 22+
+
+### NuGet repository
+
+https://www.myget.org/F/zorg-public/api/v2
\ No newline at end of file
diff --git a/jquery.simpleFilePreview.js b/jquery.simpleFilePreview.js
index e4f0d0a..684eb69 100644
--- a/jquery.simpleFilePreview.js
+++ b/jquery.simpleFilePreview.js
@@ -1,7 +1,10 @@
-/* Copyright (c) 2012 Jordan Kasper
+/* Copyright (c) 2012-2015 Jordan Kasper, 2015 eusonlito, 2015-2016 Alexei Ivashkevich
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* Copyright notice and license must remain intact for legal use
-* Requires: jQuery 1.2+
+* Requires: jQuery 1.9.1+
+* Bootstrap 3.3.4+ (for progressbar only)
+* jQuery UI 1.11.4+ (for dialog only)
+* context.js 1.0.1 (for context menu only)
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
@@ -9,7 +12,7 @@
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Fore more usage documentation and examples, visit:
* http://jordankasper.com/jquery
@@ -33,7 +36,46 @@ $('input[type=file]').simpleFilePreview({
'iconPath': '', // String The path to the folder containing icon images (when a preview is unavailable) - should be absolute, but if relative, must be relative to the page the file input is on
'defaultIcon': 'preview_file.png', // String The file name to use for the defualt preview icon (when a proper file-type-specific icon cannot be found)
'icons': {'png': 'preview_png.png', ...} // Object A mapping of file type (second half of mime type) to icon image file (used in combination with the "iconPath" option)
- 'limit': 0 // Limit files on multiple option
+ 'limit': 0, // Limit files on multiple option
+ 'removeMessage': {
+ 'prefix': 'Remove', // Prefix for remove message
+ 'stub': 'this file' // Stub instead of the file name for remove message
+ },
+ 'radio': { // Display the radio buttons (if necessary) to mark one of the files (only multiple mode)
+ 'name': string, // Name of input element
+ 'checkedItem': string // Preselect radio button
+ },
+ 'readOnly': false, // Display with no possibility of modification
+ 'ajaxUpload': { // Upload file via AJAX
+ 'url': string, // URL for upload file
+ 'progressbar': false, // Progressbar for upload file (required Bootstrap)
+ 'success': function (data, textStatus, jqXHR, inputFileElement), // Callback for ajax success function
+ 'error': function (jqXHR, textStatus, errorThrown, inputFileElement), // Callback for ajax error function
+ 'compose': function (formData) // Callback for before send FormData customization
+ },
+ 'beforeRemove': function (element), // Callback for before remove element
+ 'removeDialog': { // Dialog for remove file (required jQuery UI)
+ 'id': string, // Dialog Id
+ 'title': string, // Title dialog
+ 'text': string, // Body text
+ 'ok': string, // Text for OK button
+ 'cancel': string, // Text for Cancel button
+ 'options': object // jQuery UI Dialog options
+ },
+ 'contextMenu': { // Context menu for select file source
+ 'fileText': string, // Text for file option
+ 'linkText': string, // Text for link option
+ 'id': string, // Dialog Id
+ 'title': string, // Title dialog
+ 'text': string, // Body text
+ 'optionName': string, // Name of input link to image or file
+ 'ok': string, // Text for OK button
+ 'cancel': string, // Text for Cancel button
+ 'inputName': string // Name of hidden input element
+ 'options': object // jQuery UI Dialog options
+ },
+ 'parentSelector': string, // Parent selector for component
+ 'defaultOpen': 'file', // Default action on left button click: open file (file) or open link (link)
});
*
* TODO:
@@ -44,195 +86,186 @@ $('input[type=file]').simpleFilePreview({
* 0.1 Initial release
*
*/
-;(function($) {
+; (function ($) {
'use strict';
- $.fn.simpleFilePreview = function(options) {
+ $.fn.simpleFilePreview = function (options) {
if (!this || !this.length) {
return this;
}
// Set up options (and defaults)
options = options ? options : {};
- options = $.extend({}, $.simpleFilePreview.defaults, options);
+ options = $.extend(true, {}, $.simpleFilePreview.defaults, options);
- this.each(function() {
- setup($(this), options);
- });
+ // read only mode and radio button incompatible
+ if (options.readOnly) {
+ options.radio = null;
+ }
- // set up global events
- if ($.simpleFilePreview.init) {
- return this;
+ if (options.removeDialog) {
+ if (!options.removeDialog.id) { options.removeDialog.id = $(this).id + '_remove_dialog'; }
+ if (!options.removeDialog.title) { options.removeDialog.title = 'Remove'; }
+ if (!options.removeDialog.text) { options.removeDialog.text = 'Are you sure?'; }
+ if (!options.removeDialog.ok) { options.removeDialog.ok = 'Ok'; }
+ if (!options.removeDialog.cancel) { options.removeDialog.cancel = 'Cancel'; }
+ if (!options.removeDialog.options) { options.removeDialog.options = {}; }
+
+ $('
' +
+ '' +
+ options.removeDialog.text +
+ '
').dialog($.extend({
+ autoOpen: false,
+ resizable: false,
+ modal: true
+ }, options.removeDialog.options));
+ }
+
+ if (options.contextMenu) {
+ if (!options.contextMenu.fileText) { options.contextMenu.fileText = 'Open file'; }
+ if (!options.contextMenu.linkText) { options.contextMenu.linkText = 'Open link'; }
+ if (!options.contextMenu.id) { options.contextMenu.id = $(this).id + '_openlink_dialog'; }
+ if (!options.contextMenu.title) { options.contextMenu.title = 'Open link'; }
+ if (!options.contextMenu.text) { options.contextMenu.text = 'Please enter link to file or image'; }
+ if (!options.contextMenu.optionName) { options.contextMenu.optionName = 'Link'; }
+ if (!options.contextMenu.ok) { options.contextMenu.ok = 'Ok'; }
+ if (!options.contextMenu.cancel) { options.contextMenu.cancel = 'Cancel'; }
+ if (!options.contextMenu.inputName) { options.contextMenu.inputName = 'input_openlink_dialog'; }
+ if (!options.contextMenu.options) { options.contextMenu.options = {}; }
+
+ var $gbody = $('body');
+ var gbodyOverflow = $gbody.css('overflow');
+ $('
' +
+ options.contextMenu.text +
+ '
' +
+ '
').dialog($.extend({
+ autoOpen: false,
+ resizable: false,
+ modal: true,
+ position: { my: "center", at: "center", of: window },
+ open: function (event, ui) {
+ gbodyOverflow = $gbody.css('overflow');
+ $gbody.css('overflow', 'hidden');
+ },
+ close: function (event, ui) {
+ $gbody.css('overflow', gbodyOverflow);
+ },
+ buttons: [
+ {
+ text: options.contextMenu.ok,
+ click: function () {
+ var $element = $(((options.parentSelector) ? options.parentSelector : 'body') + ' li:has(a.simpleFilePreview_input:visible) input[name^="' + options.contextMenu.inputName + '"]');
+ var $link = $('#' + options.contextMenu.id + '_link');
+ $element.val($link.val());
+ fileProcess(options, $element, 'link');
+ $link.val("");
+ $(this).dialog("close");
+ }
+ }, {
+ text: options.contextMenu.cancel,
+ click: function () {
+ $(this).dialog("close");
+ }
+ }]
+ }, options.contextMenu.options));
+
+ var selector = ((options.parentSelector) ? options.parentSelector : 'body') + ' li:has(a.simpleFilePreview_input:visible)';
+ context.init({ preventDoubleContext: false });
+ context.settings({ compress: true });
+ context.attach(selector, [
+ {
+ text: options.contextMenu.fileText,
+ action: function (e) {
+ openFileDialog(e, options, selector);
+ }
+ }, {
+ text: options.contextMenu.linkText,
+ action: function (e) {
+ openLinkDialog(e, options);
+ }
+ }]);
}
- var $body = $('body');
+ this.each(function () {
+ setup($(this), options);
+ });
- $.simpleFilePreview.init = true;
+ var $body = ((options.parentSelector) ? $(options.parentSelector + ' .simpleFilePreview_body') : $(this).closest('.simpleFilePreview_body'));
// open file browser dialog on click of styled "button"
- $body.on('click', '.simpleFilePreview_input', function(e) {
- $(this).closest('.simpleFilePreview').find('input.simpleFilePreview_formInput').trigger('click');
- e.preventDefault();
+ $body.on('click', '.simpleFilePreview_input', function (e) {
+ if (options.defaultOpen == 'link') {
+ openLinkDialog(e, options);
+ } else {
+ openFileDialog(e, options, e.currentTarget);
+ }
});
// on click of the actual input (which is invisible), check to see if
// we need to clear the input (which is the default action for this plugin)
- $body.on('click', '.simpleFilePreview input.simpleFilePreview_formInput', function(e) {
+ $body.on('click', '.simpleFilePreview input.simpleFilePreview_formInput', function (e) {
if (!$(this).val().length) {
return this;
}
- $(this).closest('.simpleFilePreview').find('.simpleFilePreview_preview').trigger('click');
+ if (!options.readOnly) {
+ $(this).closest('.simpleFilePreview').find('.simpleFilePreview_preview').trigger('click');
+ }
e.preventDefault();
});
// when file input changes, get file contents and show preview (if it's an image)
- $body.on('change', '.simpleFilePreview input.simpleFilePreview_formInput', function(e) {
- var $parents = $(this).closest('.simpleFilePreview');
-
- // if it's a multi-select, add another selection box to the end
- // NOTE: this is done first since we clone the previous input
- // NOTE: the second check is there because IE 8 fires multiple change events for no good reason
- if (($parents.attr('data-sfpallowmultiple') == 1) && !$parents.find('.simpleFilePreview_preview').length) {
- var newId = $.simpleFilePreview.uid++;
- var $newN = $parents.clone(true).attr('id', "simpleFilePreview_" + newId);
-
- $newN.find('input.simpleFilePreview_formInput')
- .attr('id', $newN.find('input.simpleFilePreview_formInput').attr('id') + '_' + newId)
- .val('');
-
- $parents.after($newN);
-
- var nw = $parents.closest('.simpleFilePreview_multi').width('+=' + $newN.outerWidth(true)).width();
-
- if (nw > $parents.closest('.simpleFilePreview_multiClip').width()) {
- $parents.closest('.simpleFilePreview_multiUI').find('.simpleFilePreview_shiftRight').trigger('click');
- }
- }
-
- if (this.files && this.files[0]) {
- var exp = new RegExp("^image\/(" + $.simpleFilePreview.previewFileTypes + ")$");
-
- if (exp.test(this.files[0].type.toLowerCase()) && window.FileReader) {
- // show preview of image file
- var $FR = new FileReader();
-
- $FR.onload = function(e) {
- addOrChangePreview($parents, e.target.result, '', options);
- };
-
- $FR.readAsDataURL(this.files[0]);
- } else {
- // show icon if not an image upload
- var m = this.files[0].type.toLowerCase().match(/^\s*[^\/]+\/([a-zA-Z0-9\-\.]+)\s*$/);
-
- if (m && m[1] && options.icons[m[1]]) {
- addOrChangePreview($parents, options.icons[m[1]], getFilename(this.value), options);
- } else {
- addOrChangePreview($parents, options.defaultIcon, getFilename(this.value), options);
- }
- }
-
- return this;
- }
-
- // Any browser not supporting the File API (and FileReader)
-
- // Some versions of IE don't have real paths, and can't support
- // any other way to do file preview without uploading to the server
- // If a browser does report a valid path (IE or otherwise), then
- // we'll try to get the file preview
-
- var exp = new RegExp("^(" + $.simpleFilePreview.previewFileTypes + ")$");
-
- var ext = getFileExt(this.value);
- ext = ext ? ext.toLowerCase() : null;
-
- if (ext && !(/fakepath/.test(this.value.toLowerCase())) && exp.test(e)) {
- // older versions of IE (and some other browsers) report the local
- // file path, so try to get a preview that way
- addOrChangePreview($parents, "file://" + this.value, '', options);
- } else {
- // not an image (or using fakepath), so no preview anyway
- if (options.icons[ext]) {
- addOrChangePreview($parents, options.icons[ext], getFilename(this.value), options);
- } else {
- addOrChangePreview($parents, options.defaultIcon, getFilename(this.value), options);
- }
- }
+ $body.on('change', '.simpleFilePreview input.simpleFilePreview_formInput', function (e) {
+ fileProcess(options, $(this), 'file');
});
// show or hide "remove" icon for file preview/icon
- $body.on('mouseover', '.simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput', function() {
- var $parents = $(this).closest('.simpleFilePreview');
+ $body.on('mouseover', '.simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput', function () {
+ if (!options.readOnly) {
+ var $parents = $(this).closest('.simpleFilePreview');
- if ($parents.find('.simpleFilePreview_preview').is(':visible')) {
- $parents.find('.simpleFilePreview_remove').show();
+ if ($parents.find('.simpleFilePreview_preview').is(':visible')) {
+ $parents.find('.simpleFilePreview_remove').show();
+ }
}
});
- $body.on('mouseout', '.simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput', function() {
+ $body.on('mouseout', '.simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput', function () {
$(this).closest('.simpleFilePreview').find('.simpleFilePreview_remove').hide();
})
// remove file when preview/icon is clicked
- $body.on('click', '.simpleFilePreview_preview', function() {
- var $this = $(this);
- var $parents = $this.closest('.simpleFilePreview');
-
- if ($parents.attr('data-sfpallowmultiple') == 1 && $parents.siblings('.simpleFilePreview').length) {
- if ($parents.hasClass('simpleFilePreview_existing')) {
- $parents.parent().append("");
- }
-
- limit($this, options, 1);
-
- $parents.closest('.simpleFilePreview_multi').width('-=' + $parents.width());
- $parents.remove();
-
- return this;
- }
-
- // if it was an existing file, show file input and add "removeFiles" hidden input
- if ($parents.hasClass('simpleFilePreview_existing')) {
- $parents.find('input.simpleFilePreview_formInput').show();
- $parents.append("");
- $parents.removeClass('simpleFilePreview_existing'); // no longer needed
- }
-
- limit($this, options, 1);
-
- // kill value in the input
- var $input = $parents.find('input.simpleFilePreview_formInput').val('');
-
- // Some browsers (*cough*IE*cough*) do not allow us to set val()
- // on a file input, so we have to clone it without the value
- if ($input && $input.length && $input.val().length) {
- var attr = $input.get(0).attributes;
- var a = "";
-
- for (var j = 0, l = attr.length; j < l; ++j) {
- if (attr[j].name != 'value' && attr[j].name != 'title') {
- a += attr[j].name + "='" + $input.attr(attr[j].name) + "' ";
- }
+ $body.on('click', '.simpleFilePreview_preview', function () {
+ if (!options.readOnly) {
+ var $pic = $(this);
+ if (!options.removeDialog) {
+ remove($pic, options);
+ } else {
+ var $dialog = $('#' + options.removeDialog.id);
+ $dialog.dialog('option',
+ 'buttons', [{
+ text: options.removeDialog.ok,
+ click: function () {
+ remove($pic, options);
+ $(this).dialog("close");
+ }
+ }, {
+ text: options.removeDialog.cancel,
+ click: function () {
+ $(this).dialog("close");
+ }
+ }]);
+ $dialog.dialog("open");
}
-
- $input.before('');
- $input.remove();
}
-
- // remove the preview element
- $this.remove();
- $parents.find('.simpleFilePreview_filename').remove();
-
- // show styled input "button"
- $parents.find('.simpleFilePreview_remove').hide().end()
- .find('.simpleFilePreview_input').show();
});
// shift buttons for multi-selects
- $body.on('click', '.simpleFilePreview_shiftRight', function() {
+ $body.on('click', '.simpleFilePreview_shiftRight', function () {
var ul = $(this).closest('.simpleFilePreview_multiUI').find('.simpleFilePreview_multi');
var width = parseInt(ul.css('left')) + ul.width();
@@ -244,7 +277,7 @@ $('input[type=file]').simpleFilePreview({
}
});
- $body.on('click', '.simpleFilePreview_shiftLeft', function() {
+ $body.on('click', '.simpleFilePreview_shiftLeft', function () {
var ul = $(this).closest('.simpleFilePreview_multiUI').find('.simpleFilePreview_multi');
var left = parseInt(ul.css('left'));
@@ -260,7 +293,64 @@ $('input[type=file]').simpleFilePreview({
return this;
};
- var limit = function($this, options, add) {
+ var remove = function ($this, options) {
+ var $parents = $this.closest('.simpleFilePreview');
+
+ if (options.beforeRemove != null) {
+ options.beforeRemove($parents);
+ }
+
+ if ($parents.attr('data-sfpallowmultiple') == 1 && $parents.siblings('.simpleFilePreview').length) {
+ if ($parents.hasClass('simpleFilePreview_existing')) {
+ $parents.parent().append("");
+ }
+
+ limit($this, options, 1);
+
+ $parents.closest('.simpleFilePreview_multi').width('-=' + $parents.width());
+ $parents.remove();
+
+ return this;
+ }
+
+ // if it was an existing file, show file input and add "removeFiles" hidden input
+ if ($parents.hasClass('simpleFilePreview_existing')) {
+ $parents.find('input.simpleFilePreview_formInput').show();
+ $parents.append("");
+ $parents.removeClass('simpleFilePreview_existing'); // no longer needed
+ }
+
+ limit($this, options, 1);
+
+ // kill value in the input
+ var $input = $parents.find('input.simpleFilePreview_formInput').val('');
+
+ // Some browsers (*cough*IE*cough*) do not allow us to set val()
+ // on a file input, so we have to clone it without the value
+ if ($input && $input.length && $input.val().length) {
+ var attr = $input.get(0).attributes;
+ var a = "";
+
+ for (var j = 0, l = attr.length; j < l; ++j) {
+ if (attr[j].name != 'value' && attr[j].name != 'title') {
+ a += attr[j].name + "='" + $input.attr(attr[j].name) + "' ";
+ }
+ }
+
+ $input.before('');
+ $input.remove();
+ }
+
+ // remove the preview element
+ $this.remove();
+ $parents.find('.simpleFilePreview_filename').remove();
+
+ // show styled input "button"
+ $parents.find('.simpleFilePreview_remove').hide().end()
+ .find('.simpleFilePreview_input').show();
+ };
+
+ var limit = function ($this, options, add) {
if (!options.limit) {
return false;
}
@@ -275,37 +365,44 @@ $('input[type=file]').simpleFilePreview({
}
};
- var setup = function(these, options) {
+ var setup = function (these, options) {
var isMulti = these.is('[multiple]');
// "multiple" removed because it's handled later manually
these = these.removeAttr('multiple').addClass('simpleFilePreview_formInput');
+ // multiple mode and radio button incompatible
+ if (!isMulti) {
+ options.radio = null;
+ }
+
// wrap input with necessary structure
var $html = $("<" + (isMulti ? 'li' : 'div')
+ " id='simpleFilePreview_" + ($.simpleFilePreview.uid++) + "'"
- + " class='simpleFilePreview' data-sfpallowmultiple='" + (isMulti ? 1 : 0) + "'>"
+ + " class='simpleFilePreview" + ((options.radio) ? " simpleFilePreview_withRadio" : "") + "' data-sfpallowmultiple='" + (isMulti ? 1 : 0) + "'>"
+ ""
+ options.buttonContent + ""
+ "" + options.removeContent + ""
+ + ((options.radio) ? "" : "")
+ + ((options.ajaxUpload.progressbar) ? "
" : "")
+ + ((options.contextMenu) ? '' : '')
+ "" + (isMulti ? 'li' : 'div') + ">");
these.before($html);
$html.append(these);
- // mostly for IE, the file input must be sized the same as the container,
- // opacity 0, and z-indexed above other elements within the preview container
- these.css({
- width: ($html.width() + 'px'),
- height: ($html.height() + 'px')
- });
+ if (options.readOnly) {
+ $html.hide();
+ }
// if it's a multi-select we use multiple separate inputs instead to support file preview
if (isMulti) {
- $html.wrap("
");
+ $html.wrap("
");
$html.closest('.simpleFilePreview_multiUI')
.prepend("" + options.shiftRight + "")
.append("" + options.shiftLeft + "");
+ } else {
+ $html.wrap("");
}
var exists = options.existingFiles;
@@ -328,9 +425,14 @@ $('input[type=file]').simpleFilePreview({
var ni = $.simpleFilePreview.uid++;
var nn = $html.clone(true).attr('id', "simpleFilePreview_" + ni);
+ if (options.contextMenu) {
+ nn.find('[name^="' + options.contextMenu.inputName + '"]').attr('name', options.contextMenu.inputName + '[' + ++$.simpleFilePreview.linkid + ']').val('');
+ }
+
nn.addClass('simpleFilePreview_existing')
.attr('data-sfprid', arr ? exists[i] : i)
.find('input.simpleFilePreview_formInput').remove();
+ nn.show();
$html.before(nn);
@@ -344,10 +446,18 @@ $('input[type=file]').simpleFilePreview({
} else {
addOrChangePreview(nn, options.defaultIcon, getFilename(exists[i]), options);
}
+
+ if (options.radio) {
+ nn.find('input.simpleFilePreview_radio').val(nn.attr('data-sfprid'));
+ }
}
$('.simpleFilePreview_multi').width($html.outerWidth(true) * $html.parent().find('.simpleFilePreview').length);
+ if (options.radio) {
+ $html.closest('.simpleFilePreview_multi').find("input[type='radio'][value='" + options.radio.checkedItem + "']").prop("checked", true);
+ }
+
return these;
}
@@ -385,9 +495,9 @@ $('input[type=file]').simpleFilePreview({
return these;
};
- var addOrChangePreview = function($parents, src, filename, options) {
+ var addOrChangePreview = function ($parents, src, filename, options) {
filename = filename ? filename : null;
- src = ((new RegExp('[/\\\\]')).test(src) ? '' : (options.iconPath)) + src
+ src = ((new RegExp('^(http|https)://').test(src)) ? src : (((new RegExp('[/\\\\]')).test(src) ? '' : (options.iconPath)) + src));
$parents.find('.simpleFilePreview_input').hide();
@@ -399,10 +509,10 @@ $('input[type=file]').simpleFilePreview({
$parents.append("");
+ + " title='" + options.removeMessage.prefix + ' ' + (filename ? filename : options.removeMessage.stub) + "' />");
// for tooltips
- $parents.find('input.simpleFilePreview_formInput').attr('title', "Remove " + (filename ? filename : 'this file'));
+ $parents.find('input.simpleFilePreview_formInput').attr('title', options.removeMessage.prefix + " " + (filename ? filename : options.removeMessage.stub));
}
limit($parents, options);
@@ -421,7 +531,7 @@ $('input[type=file]').simpleFilePreview({
}
};
- var getFilename = function($parents) {
+ var getFilename = function ($parents) {
var m = $parents.match(/[\/\\]([^\/\\]+)$/);
if (m && m[1] && m[1].length) {
@@ -431,7 +541,7 @@ $('input[type=file]').simpleFilePreview({
return null;
};
- var getFileExt = function($parents) {
+ var getFileExt = function ($parents) {
var m = $parents.match(/[\.]([^\/\\\.]+)$/);
if (m && m[1] && m[1].length) {
@@ -441,9 +551,179 @@ $('input[type=file]').simpleFilePreview({
return null;
};
+ var openLinkDialog = function (e, options) {
+ window.scrollTo(0, 0);
+ $('#' + options.contextMenu.id).dialog("open");
+ };
+
+ var openFileDialog = function (e, options, that) {
+ $(that).closest('.simpleFilePreview').find('input.simpleFilePreview_formInput').trigger('click');
+ e.preventDefault();
+ };
+
+ var fileProcess = function (options, $this, type) {
+ if (!options.readOnly) {
+ var _this = $this.get()[0];
+ if (options.ajaxUpload) {
+ var fd = new FormData();
+ var cutNameToken = _this.name.indexOf('[');
+ var value = ((type == 'file') ? _this.files[0] : ((type == 'link') ? $this.val() : ''));
+ if (cutNameToken > -1) {
+ fd.append(_this.name.substr(0, cutNameToken), value);
+ } else {
+ fd.append(_this.name, value);
+ }
+
+ if (options.ajaxUpload.compose) {
+ options.ajaxUpload.compose(fd);
+ }
+
+ if (options.ajaxUpload.progressbar) {
+ $this.parent().find('.simpleFilePreview_progress').show();
+ }
+
+ $.ajax({
+ url: options.ajaxUpload.url,
+ type: "POST",
+ data: fd,
+ dataType: 'json',
+ contentType: false,
+ processData: false,
+ success: function (data, textStatus, jqXHR) {
+ if (options.ajaxUpload.progressbar) {
+ $this.parent().find('.simpleFilePreview_progress').hide();
+ }
+ if (options.ajaxUpload.success) {
+ options.ajaxUpload.success(data, textStatus, jqXHR, _this);
+ }
+ },
+ error: function (jqXHR, textStatus, errorThrown) {
+ if (options.ajaxUpload.progressbar) {
+ $this.parent().find('.simpleFilePreview_progress').hide();
+ }
+ if (options.ajaxUpload.error) {
+ options.ajaxUpload.error(jqXHR, textStatus, errorThrown, _this);
+ }
+ },
+ xhr: function () {
+ var xhr = new window.XMLHttpRequest();
+ //Upload progress
+ xhr.upload.addEventListener("progress", function (evt) {
+ if (evt.lengthComputable && options.ajaxUpload.progressbar) {
+ var percentComplete = evt.loaded * 100 / evt.total;
+ var progressbar = $this.parent().find('.simpleFilePreview_progress div');
+ progressbar.css({ width: percentComplete + '%' }).attr('aria-valuenow', percentComplete);
+ }
+ }, false);
+ return xhr;
+ }
+ });
+ }
+
+ var $parents = $this.closest('.simpleFilePreview');
+
+ // if it's a multi-select, add another selection box to the end
+ // NOTE: this is done first since we clone the previous input
+ // NOTE: the second check is there because IE 8 fires multiple change events for no good reason
+ if (($parents.attr('data-sfpallowmultiple') == 1) && !$parents.find('.simpleFilePreview_preview').length) {
+ var newId = $.simpleFilePreview.uid++;
+ var $newN = $parents.clone(true).attr('id', "simpleFilePreview_" +newId);
+
+ if (options.contextMenu) {
+ $newN.find('[name^="' + options.contextMenu.inputName + '"]').attr('name', options.contextMenu.inputName + '[' + ++$.simpleFilePreview.linkid + ']').val('');
+ }
+
+ if (options.ajaxUpload.progressbar) {
+ $newN.find('.simpleFilePreview_progress').hide();
+ }
+
+ $newN.find('input.simpleFilePreview_formInput')
+ .attr('id', $newN.find('input.simpleFilePreview_formInput').attr('id') + '_' + newId)
+ .attr('name', function (index, previousValue) {
+ var previousName = $parents.find('input.simpleFilePreview_formInput').attr('name');
+ var inputIndex = parseInt(previousName.substring(previousName.indexOf('[') + 1, previousName.indexOf(']')));
+ return (!isNaN(inputIndex)) ? previousName.substring(0, previousName.indexOf('[') + 1) + ++inputIndex + previousName.substring(previousName.indexOf(']')) : previousValue;
+ })
+ .val('');
+
+ $parents.after($newN);
+
+ var nw = $parents.closest('.simpleFilePreview_multi').width('+=' + $newN.outerWidth(true)).width();
+
+ if (nw > $parents.closest('.simpleFilePreview_multiClip').width()) {
+ $parents.closest('.simpleFilePreview_multiUI').find('.simpleFilePreview_shiftRight').trigger('click');
+ }
+ }
+
+ var ext = getFileExt(_this.value);
+ ext = ext ? ext.toLowerCase(): null;
+
+ if ((type == 'file') && _this.files && _this.files[0]) {
+ var exp = new RegExp("^image\/(" + $.simpleFilePreview.previewFileTypes + ")$");
+
+ if (exp.test(_this.files[0].type.toLowerCase()) && window.FileReader) {
+ // show preview of image file
+ var $FR = new FileReader();
+
+ $FR.onload = function (e) {
+ addOrChangePreview($parents, e.target.result, '', options);
+ };
+
+ $FR.readAsDataURL(_this.files[0]);
+ } else {
+ // show icon if not an image upload
+ var m = _this.files[0].type.toLowerCase().match(/^\s*[^\/]+\/([a-zA-Z0-9\-\.]+)\s*$/);
+
+ if (m && m[1] && options.icons[m[1]]) {
+ addOrChangePreview($parents, options.icons[m[1]], getFilename(_this.value), options);
+ } else {
+ addOrChangePreview($parents, options.defaultIcon, getFilename(_this.value), options);
+ }
+ }
+
+ if (options.radio) {
+ $parents.find('input.simpleFilePreview_radio').val($parents.context.files[0].name);
+ }
+
+ return _this;
+ }
+
+ if (type == 'file') {
+ // Any browser not supporting the File API (and FileReader)
+ // Some versions of IE don't have real paths, and can't support
+ // any other way to do file preview without uploading to the server
+ // If a browser does report a valid path (IE or otherwise), then
+ // we'll try to get the file preview
+ var exp = new RegExp("^(" +$.simpleFilePreview.previewFileTypes + ")$");
+
+ if (ext && !(/fakepath/.test(_this.value.toLowerCase())) && exp.test(e)) {
+ // older versions of IE (and some other browsers) report the local
+ // file path, so try to get a preview that way
+ addOrChangePreview($parents, "file://" +_this.value, '', options);
+ } else {
+ // not an image (or using fakepath), so no preview anyway
+ if (options.icons[ext]) {
+ addOrChangePreview($parents, options.icons[ext], getFilename(_this.value), options);
+ } else {
+ addOrChangePreview($parents, options.defaultIcon, getFilename(_this.value), options);
+ }
+ }
+ }
+
+ if (type == 'link') {
+ var exp = new RegExp("^(" + $.simpleFilePreview.previewFileTypes + ")$");
+ if (exp.test(ext)) {
+ addOrChangePreview($parents, $this.val(), '', options);
+ } else {
+ addOrChangePreview($parents, _this.value, getFilename(_this.value), options);
+ }
+ }
+ }
+ };
+
// Static properties
$.simpleFilePreview = {
- defaults: {
+ defaults: {
'buttonContent': 'Add File',
'removeContent': 'X',
'existingFiles': null, // array or object. if object, key is used in the remove hidden input
@@ -452,6 +732,18 @@ $('input[type=file]').simpleFilePreview({
'iconPath': '',
'defaultIcon': 'preview_file.png',
'limit': 0,
+ 'removeMessage': {
+ 'prefix': 'Remove',
+ 'stub': 'this file',
+ },
+ 'radio': null,
+ 'readOnly': false,
+ 'ajaxUpload': null,
+ 'beforeRemove': null,
+ 'removeDialog': null,
+ 'contextMenu': null,
+ 'parentSelector': null,
+ 'defaultOpen': 'file',
'icons': {
'png': 'preview_png.png',
'gif': 'preview_png.png',
@@ -493,6 +785,7 @@ $('input[type=file]').simpleFilePreview({
}
},
uid: 0,
+ linkid: 0,
init: false,
previewFileTypes: 'p?jpe?g|png|gif|bmp|svg'
};
diff --git a/jquery.simpleFilePreview.min.js b/jquery.simpleFilePreview.min.js
index 4a87b07..04737b4 100644
--- a/jquery.simpleFilePreview.min.js
+++ b/jquery.simpleFilePreview.min.js
@@ -1,6 +1,6 @@
/*!
* jquery.simpleFilePreview v0.1.0 (https://github.com/jakerella/jquerySimpleFilePreview)
- * Copyright (c) 2012 Jordan Kasper
+ * Copyright (c) 2012-2015 Jordan Kasper, 2015 eusonlito, 2015-2016 Alexei Ivashkevich
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
-!function(a){"use strict";a.fn.simpleFilePreview=function(g){if(!this||!this.length)return this;if(g=g?g:{},g=a.extend({},a.simpleFilePreview.defaults,g),this.each(function(){c(a(this),g)}),a.simpleFilePreview.init)return this;var h=a("body");return a.simpleFilePreview.init=!0,h.on("click",".simpleFilePreview_input",function(b){a(this).closest(".simpleFilePreview").find("input.simpleFilePreview_formInput").trigger("click"),b.preventDefault()}),h.on("click",".simpleFilePreview input.simpleFilePreview_formInput",function(b){return a(this).val().length?(a(this).closest(".simpleFilePreview").find(".simpleFilePreview_preview").trigger("click"),void b.preventDefault()):this}),h.on("change",".simpleFilePreview input.simpleFilePreview_formInput",function(b){var c=a(this).closest(".simpleFilePreview");if(1==c.attr("data-sfpallowmultiple")&&!c.find(".simpleFilePreview_preview").length){var h=a.simpleFilePreview.uid++,i=c.clone(!0).attr("id","simpleFilePreview_"+h);i.find("input.simpleFilePreview_formInput").attr("id",i.find("input.simpleFilePreview_formInput").attr("id")+"_"+h).val(""),c.after(i);var j=c.closest(".simpleFilePreview_multi").width("+="+i.outerWidth(!0)).width();j>c.closest(".simpleFilePreview_multiClip").width()&&c.closest(".simpleFilePreview_multiUI").find(".simpleFilePreview_shiftRight").trigger("click")}if(this.files&&this.files[0]){var k=new RegExp("^image/("+a.simpleFilePreview.previewFileTypes+")$");if(k.test(this.files[0].type.toLowerCase())&&window.FileReader){var l=new FileReader;l.onload=function(a){d(c,a.target.result,"",g)},l.readAsDataURL(this.files[0])}else{var m=this.files[0].type.toLowerCase().match(/^\s*[^\/]+\/([a-zA-Z0-9\-\.]+)\s*$/);m&&m[1]&&g.icons[m[1]]?d(c,g.icons[m[1]],e(this.value),g):d(c,g.defaultIcon,e(this.value),g)}return this}var k=new RegExp("^("+a.simpleFilePreview.previewFileTypes+")$"),n=f(this.value);n=n?n.toLowerCase():null,n&&!/fakepath/.test(this.value.toLowerCase())&&k.test(b)?d(c,"file://"+this.value,"",g):g.icons[n]?d(c,g.icons[n],e(this.value),g):d(c,g.defaultIcon,e(this.value),g)}),h.on("mouseover",".simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput",function(){var b=a(this).closest(".simpleFilePreview");b.find(".simpleFilePreview_preview").is(":visible")&&b.find(".simpleFilePreview_remove").show()}),h.on("mouseout",".simpleFilePreview_preview, .simpleFilePreview input.simpleFilePreview_formInput",function(){a(this).closest(".simpleFilePreview").find(".simpleFilePreview_remove").hide()}),h.on("click",".simpleFilePreview_preview",function(){var c=a(this),d=c.closest(".simpleFilePreview");if(1==d.attr("data-sfpallowmultiple")&&d.siblings(".simpleFilePreview").length)return d.hasClass("simpleFilePreview_existing")&&d.parent().append(""),b(c,g,1),d.closest(".simpleFilePreview_multi").width("-="+d.width()),d.remove(),this;d.hasClass("simpleFilePreview_existing")&&(d.find("input.simpleFilePreview_formInput").show(),d.append(""),d.removeClass("simpleFilePreview_existing")),b(c,g,1);var e=d.find("input.simpleFilePreview_formInput").val("");if(e&&e.length&&e.val().length){for(var f=e.get(0).attributes,h="",i=0,j=f.length;i'),e.remove()}c.remove(),d.find(".simpleFilePreview_filename").remove(),d.find(".simpleFilePreview_remove").hide().end().find(".simpleFilePreview_input").show()}),h.on("click",".simpleFilePreview_shiftRight",function(){var b=a(this).closest(".simpleFilePreview_multiUI").find(".simpleFilePreview_multi"),c=parseInt(b.css("left"))+b.width();if(c>b.parent().width()){var d=b.find("li:first");b.animate({left:"-="+d.outerWidth(!0)})}}),h.on("click",".simpleFilePreview_shiftLeft",function(){var b=a(this).closest(".simpleFilePreview_multiUI").find(".simpleFilePreview_multi"),c=parseInt(b.css("left"));if(c<0){var d=b.find("li:first").outerWidth(!0);b.animate({left:c+d<1?"+="+d:0})}}),this};var b=function(a,b,c){if(!b.limit)return!1;var d=a.closest(".simpleFilePreview_multi").find("> li");c=c?c:0,d.length>b.limit+c?d.last().hide():d.last().show()},c=function(b,c){var g=b.is("[multiple]");b=b.removeAttr("multiple").addClass("simpleFilePreview_formInput");var h=a("<"+(g?"li":"div")+" id='simpleFilePreview_"+a.simpleFilePreview.uid++ +"' class='simpleFilePreview' data-sfpallowmultiple='"+(g?1:0)+"'>"+c.buttonContent+""+c.removeContent+""+(g?"li":"div")+">");b.before(h),h.append(b),b.css({width:h.width()+"px",height:h.height()+"px"}),g&&(h.wrap("
"),h.closest(".simpleFilePreview_multiUI").prepend(""+c.shiftRight+"").append(""+c.shiftLeft+""));var i=c.existingFiles;if(!i)return g&&a(".simpleFilePreview_multi").width(h.outerWidth(!0)*h.parent().find(".simpleFilePreview").length),b;var j=new RegExp("^("+a.simpleFilePreview.previewFileTypes+")$");if(g){var k=a.isArray(i)?1:0;for(var l in i){var m=a.simpleFilePreview.uid++,n=h.clone(!0).attr("id","simpleFilePreview_"+m);n.addClass("simpleFilePreview_existing").attr("data-sfprid",k?i[l]:l).find("input.simpleFilePreview_formInput").remove(),h.before(n);var o=f(i[l]);o=o?o.toLowerCase():null,o&&j.test(o)?d(n,i[l],"",c):c.icons[o]?d(n,c.icons[o],e(i[l]),c):d(n,c.defaultIcon,e(i[l]),c)}return a(".simpleFilePreview_multi").width(h.outerWidth(!0)*h.parent().find(".simpleFilePreview").length),b}var p=null,k=a.isArray(i)?1:0;for(var l in i)p={id:k?i[l]:l,file:i[l]};if(!p)return b;h.attr("data-sfprid",p.id).addClass("simpleFilePreview_existing").find("input.simpleFilePreview_formInput").hide();var o=f(p.file);return o=o?o.toLowerCase():null,o&&j.test(o)?d(h,p.file,"",c):c.icons[o]?d(h,c.icons[o],e(p.file),c):d(h,c.defaultIcon,e(p.file),c),b},d=function(a,c,d,e){d=d?d:null,c=(new RegExp("[/\\\\]").test(c)?"":e.iconPath)+c,a.find(".simpleFilePreview_input").hide();var f=a.find(".simpleFilePreview_preview");if(f&&f.length?f.attr("src",c):(a.append(""),a.find("input.simpleFilePreview_formInput").attr("title","Remove "+(d?d:"this file"))),b(a,e),!d)return null;var g=a.find(".simpleFilePreview_filename");g&&g.length?g.text(d):a.append(""+d+"").find(".simpleFilePreview_filename")},e=function(a){var b=a.match(/[\/\\]([^\/\\]+)$/);return b&&b[1]&&b[1].length?b[1]:null},f=function(a){var b=a.match(/[\.]([^\/\\\.]+)$/);return b&&b[1]&&b[1].length?b[1]:null};a.simpleFilePreview={defaults:{buttonContent:"Add File",removeContent:"X",existingFiles:null,shiftLeft:"<<",shiftRight:">>",iconPath:"",defaultIcon:"preview_file.png",limit:0,icons:{png:"preview_png.png",gif:"preview_png.png",bmp:"preview_png.png",svg:"preview_png.png",jpg:"preview_png.png",jpeg:"preview_png.png",pjpg:"preview_png.png",pjpeg:"preview_png.png",tif:"preview_png.png",tiff:"preview_png.png",mp3:"preview_mp3.png",mp4:"preview_mp3.png",wav:"preview_mp3.png",wma:"preview_mp3.png",pdf:"preview_pdf.png",txt:"preview_txt.png",rtf:"preview_txt.png",text:"preview_txt.png",plain:"preview_txt.png",zip:"preview_zip.png",tgz:"preview_zip.png","x-rar-compressed":"preview_zip.png","octet-stream":"preview_zip.png",odf:"preview_doc.png",odt:"preview_doc.png",doc:"preview_doc.png",msword:"preview_doc.png","vnd.openxmlformats-officedocument.wordprocessingml.document":"preview_doc.png",docx:"preview_doc.png",ods:"preview_xls.png","vnd.ms-excel":"preview_xls.png",xls:"preview_xls.png",xlx:"preview_xls.png",msexcel:"preview_xls.png","x-excel":"preview_xls.png","x-ms-excel":"preview_xls.png","vnd.openxmlformats-officedocument.spreadsheetml.sheet":"preview_xls.png"}},uid:0,init:!1,previewFileTypes:"p?jpe?g|png|gif|bmp|svg"}}(jQuery);
\ No newline at end of file
+!function (i) { "use strict"; i.fn.simpleFilePreview = function (e) { if (!this || !this.length) return this; if (e = e ? e : {}, e = i.extend(!0, {}, i.simpleFilePreview.defaults, e), e.readOnly && (e.radio = null), e.removeDialog && (e.removeDialog.id || (e.removeDialog.id = i(this).id + "_remove_dialog"), e.removeDialog.title || (e.removeDialog.title = "Remove"), e.removeDialog.text || (e.removeDialog.text = "Are you sure?"), e.removeDialog.ok || (e.removeDialog.ok = "Ok"), e.removeDialog.cancel || (e.removeDialog.cancel = "Cancel"), e.removeDialog.options || (e.removeDialog.options = {}), i('