Skip to content

Commit

Permalink
Exif updates and new methods fix #974 fix #978 fix #979 fix #980
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-v committed May 25, 2017
1 parent 9e39f4f commit 19a53a7
Show file tree
Hide file tree
Showing 8 changed files with 2,597 additions and 146 deletions.
5 changes: 4 additions & 1 deletion CHANGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ Change Log: `bootstrap-fileinput`

## version 4.4.1

**Date:** 14-May-2017
**Date:** 25-May-2017

- (enh #980): Add new method `getFrames` to get all thumbnail frames as jQuery objects.
- (enh #979): Add new method `getExif` to retrieve exif data for a selected jpeg image.
- (enh #978, #974): Implement exif restoration for resized images via [`piexif` plugin](https://github.com/hMatoba/piexifjs).
- (enh #968): Update Turkish Translations.
- (enh #967): Correct file caption display for ajax upload mode when `showPreview` is `false`.

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ Step 1: Load the following assets in your header.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="path/to/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- canvas-to-blob.min.js is only needed if you wish to resize images before upload.
<!-- piexif.min.js is only needed if you wish to resize images before upload to restore exif data.
This must be loaded before fileinput.min.js -->
<script src="path/to/js/plugins/canvas-to-blob.min.js" type="text/javascript"></script>
<script src="path/to/js/plugins/piexif.min.js" type="text/javascript"></script>
<!-- sortable.min.js is only needed if you wish to sort / rearrange files in initial preview.
This must be loaded before fileinput.min.js -->
<script src="path/to/js/plugins/sortable.min.js" type="text/javascript"></script>
Expand All @@ -87,7 +87,7 @@ If you noticed, you need to load the `jquery.min.js` and `bootstrap.min.css` in

**Optional Dependent Plugins**

- The `canvas-to-blob.min.js` file is the source for the [JavaScript-Canvas-to-Blob plugin by blueimp](https://github.com/blueimp/JavaScript-Canvas-to-Blob). It is required to be loaded before `fileinput.min.js` if you wish to use the image resize feature of the **bootstrap-fileinput** plugin.
- The `piexif.min.js` file is the source for the [Piexifjs plugin by hMatoba](https://github.com/hMatoba/piexifjs). It is required to be loaded before `fileinput.min.js` if you wish to use the image resize feature of the **bootstrap-fileinput** plugin.
- The `sortable.min.js` file is the source for the [Sortable plugin by rubaxa](https://github.com/rubaxa/Sortable). It is required to be loaded before `fileinput.min.js` if you wish to sort the thumbnails in the initial preview.
- The `purify.min.js` file is the source for the [DomPurify plugin by cure53](https://github.com/cure53/DOMPurify). It is required to be loaded before `fileinput.min.js` if you wish to purify your HTML for HTML content preview.

Expand All @@ -113,4 +113,4 @@ Alternatively, you can directly call the plugin options by setting data attribut

## License

**bootstrap-fileinput** is released under the BSD 3-Clause License. See the bundled `LICENSE.md` for details.
**bootstrap-fileinput** is released under the BSD 3-Clause License. See the bundled `LICENSE.md` for details.
156 changes: 114 additions & 42 deletions js/fileinput.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,46 @@
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);
},
addCss: function ($el, css) {
$el.removeClass(css).addClass(css);
},
Expand Down Expand Up @@ -307,9 +347,10 @@
self.$element.removeClass('file-loading');
}
};
//noinspection JSUnusedGlobalSymbols
FileInput.prototype = {
constructor: FileInput,
_cleanup: function() {
_cleanup: function () {
var self = this;
self.reader = null;
self.formdata = {};
Expand Down Expand Up @@ -1205,13 +1246,13 @@
},
_clearPreview: function () {
var self = this, $p = self.$preview,
$thumbs = self.showUploadedThumbs ? $p.find($h.FRAMES + ':not(.file-preview-success)') : $p.find($h.FRAMES);
$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.$preview.find($h.FRAMES).length || !self.showPreview) {
if (!self.getFrames().length || !self.showPreview) {
self._resetUpload();
}
self._validateDefaultPreview();
Expand Down Expand Up @@ -1339,9 +1380,9 @@
});
},
_initZoomButtons: function () {
var self = this, previewId = self.$modal.data('previewId') || '', $first, $last, $preview = self.$preview,
thumbs = $preview.find($h.FRAMES).toArray(), len = thumbs.length,
$prev = self.$modal.find('.btn-prev'), $next = self.$modal.find('.btn-next');
var self = this, previewId = self.$modal.data('previewId') || '', $first, $last,
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();
Expand Down Expand Up @@ -1515,7 +1556,7 @@
},
_zoomSlideShow: function (dir, previewId) {
var self = this, $btn = self.$modal.find('.kv-zoom-actions .btn-' + dir), $targFrame, i,
thumbs = self.$preview.find($h.FRAMES).toArray(), len = thumbs.length, out;
thumbs = self.getFrames().toArray(), len = thumbs.length, out;
if ($btn.attr('disabled')) {
return;
}
Expand Down Expand Up @@ -1697,9 +1738,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($h.FRAMES + ':not(.file-preview-initial)' + css);
return this.getFrames(':not(.file-preview-initial)' + css);
},
_getExtraData: function (previewId, index) {
var self = this, data = self.uploadExtraData;
Expand Down Expand Up @@ -1825,7 +1874,7 @@
$thumb.fadeOut('slow', function () {
$h.cleanZoomCache($preview.find('#zoom-' + id));
$thumb.remove();
if (!$preview.find($h.FRAMES).length) {
if (!self.getFrames().length) {
self.reset();
}
});
Expand Down Expand Up @@ -1891,6 +1940,7 @@
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];
}
Expand Down Expand Up @@ -2173,7 +2223,7 @@
self.initialPreview = $h.spliceArray(self.initialPreview, ind);
self.initialPreviewConfig = $h.spliceArray(self.initialPreviewConfig, ind);
self.initialPreviewThumbTags = $h.spliceArray(self.initialPreviewThumbTags, ind);
self.$preview.find($h.FRAMES).each(function () {
self.getFrames().each(function () {
var $nFrame = $(this), nInd = $nFrame.attr('data-fileindex');
if (nInd.substring(0, 5) === 'init_') {
nInd = parseInt(nInd.replace('init_', ''));
Expand All @@ -2194,7 +2244,7 @@
return;
}
self._initZoomButton();
$preview.find($h.FRAMES + ' .kv-file-remove').each(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 () {
Expand All @@ -2219,8 +2269,7 @@
}
self._clearFileInput();
var filestack = self.getFileStack(true), chk = self.previewCache.count(),
len = filestack.length,
hasThumb = self.showPreview && $preview.find($h.FRAMES).length;
len = filestack.length, hasThumb = self.showPreview && self.getFrames().length;
if (len === 0 && chk === 0 && !hasThumb) {
self.reset();
} else {
Expand All @@ -2232,7 +2281,7 @@
});
});
});
self.$preview.find($h.FRAMES + ' .kv-file-upload').each(function () {
self.getFrames(' .kv-file-upload').each(function () {
var $el = $(this);
self._handler($el, 'click', function () {
var $frame = $el.closest($h.FRAMES), ind = $frame.attr('data-fileindex');
Expand Down Expand Up @@ -2454,11 +2503,11 @@
$h.addCss($zoomImg, css);
self._raise('fileimageoriented', {'$img': $img, 'file': file});
}
self._validateImage(previewId, caption, ftype, fsize);
self._validateImage(previewId, caption, ftype, fsize, iData);
$h.adjustOrientedImage($img);
});
} else {
self._validateImage(previewId, caption, ftype, fsize);
self._validateImage(previewId, caption, ftype, fsize, iData);
}
} else {
self._previewDefault(file, previewId);
Expand Down Expand Up @@ -2850,14 +2899,11 @@
self._showUploadError(msg, params);
self._setPreviewError($thumb, i, null);
},
_validateImage: function (previewId, fname, ftype, fsize) {
_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');
i = $thumb.attr('data-fileindex'), $img = $thumb.find('img'), exifObject;
fname = fname || 'Untitled';
if (!$img.length) {
return;
}
self._handler($img, 'load', function () {
$img.one('load', function () {
w1 = $thumb.width();
w2 = $preview.width();
if (w1 > w2) {
Expand All @@ -2872,16 +2918,30 @@
self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params);
}
self._raise('fileimageloaded', [previewId]);
exifObject = window.piexif ? window.piexif.load(iData) : null;
self.loadedImages.push({
ind: i,
img: $img,
thumb: $thumb,
pid: previewId,
typ: ftype,
siz: fsize,
validated: false
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).load();
} else {
if (this.error) {
$(this).error();
}
}
});
},
_validateAllImages: function () {
Expand All @@ -2907,11 +2967,11 @@
}
},
_getResizedImage: function (config, counter, numImgs) {
var self = this, img = $(config.img)[0], width = img.naturalWidth, height = img.naturalHeight,
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,
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;
$thumb = config.thumb, throwError, msg, exifObj = config.exifObj, exifStr;
throwError = function (msg, params, ev) {
if (self.isUploadable) {
self._showUploadError(msg, params, ev);
Expand Down Expand Up @@ -2948,17 +3008,21 @@
canvas.height = height;
try {
context.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
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');
}
}, type, self.resizeQuality);
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++;
Expand Down Expand Up @@ -3324,7 +3388,7 @@
self.filenames = newnames;
self.fileids = newids;
},
_isFileSelectionValid: function(cnt) {
_isFileSelectionValid: function (cnt) {
var self = this;
cnt = cnt || 0;
if (self.required && !self.getFilesCount()) {
Expand Down Expand Up @@ -3471,7 +3535,7 @@
self._resetPreview();
self.$container.find('.fileinput-filename').text('');
$h.addCss(self.$container, 'file-input-new');
if (self.$preview.find($h.FRAMES).length || self.isUploadable && self.dropZoneEnabled) {
if (self.getFrames().length || self.isUploadable && self.dropZoneEnabled) {
self.$container.removeClass('file-input-new');
}
self._setFileDropZoneTitle();
Expand Down Expand Up @@ -3580,9 +3644,8 @@
return $el;
},
zoom: function (frameId) {
var self = this, $frame = $('#' + frameId), $modal = self.$modal;
if (!$frame.length) {
self._log('Cannot zoom to detailed preview! Invalid frame with id: "' + frameId + '".');
var self = this, $frame = self._getFrame(frameId), $modal = self.$modal;
if (!$frame) {
return;
}
$h.initModal($modal);
Expand All @@ -3591,6 +3654,15 @@
$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;
cssFilter = cssFilter || '';
return self.$preview.find($h.FRAMES + cssFilter);
},
getPreview: function () {
var self = this;
return {
Expand Down
6 changes: 3 additions & 3 deletions js/fileinput.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit 19a53a7

Please sign in to comment.