added attributions.md
This commit is contained in:
parent
b8dcc30742
commit
cea7821bd6
6 changed files with 43 additions and 1572 deletions
43
attributions.md
Normal file
43
attributions.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
## Frappe framework includes these public works
|
||||||
|
|
||||||
|
### Javascript / CSS
|
||||||
|
|
||||||
|
- Bootstrap: MIT License, (c) Twitter Inc, https://getbootstrap.com
|
||||||
|
- JQuery: MIT License, (c) JQuery Foundation, http://jquery.org/license
|
||||||
|
- JQuery UI: MIT License / GPL 2, (c) JQuery Foundation, https://jqueryui.com/about
|
||||||
|
- JQuery UI Bootstrap Theme: MIT / GPL 2, (c) Addy Osmani, http://addyosmani.github.com/jquery-ui-bootstrap
|
||||||
|
- QUnit: MIT License, (c) JQuery Foundation, http://jquery.org/license
|
||||||
|
- jquery.event.drag, MIT License, (c) 2010 Three Dub Media - http://threedubmedia.com
|
||||||
|
- JQuery Cookie Plugin, MIT / GPL 2, (c) 2011, Klaus Hartl
|
||||||
|
- JQuery Time Picker, MIT License, (c) 2013 Trent Richardson, http://trentrichardson.com/examples/timepicker
|
||||||
|
- JQuery Hotkeys Plugin, MIT License, (c) 2010, John Resig
|
||||||
|
- prettydate.js, MIT License, (c) 2011, John Resig
|
||||||
|
- jquery.flot.downsample, MIT License, (c) 2013, Sveinn Steinarsson
|
||||||
|
- JQuery Resize Event, MIT License, (c) 2010 "Cowboy" Ben Alman
|
||||||
|
- excanvas.js, Apache License Version 2.0, (c) 2006 Google Inc
|
||||||
|
- showdown.js - Javascript Markdown, BSD-style Open Source License, (c) 2007 John Fraser
|
||||||
|
- Beautify HTML - MIT License, (c) 2007-2013 Einar Lielmanis and contributors.
|
||||||
|
- JQuery Gantt - MIT License, http://taitems.github.com/jQuery.Gantt/
|
||||||
|
- SlickGrid - MIT License, https://github.com/mleibman/SlickGrid
|
||||||
|
- MomentJS - MIT License, https://github.com/moment/moment
|
||||||
|
- JSColor - LGPL, (c) Jan Odvarko, http://jscolor.com
|
||||||
|
- FullCalendar - MIT License, (c) 2013 Adam Shaw, http://fullcalendar.io/license/
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
- html2text.py - GNU GPL 3, (c) 2004-2008 Aaron Swartz
|
||||||
|
- minify.js - MIT License, (c) 2002 Douglas Crockford
|
||||||
|
|
||||||
|
### Icon Fonts
|
||||||
|
|
||||||
|
- Font Awesome - http://fontawesome.io/
|
||||||
|
- Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL)
|
||||||
|
- Code License: MIT (http://choosealicense.com/licenses/mit/)
|
||||||
|
- Octicons (c) GitHub Inc, https://octicons.github.com/
|
||||||
|
- Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL)
|
||||||
|
- Code License: MIT (http://choosealicense.com/licenses/mit/)
|
||||||
|
- Ionicons - MIT License, http://ionicons.com/
|
||||||
|
|
||||||
|
### IP Address Database
|
||||||
|
|
||||||
|
- GeoIP: (c) 2014 MaxMind, http://dev.maxmind.com/geoip/geoip2/downloadable/
|
||||||
|
|
@ -16,10 +16,6 @@
|
||||||
"public/js/lib/highlight.pack.js",
|
"public/js/lib/highlight.pack.js",
|
||||||
"website/js/website.js"
|
"website/js/website.js"
|
||||||
],
|
],
|
||||||
"js/canvasResize.min.js": [
|
|
||||||
"website/js/jquery.exif.js",
|
|
||||||
"website/js/jquery.canvasResize.js"
|
|
||||||
],
|
|
||||||
"js/editor.min.js": [
|
"js/editor.min.js": [
|
||||||
"public/js/lib/jquery/jquery.hotkeys.js",
|
"public/js/lib/jquery/jquery.hotkeys.js",
|
||||||
"public/js/frappe/class.js",
|
"public/js/frappe/class.js",
|
||||||
|
|
@ -48,7 +44,6 @@
|
||||||
"public/js/lib/jquery/jquery.ui.min.js",
|
"public/js/lib/jquery/jquery.ui.min.js",
|
||||||
"public/js/lib/tag-it.min.js",
|
"public/js/lib/tag-it.min.js",
|
||||||
"public/js/lib/jquery/jquery.hotkeys.js",
|
"public/js/lib/jquery/jquery.hotkeys.js",
|
||||||
"public/js/lib/center_image.js",
|
|
||||||
"public/js/lib/bootstrap.min.js",
|
"public/js/lib/bootstrap.min.js",
|
||||||
"public/js/lib/nprogress.js",
|
"public/js/lib/nprogress.js",
|
||||||
"public/js/lib/beautify-html.js",
|
"public/js/lib/beautify-html.js",
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
/**
|
|
||||||
* bl-jquery-image-center jQuery Plugin
|
|
||||||
*
|
|
||||||
* @copyright Boxlight Media Ltd. 2012
|
|
||||||
* @license MIT License
|
|
||||||
* @description Centers an image by moving, cropping and filling spaces inside it's parent container. Call
|
|
||||||
* this on a set of images to have them fill their parent whilst maintaining aspect ratio
|
|
||||||
* @author Robert Cambridge
|
|
||||||
*
|
|
||||||
* Usage: See documentation at http://boxlight.github.com/bl-jquery-image-center
|
|
||||||
*/
|
|
||||||
$.fn.centerImage = function(method, callback) {
|
|
||||||
callback = callback || function() {};
|
|
||||||
var els = this;
|
|
||||||
var remaining = $(this).length;
|
|
||||||
method = method == 'inside';
|
|
||||||
|
|
||||||
// execute this on an individual image element once it's loaded
|
|
||||||
var fn = function(img) {
|
|
||||||
var $img = $(img);
|
|
||||||
var $div = $img.parent();
|
|
||||||
// parent CSS should be in stylesheet, but to reinforce:
|
|
||||||
$div.css({
|
|
||||||
overflow: 'hidden',
|
|
||||||
});
|
|
||||||
|
|
||||||
// temporarily set the image size naturally so we can get the aspect ratio
|
|
||||||
$img.css({
|
|
||||||
'position': 'static',
|
|
||||||
'width': 'auto',
|
|
||||||
'height': 'auto',
|
|
||||||
'max-width': '100%',
|
|
||||||
'max-height': '100%'
|
|
||||||
});
|
|
||||||
|
|
||||||
// now resize
|
|
||||||
var div = { w: $div.width(), h: $div.height(), r: $div.width() / $div.height() };
|
|
||||||
var img = { w: $img.width(), h: $img.height(), r: $img.width() / $img.height() };
|
|
||||||
var width = Math.round((div.r > img.r) ^ method ? '100%' : div.h / img.h * img.w);
|
|
||||||
var height = Math.round((div.r < img.r) ^ method ? '100%' : div.w / img.w * img.h);
|
|
||||||
|
|
||||||
if(width) width++;
|
|
||||||
if(height) height++;
|
|
||||||
|
|
||||||
$img.css({
|
|
||||||
'max-width': 'none',
|
|
||||||
'max-height': 'none',
|
|
||||||
'width': width,
|
|
||||||
'height': height,
|
|
||||||
});
|
|
||||||
|
|
||||||
// now center - but portrait images need to be centered slightly above halfway (33%)
|
|
||||||
var div = { w: $div.width(), h: $div.height() };
|
|
||||||
var img = { w: $img.width(), h: $img.height() };
|
|
||||||
|
|
||||||
var left = Math.round((div.w - img.w) / 2);
|
|
||||||
var top = Math.round((div.h - img.h) / 3);
|
|
||||||
|
|
||||||
$img.css({
|
|
||||||
'margin-left': left,
|
|
||||||
'margin-top': top
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackWrapped(img)
|
|
||||||
};
|
|
||||||
|
|
||||||
var callbackWrapped = function(img) {
|
|
||||||
remaining--;
|
|
||||||
callback.apply(els, [ img, remaining ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
// iterate through elements
|
|
||||||
return els.each(function(i) {
|
|
||||||
if (this.complete || this.readyState === 'complete') {
|
|
||||||
// loaded already? run fn
|
|
||||||
// when binding, we can tell whether image loaded or not.
|
|
||||||
// not if it's already failed though :(
|
|
||||||
(function(el) {
|
|
||||||
// use setTimeout to prevent browser locking up
|
|
||||||
setTimeout(function() { fn(el) }, 10);
|
|
||||||
})(this);
|
|
||||||
} else {
|
|
||||||
// not loaded? bind to load
|
|
||||||
(function(el) {
|
|
||||||
$(el)
|
|
||||||
.one('load', function() {
|
|
||||||
// use setTimeout to prevent browser locking up
|
|
||||||
setTimeout(function() {
|
|
||||||
fn(el);
|
|
||||||
}, 10);
|
|
||||||
})
|
|
||||||
.one('error', function() {
|
|
||||||
// the image did not load
|
|
||||||
callbackWrapped(el)
|
|
||||||
})
|
|
||||||
.end();
|
|
||||||
|
|
||||||
// IE9 won't always trigger the load event. fix it.
|
|
||||||
if (navigator.userAgent.indexOf("Trident/5") >= 0) {
|
|
||||||
el.src = el.src;
|
|
||||||
}
|
|
||||||
})(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// Alias functions which often better describe the use case
|
|
||||||
$.fn.imageCenterResize = function(callback) {
|
|
||||||
return $(this).centerImage('inside', callback);
|
|
||||||
};
|
|
||||||
$.fn.imageCropFill = function(callback) {
|
|
||||||
return $(this).centerImage('outside', callback);
|
|
||||||
};
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
/**
|
|
||||||
sprintf() for JavaScript 0.7-beta1
|
|
||||||
http://www.diveintojavascript.com/projects/javascript-sprintf
|
|
||||||
|
|
||||||
Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of sprintf() for JavaScript nor the
|
|
||||||
names of its contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
Changelog:
|
|
||||||
2010.09.06 - 0.7-beta1
|
|
||||||
- features: vsprintf, support for named placeholders
|
|
||||||
- enhancements: format cache, reduced global namespace pollution
|
|
||||||
|
|
||||||
2010.05.22 - 0.6:
|
|
||||||
- reverted to 0.4 and fixed the bug regarding the sign of the number 0
|
|
||||||
Note:
|
|
||||||
Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/)
|
|
||||||
who warned me about a bug in 0.5, I discovered that the last update was
|
|
||||||
a regress. I appologize for that.
|
|
||||||
|
|
||||||
2010.05.09 - 0.5:
|
|
||||||
- bug fix: 0 is now preceeded with a + sign
|
|
||||||
- bug fix: the sign was not at the right position on padded results (Kamal Abdali)
|
|
||||||
- switched from GPL to BSD license
|
|
||||||
|
|
||||||
2007.10.21 - 0.4:
|
|
||||||
- unit test and patch (David Baird)
|
|
||||||
|
|
||||||
2007.09.17 - 0.3:
|
|
||||||
- bug fix: no longer throws exception on empty paramenters (Hans Pufal)
|
|
||||||
|
|
||||||
2007.09.11 - 0.2:
|
|
||||||
- feature: added argument swapping
|
|
||||||
|
|
||||||
2007.04.03 - 0.1:
|
|
||||||
- initial release
|
|
||||||
**/
|
|
||||||
|
|
||||||
var sprintf = (function() {
|
|
||||||
function get_type(variable) {
|
|
||||||
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
|
|
||||||
}
|
|
||||||
function str_repeat(input, multiplier) {
|
|
||||||
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
|
|
||||||
return output.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
var str_format = function() {
|
|
||||||
if (!str_format.cache.hasOwnProperty(arguments[0])) {
|
|
||||||
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
|
|
||||||
}
|
|
||||||
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
str_format.format = function(parse_tree, argv) {
|
|
||||||
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
|
|
||||||
for (i = 0; i < tree_length; i++) {
|
|
||||||
node_type = get_type(parse_tree[i]);
|
|
||||||
if (node_type === 'string') {
|
|
||||||
output.push(parse_tree[i]);
|
|
||||||
}
|
|
||||||
else if (node_type === 'array') {
|
|
||||||
match = parse_tree[i]; // convenience purposes only
|
|
||||||
if (match[2]) { // keyword argument
|
|
||||||
arg = argv[cursor];
|
|
||||||
for (k = 0; k < match[2].length; k++) {
|
|
||||||
if (!arg.hasOwnProperty(match[2][k])) {
|
|
||||||
//throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
|
|
||||||
arg = '';
|
|
||||||
} else {
|
|
||||||
arg = arg[match[2][k]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (match[1]) { // positional argument (explicit)
|
|
||||||
arg = argv[match[1]];
|
|
||||||
}
|
|
||||||
else { // positional argument (implicit)
|
|
||||||
arg = argv[cursor++];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
|
|
||||||
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
|
|
||||||
}
|
|
||||||
switch (match[8]) {
|
|
||||||
case 'b': arg = arg.toString(2); break;
|
|
||||||
case 'c': arg = String.fromCharCode(arg); break;
|
|
||||||
case 'd': arg = parseInt(arg, 10); break;
|
|
||||||
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
|
|
||||||
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
|
|
||||||
case 'o': arg = arg.toString(8); break;
|
|
||||||
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
|
|
||||||
case 'u': arg = Math.abs(arg); break;
|
|
||||||
case 'x': arg = arg.toString(16); break;
|
|
||||||
case 'X': arg = arg.toString(16).toUpperCase(); break;
|
|
||||||
}
|
|
||||||
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
|
|
||||||
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
|
|
||||||
pad_length = match[6] - String(arg).length;
|
|
||||||
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
|
|
||||||
output.push(match[5] ? arg + pad : pad + arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output.join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
str_format.cache = {};
|
|
||||||
|
|
||||||
str_format.parse = function(fmt) {
|
|
||||||
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
|
|
||||||
while (_fmt) {
|
|
||||||
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
|
|
||||||
parse_tree.push(match[0]);
|
|
||||||
}
|
|
||||||
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
|
|
||||||
parse_tree.push('%');
|
|
||||||
}
|
|
||||||
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
|
|
||||||
if (match[2]) {
|
|
||||||
arg_names |= 1;
|
|
||||||
var field_list = [], replacement_field = match[2], field_match = [];
|
|
||||||
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
|
||||||
field_list.push(field_match[1]);
|
|
||||||
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
|
|
||||||
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
|
||||||
field_list.push(field_match[1]);
|
|
||||||
}
|
|
||||||
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
|
|
||||||
field_list.push(field_match[1]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw('[sprintf] huh?');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw('[sprintf] huh?');
|
|
||||||
}
|
|
||||||
match[2] = field_list;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arg_names |= 2;
|
|
||||||
}
|
|
||||||
if (arg_names === 3) {
|
|
||||||
throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
|
|
||||||
}
|
|
||||||
parse_tree.push(match);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw('[sprintf] huh?');
|
|
||||||
}
|
|
||||||
_fmt = _fmt.substring(match[0].length);
|
|
||||||
}
|
|
||||||
return parse_tree;
|
|
||||||
};
|
|
||||||
|
|
||||||
return str_format;
|
|
||||||
})();
|
|
||||||
|
|
||||||
var vsprintf = function(fmt, argv) {
|
|
||||||
argv.unshift(fmt);
|
|
||||||
return sprintf.apply(null, argv);
|
|
||||||
};
|
|
||||||
|
|
@ -1,313 +0,0 @@
|
||||||
/*
|
|
||||||
* jQuery canvasResize plugin
|
|
||||||
*
|
|
||||||
* Version: 1.2.0
|
|
||||||
* Date (d/m/y): 02/10/12
|
|
||||||
* Update (d/m/y): 14/05/13
|
|
||||||
* Original author: @gokercebeci
|
|
||||||
* Licensed under the MIT license
|
|
||||||
* - This plugin working with jquery.exif.js
|
|
||||||
* (It's under the MPL License http://www.nihilogic.dk/licenses/mpl-license.txt)
|
|
||||||
* Demo: http://ios6-image-resize.gokercebeci.com/
|
|
||||||
*
|
|
||||||
* - I fixed iOS6 Safari's image file rendering issue for large size image (over mega-pixel)
|
|
||||||
* using few functions from https://github.com/stomita/ios-imagefile-megapixel
|
|
||||||
* (detectSubsampling, )
|
|
||||||
* And fixed orientation issue by edited http://blog.nihilogic.dk/2008/05/jquery-exif-data-plugin.html
|
|
||||||
* Thanks, Shinichi Tomita and Jacob Seidelin
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
var pluginName = 'canvasResize',
|
|
||||||
methods = {
|
|
||||||
newsize: function(w, h, W, H, C) {
|
|
||||||
var c = C ? 'h' : '';
|
|
||||||
if ((W && w > W) || (H && h > H)) {
|
|
||||||
var r = w / h;
|
|
||||||
if ((r >= 1 || H === 0) && W && !C) {
|
|
||||||
w = W;
|
|
||||||
h = (W / r) >> 0;
|
|
||||||
} else if (C && r <= (W / H)) {
|
|
||||||
w = W;
|
|
||||||
h = (W / r) >> 0;
|
|
||||||
c = 'w';
|
|
||||||
} else {
|
|
||||||
w = (H * r) >> 0;
|
|
||||||
h = H;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
'width': w,
|
|
||||||
'height': h,
|
|
||||||
'cropped': c
|
|
||||||
};
|
|
||||||
},
|
|
||||||
dataURLtoBlob: function(data) {
|
|
||||||
var mimeString = data.split(',')[0].split(':')[1].split(';')[0];
|
|
||||||
var byteString = atob(data.split(',')[1]);
|
|
||||||
var ab = new ArrayBuffer(byteString.length);
|
|
||||||
var ia = new Uint8Array(ab);
|
|
||||||
for (var i = 0; i < byteString.length; i++) {
|
|
||||||
ia[i] = byteString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
var bb = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder);
|
|
||||||
if (bb) {
|
|
||||||
// console.log('BlobBuilder');
|
|
||||||
bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)();
|
|
||||||
bb.append(ab);
|
|
||||||
return bb.getBlob(mimeString);
|
|
||||||
} else {
|
|
||||||
// console.log('Blob');
|
|
||||||
bb = new Blob([ab], {
|
|
||||||
'type': (mimeString)
|
|
||||||
});
|
|
||||||
return bb;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Detect subsampling in loaded image.
|
|
||||||
* In iOS, larger images than 2M pixels may be subsampled in rendering.
|
|
||||||
*/
|
|
||||||
detectSubsampling: function(img) {
|
|
||||||
var iw = img.width, ih = img.height;
|
|
||||||
if (iw * ih > 1048576) { // subsampling may happen over megapixel image
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
canvas.width = canvas.height = 1;
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
ctx.drawImage(img, -iw + 1, 0);
|
|
||||||
// subsampled image becomes half smaller in rendering size.
|
|
||||||
// check alpha channel value to confirm image is covering edge pixel or not.
|
|
||||||
// if alpha value is 0 image is not covering, hence subsampled.
|
|
||||||
return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Update the orientation according to the specified rotation angle
|
|
||||||
*/
|
|
||||||
rotate: function(orientation, angle) {
|
|
||||||
var o = {
|
|
||||||
// nothing
|
|
||||||
1: {90: 6, 180: 3, 270: 8},
|
|
||||||
// horizontal flip
|
|
||||||
2: {90: 7, 180: 4, 270: 5},
|
|
||||||
// 180 rotate left
|
|
||||||
3: {90: 8, 180: 1, 270: 6},
|
|
||||||
// vertical flip
|
|
||||||
4: {90: 5, 180: 2, 270: 7},
|
|
||||||
// vertical flip + 90 rotate right
|
|
||||||
5: {90: 2, 180: 7, 270: 4},
|
|
||||||
// 90 rotate right
|
|
||||||
6: {90: 3, 180: 8, 270: 1},
|
|
||||||
// horizontal flip + 90 rotate right
|
|
||||||
7: {90: 4, 180: 5, 270: 2},
|
|
||||||
// 90 rotate left
|
|
||||||
8: {90: 1, 180: 6, 270: 3}
|
|
||||||
};
|
|
||||||
return o[orientation][angle] ? o[orientation][angle] : orientation;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Transform canvas coordination according to specified frame size and orientation
|
|
||||||
* Orientation value is from EXIF tag
|
|
||||||
*/
|
|
||||||
transformCoordinate: function(canvas, width, height, orientation) {
|
|
||||||
//console.log(width, height);
|
|
||||||
switch (orientation) {
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
case 7:
|
|
||||||
case 8:
|
|
||||||
canvas.width = height;
|
|
||||||
canvas.height = width;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
}
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
switch (orientation) {
|
|
||||||
case 1:
|
|
||||||
// nothing
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
// horizontal flip
|
|
||||||
ctx.translate(width, 0);
|
|
||||||
ctx.scale(-1, 1);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// 180 rotate left
|
|
||||||
ctx.translate(width, height);
|
|
||||||
ctx.rotate(Math.PI);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
// vertical flip
|
|
||||||
ctx.translate(0, height);
|
|
||||||
ctx.scale(1, -1);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
// vertical flip + 90 rotate right
|
|
||||||
ctx.rotate(0.5 * Math.PI);
|
|
||||||
ctx.scale(1, -1);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
// 90 rotate right
|
|
||||||
ctx.rotate(0.5 * Math.PI);
|
|
||||||
ctx.translate(0, -height);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
// horizontal flip + 90 rotate right
|
|
||||||
ctx.rotate(0.5 * Math.PI);
|
|
||||||
ctx.translate(width, -height);
|
|
||||||
ctx.scale(-1, 1);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
// 90 rotate left
|
|
||||||
ctx.rotate(-0.5 * Math.PI);
|
|
||||||
ctx.translate(-width, 0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Detecting vertical squash in loaded image.
|
|
||||||
* Fixes a bug which squash image vertically while drawing into canvas for some images.
|
|
||||||
*/
|
|
||||||
detectVerticalSquash: function(img, iw, ih) {
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
canvas.width = 1;
|
|
||||||
canvas.height = ih;
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
ctx.drawImage(img, 0, 0);
|
|
||||||
var data = ctx.getImageData(0, 0, 1, ih).data;
|
|
||||||
// search image edge pixel position in case it is squashed vertically.
|
|
||||||
var sy = 0;
|
|
||||||
var ey = ih;
|
|
||||||
var py = ih;
|
|
||||||
while (py > sy) {
|
|
||||||
var alpha = data[(py - 1) * 4 + 3];
|
|
||||||
if (alpha === 0) {
|
|
||||||
ey = py;
|
|
||||||
} else {
|
|
||||||
sy = py;
|
|
||||||
}
|
|
||||||
py = (ey + sy) >> 1;
|
|
||||||
}
|
|
||||||
var ratio = py / ih;
|
|
||||||
return ratio === 0 ? 1 : ratio;
|
|
||||||
},
|
|
||||||
callback: function(d) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
defaults = {
|
|
||||||
width: 300,
|
|
||||||
height: 0,
|
|
||||||
crop: false,
|
|
||||||
quality: 80,
|
|
||||||
'callback': methods.callback
|
|
||||||
};
|
|
||||||
function Plugin(file, options) {
|
|
||||||
this.file = file;
|
|
||||||
this.options = $.extend({}, defaults, options);
|
|
||||||
this._defaults = defaults;
|
|
||||||
this._name = pluginName;
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
Plugin.prototype = {
|
|
||||||
init: function() {
|
|
||||||
//this.options.init(this);
|
|
||||||
var $this = this;
|
|
||||||
var file = this.file;
|
|
||||||
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onloadend = function(e) {
|
|
||||||
var dataURL = e.target.result;
|
|
||||||
var img = new Image();
|
|
||||||
img.onload = function(e) {
|
|
||||||
// Read Orientation Data in EXIF
|
|
||||||
$(img).exifLoadFromDataURL(function() {
|
|
||||||
var orientation = $(img).exif('Orientation')[0] || 1;
|
|
||||||
orientation = methods.rotate(orientation, $this.options.rotate);
|
|
||||||
|
|
||||||
// CW or CCW ? replace width and height
|
|
||||||
var size = (orientation >= 5 && orientation <= 8)
|
|
||||||
? methods.newsize(img.height, img.width, $this.options.width, $this.options.height, $this.options.crop)
|
|
||||||
: methods.newsize(img.width, img.height, $this.options.width, $this.options.height, $this.options.crop);
|
|
||||||
|
|
||||||
var iw = img.width, ih = img.height;
|
|
||||||
var width = size.width, height = size.height;
|
|
||||||
|
|
||||||
//console.log(iw, ih, size.width, size.height, orientation);
|
|
||||||
|
|
||||||
var canvas = document.createElement("canvas");
|
|
||||||
var ctx = canvas.getContext("2d");
|
|
||||||
ctx.save();
|
|
||||||
methods.transformCoordinate(canvas, width, height, orientation);
|
|
||||||
|
|
||||||
// over image size
|
|
||||||
if (methods.detectSubsampling(img)) {
|
|
||||||
iw /= 2;
|
|
||||||
ih /= 2;
|
|
||||||
}
|
|
||||||
var d = 1024; // size of tiling canvas
|
|
||||||
var tmpCanvas = document.createElement('canvas');
|
|
||||||
tmpCanvas.width = tmpCanvas.height = d;
|
|
||||||
var tmpCtx = tmpCanvas.getContext('2d');
|
|
||||||
var vertSquashRatio = methods.detectVerticalSquash(img, iw, ih);
|
|
||||||
var sy = 0;
|
|
||||||
while (sy < ih) {
|
|
||||||
var sh = sy + d > ih ? ih - sy : d;
|
|
||||||
var sx = 0;
|
|
||||||
while (sx < iw) {
|
|
||||||
var sw = sx + d > iw ? iw - sx : d;
|
|
||||||
tmpCtx.clearRect(0, 0, d, d);
|
|
||||||
tmpCtx.drawImage(img, -sx, -sy);
|
|
||||||
var dx = Math.floor(sx * width / iw);
|
|
||||||
var dw = Math.ceil(sw * width / iw);
|
|
||||||
var dy = Math.floor(sy * height / ih / vertSquashRatio);
|
|
||||||
var dh = Math.ceil(sh * height / ih / vertSquashRatio);
|
|
||||||
ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
|
|
||||||
sx += d;
|
|
||||||
}
|
|
||||||
sy += d;
|
|
||||||
}
|
|
||||||
ctx.restore();
|
|
||||||
tmpCanvas = tmpCtx = null;
|
|
||||||
|
|
||||||
// if cropped or rotated width and height data replacing issue
|
|
||||||
var newcanvas = document.createElement('canvas');
|
|
||||||
newcanvas.width = size.cropped === 'h' ? height : width;
|
|
||||||
newcanvas.height = size.cropped === 'w' ? width : height;
|
|
||||||
var x = size.cropped === 'h' ? (height - width) * .5 : 0;
|
|
||||||
var y = size.cropped === 'w' ? (width - height) * .5 : 0;
|
|
||||||
newctx = newcanvas.getContext('2d');
|
|
||||||
newctx.drawImage(canvas, x, y, width, height);
|
|
||||||
|
|
||||||
if (file.type === "image/png") {
|
|
||||||
var data = newcanvas.toDataURL(file.type);
|
|
||||||
} else {
|
|
||||||
var data = newcanvas.toDataURL("image/jpeg", ($this.options.quality * .01));
|
|
||||||
}
|
|
||||||
|
|
||||||
// CALLBACK
|
|
||||||
$this.options.callback(data, width, height);
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
img.src = dataURL;
|
|
||||||
// =====================================================
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$[pluginName] = function(file, options) {
|
|
||||||
if (typeof file === 'string')
|
|
||||||
return methods[file](options);
|
|
||||||
else
|
|
||||||
new Plugin(file, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery);
|
|
||||||
|
|
@ -1,957 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* Javascript EXIF Reader - jQuery plugin 0.1.3
|
|
||||||
* Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
|
|
||||||
* Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* I added three functions for read EXIF from dataURL
|
|
||||||
* - getImageDataFromDataURL
|
|
||||||
* - getDataFromDataURL
|
|
||||||
* - jQuery.fn.exifLoadFromDataURL
|
|
||||||
*
|
|
||||||
* http://orientation.gokercebeci.com
|
|
||||||
* @gokercebeci
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
|
|
||||||
var BinaryFile = function(strData, iDataOffset, iDataLength) {
|
|
||||||
var data = strData;
|
|
||||||
var dataOffset = iDataOffset || 0;
|
|
||||||
var dataLength = 0;
|
|
||||||
|
|
||||||
this.getRawData = function() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof strData == "string") {
|
|
||||||
dataLength = iDataLength || data.length;
|
|
||||||
|
|
||||||
this.getByteAt = function(iOffset) {
|
|
||||||
return data.charCodeAt(iOffset + dataOffset) & 0xFF;
|
|
||||||
}
|
|
||||||
} else if (typeof strData == "unknown") {
|
|
||||||
dataLength = iDataLength || IEBinary_getLength(data);
|
|
||||||
|
|
||||||
this.getByteAt = function(iOffset) {
|
|
||||||
return IEBinary_getByteAt(data, iOffset + dataOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getLength = function() {
|
|
||||||
return dataLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getSByteAt = function(iOffset) {
|
|
||||||
var iByte = this.getByteAt(iOffset);
|
|
||||||
if (iByte > 127)
|
|
||||||
return iByte - 256;
|
|
||||||
else
|
|
||||||
return iByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getShortAt = function(iOffset, bBigEndian) {
|
|
||||||
var iShort = bBigEndian ?
|
|
||||||
(this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1)
|
|
||||||
: (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset)
|
|
||||||
if (iShort < 0)
|
|
||||||
iShort += 65536;
|
|
||||||
return iShort;
|
|
||||||
}
|
|
||||||
this.getSShortAt = function(iOffset, bBigEndian) {
|
|
||||||
var iUShort = this.getShortAt(iOffset, bBigEndian);
|
|
||||||
if (iUShort > 32767)
|
|
||||||
return iUShort - 65536;
|
|
||||||
else
|
|
||||||
return iUShort;
|
|
||||||
}
|
|
||||||
this.getLongAt = function(iOffset, bBigEndian) {
|
|
||||||
var iByte1 = this.getByteAt(iOffset),
|
|
||||||
iByte2 = this.getByteAt(iOffset + 1),
|
|
||||||
iByte3 = this.getByteAt(iOffset + 2),
|
|
||||||
iByte4 = this.getByteAt(iOffset + 3);
|
|
||||||
|
|
||||||
var iLong = bBigEndian ?
|
|
||||||
(((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4
|
|
||||||
: (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
|
|
||||||
if (iLong < 0)
|
|
||||||
iLong += 4294967296;
|
|
||||||
return iLong;
|
|
||||||
}
|
|
||||||
this.getSLongAt = function(iOffset, bBigEndian) {
|
|
||||||
var iULong = this.getLongAt(iOffset, bBigEndian);
|
|
||||||
if (iULong > 2147483647)
|
|
||||||
return iULong - 4294967296;
|
|
||||||
else
|
|
||||||
return iULong;
|
|
||||||
}
|
|
||||||
this.getStringAt = function(iOffset, iLength) {
|
|
||||||
var aStr = [];
|
|
||||||
for (var i = iOffset, j = 0; i < iOffset + iLength; i++, j++) {
|
|
||||||
aStr[j] = String.fromCharCode(this.getByteAt(i));
|
|
||||||
}
|
|
||||||
return aStr.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getCharAt = function(iOffset) {
|
|
||||||
return String.fromCharCode(this.getByteAt(iOffset));
|
|
||||||
}
|
|
||||||
this.toBase64 = function() {
|
|
||||||
return window.btoa(data);
|
|
||||||
}
|
|
||||||
this.fromBase64 = function(strBase64) {
|
|
||||||
data = window.atob(strBase64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var BinaryAjax = (function() {
|
|
||||||
|
|
||||||
function createRequest() {
|
|
||||||
var oHTTP = null;
|
|
||||||
if (window.XMLHttpRequest) {
|
|
||||||
oHTTP = new XMLHttpRequest();
|
|
||||||
} else if (window.ActiveXObject) {
|
|
||||||
oHTTP = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
}
|
|
||||||
return oHTTP;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHead(strURL, fncCallback, fncError) {
|
|
||||||
var oHTTP = createRequest();
|
|
||||||
if (oHTTP) {
|
|
||||||
if (fncCallback) {
|
|
||||||
if (typeof(oHTTP.onload) != "undefined") {
|
|
||||||
oHTTP.onload = function() {
|
|
||||||
if (oHTTP.status == "200") {
|
|
||||||
fncCallback(this);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
oHTTP = null;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
oHTTP.onreadystatechange = function() {
|
|
||||||
if (oHTTP.readyState == 4) {
|
|
||||||
if (oHTTP.status == "200") {
|
|
||||||
fncCallback(this);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
oHTTP = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oHTTP.open("HEAD", strURL, true);
|
|
||||||
oHTTP.send(null);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendRequest(strURL, fncCallback, fncError, aRange, bAcceptRanges, iFileSize) {
|
|
||||||
var oHTTP = createRequest();
|
|
||||||
if (oHTTP) {
|
|
||||||
|
|
||||||
var iDataOffset = 0;
|
|
||||||
if (aRange && !bAcceptRanges) {
|
|
||||||
iDataOffset = aRange[0];
|
|
||||||
}
|
|
||||||
var iDataLen = 0;
|
|
||||||
if (aRange) {
|
|
||||||
iDataLen = aRange[1] - aRange[0] + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fncCallback) {
|
|
||||||
if (typeof(oHTTP.onload) != "undefined") {
|
|
||||||
oHTTP.onload = function() {
|
|
||||||
|
|
||||||
if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") {
|
|
||||||
this.binaryResponse = new BinaryFile(this.responseText, iDataOffset, iDataLen);
|
|
||||||
this.fileSize = iFileSize || this.getResponseHeader("Content-Length");
|
|
||||||
fncCallback(this);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
oHTTP = null;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
oHTTP.onreadystatechange = function() {
|
|
||||||
if (oHTTP.readyState == 4) {
|
|
||||||
if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") {
|
|
||||||
this.binaryResponse = new BinaryFile(oHTTP.responseBody, iDataOffset, iDataLen);
|
|
||||||
this.fileSize = iFileSize || this.getResponseHeader("Content-Length");
|
|
||||||
fncCallback(this);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
oHTTP = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oHTTP.open("GET", strURL, true);
|
|
||||||
|
|
||||||
if (oHTTP.overrideMimeType)
|
|
||||||
oHTTP.overrideMimeType('text/plain; charset=x-user-defined');
|
|
||||||
|
|
||||||
if (aRange && bAcceptRanges) {
|
|
||||||
oHTTP.setRequestHeader("Range", "bytes=" + aRange[0] + "-" + aRange[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
oHTTP.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 1970 00:00:00 GMT");
|
|
||||||
|
|
||||||
oHTTP.send(null);
|
|
||||||
} else {
|
|
||||||
if (fncError)
|
|
||||||
fncError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function(strURL, fncCallback, fncError, aRange) {
|
|
||||||
|
|
||||||
if (aRange) {
|
|
||||||
getHead(
|
|
||||||
strURL,
|
|
||||||
function(oHTTP) {
|
|
||||||
var iLength = parseInt(oHTTP.getResponseHeader("Content-Length"), 10);
|
|
||||||
var strAcceptRanges = oHTTP.getResponseHeader("Accept-Ranges");
|
|
||||||
|
|
||||||
var iStart, iEnd;
|
|
||||||
iStart = aRange[0];
|
|
||||||
if (aRange[0] < 0)
|
|
||||||
iStart += iLength;
|
|
||||||
iEnd = iStart + aRange[1] - 1;
|
|
||||||
|
|
||||||
sendRequest(strURL, fncCallback, fncError, [iStart, iEnd], (strAcceptRanges == "bytes"), iLength);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
sendRequest(strURL, fncCallback, fncError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}());
|
|
||||||
|
|
||||||
var script = document.createElement("script");
|
|
||||||
script.type = 'text/vbscript';
|
|
||||||
script.innerHTML =
|
|
||||||
"Function IEBinary_getByteAt(strBinary, iOffset)\r\n"
|
|
||||||
+ " IEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\n"
|
|
||||||
+ "End Function\r\n"
|
|
||||||
+ "Function IEBinary_getLength(strBinary)\r\n"
|
|
||||||
+ " IEBinary_getLength = LenB(strBinary)\r\n"
|
|
||||||
+ "End Function";
|
|
||||||
document.head.appendChild(script);
|
|
||||||
|
|
||||||
var EXIF = {};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var bDebug = false;
|
|
||||||
|
|
||||||
EXIF.Tags = {
|
|
||||||
|
|
||||||
// version tags
|
|
||||||
0x9000: "ExifVersion", // EXIF version
|
|
||||||
0xA000: "FlashpixVersion", // Flashpix format version
|
|
||||||
|
|
||||||
// colorspace tags
|
|
||||||
0xA001: "ColorSpace", // Color space information tag
|
|
||||||
|
|
||||||
// image configuration
|
|
||||||
0xA002: "PixelXDimension", // Valid width of meaningful image
|
|
||||||
0xA003: "PixelYDimension", // Valid height of meaningful image
|
|
||||||
0x9101: "ComponentsConfiguration", // Information about channels
|
|
||||||
0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel
|
|
||||||
|
|
||||||
// user information
|
|
||||||
0x927C: "MakerNote", // Any desired information written by the manufacturer
|
|
||||||
0x9286: "UserComment", // Comments by user
|
|
||||||
|
|
||||||
// related file
|
|
||||||
0xA004: "RelatedSoundFile", // Name of related sound file
|
|
||||||
|
|
||||||
// date and time
|
|
||||||
0x9003: "DateTimeOriginal", // Date and time when the original image was generated
|
|
||||||
0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally
|
|
||||||
0x9290: "SubsecTime", // Fractions of seconds for DateTime
|
|
||||||
0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
|
|
||||||
0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
|
|
||||||
|
|
||||||
// picture-taking conditions
|
|
||||||
0x829A: "ExposureTime", // Exposure time (in seconds)
|
|
||||||
0x829D: "FNumber", // F number
|
|
||||||
0x8822: "ExposureProgram", // Exposure program
|
|
||||||
0x8824: "SpectralSensitivity", // Spectral sensitivity
|
|
||||||
0x8827: "ISOSpeedRatings", // ISO speed rating
|
|
||||||
0x8828: "OECF", // Optoelectric conversion factor
|
|
||||||
0x9201: "ShutterSpeedValue", // Shutter speed
|
|
||||||
0x9202: "ApertureValue", // Lens aperture
|
|
||||||
0x9203: "BrightnessValue", // Value of brightness
|
|
||||||
0x9204: "ExposureBias", // Exposure bias
|
|
||||||
0x9205: "MaxApertureValue", // Smallest F number of lens
|
|
||||||
0x9206: "SubjectDistance", // Distance to subject in meters
|
|
||||||
0x9207: "MeteringMode", // Metering mode
|
|
||||||
0x9208: "LightSource", // Kind of light source
|
|
||||||
0x9209: "Flash", // Flash status
|
|
||||||
0x9214: "SubjectArea", // Location and area of main subject
|
|
||||||
0x920A: "FocalLength", // Focal length of the lens in mm
|
|
||||||
0xA20B: "FlashEnergy", // Strobe energy in BCPS
|
|
||||||
0xA20C: "SpatialFrequencyResponse", //
|
|
||||||
0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
|
|
||||||
0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
|
|
||||||
0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
|
|
||||||
0xA214: "SubjectLocation", // Location of subject in image
|
|
||||||
0xA215: "ExposureIndex", // Exposure index selected on camera
|
|
||||||
0xA217: "SensingMethod", // Image sensor type
|
|
||||||
0xA300: "FileSource", // Image source (3 == DSC)
|
|
||||||
0xA301: "SceneType", // Scene type (1 == directly photographed)
|
|
||||||
0xA302: "CFAPattern", // Color filter array geometric pattern
|
|
||||||
0xA401: "CustomRendered", // Special processing
|
|
||||||
0xA402: "ExposureMode", // Exposure mode
|
|
||||||
0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual
|
|
||||||
0xA404: "DigitalZoomRation", // Digital zoom ratio
|
|
||||||
0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
|
|
||||||
0xA406: "SceneCaptureType", // Type of scene
|
|
||||||
0xA407: "GainControl", // Degree of overall image gain adjustment
|
|
||||||
0xA408: "Contrast", // Direction of contrast processing applied by camera
|
|
||||||
0xA409: "Saturation", // Direction of saturation processing applied by camera
|
|
||||||
0xA40A: "Sharpness", // Direction of sharpness processing applied by camera
|
|
||||||
0xA40B: "DeviceSettingDescription", //
|
|
||||||
0xA40C: "SubjectDistanceRange", // Distance to subject
|
|
||||||
|
|
||||||
// other tags
|
|
||||||
0xA005: "InteroperabilityIFDPointer",
|
|
||||||
0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image
|
|
||||||
};
|
|
||||||
|
|
||||||
EXIF.TiffTags = {
|
|
||||||
0x0100: "ImageWidth",
|
|
||||||
0x0101: "ImageHeight",
|
|
||||||
0x8769: "ExifIFDPointer",
|
|
||||||
0x8825: "GPSInfoIFDPointer",
|
|
||||||
0xA005: "InteroperabilityIFDPointer",
|
|
||||||
0x0102: "BitsPerSample",
|
|
||||||
0x0103: "Compression",
|
|
||||||
0x0106: "PhotometricInterpretation",
|
|
||||||
0x0112: "Orientation",
|
|
||||||
0x0115: "SamplesPerPixel",
|
|
||||||
0x011C: "PlanarConfiguration",
|
|
||||||
0x0212: "YCbCrSubSampling",
|
|
||||||
0x0213: "YCbCrPositioning",
|
|
||||||
0x011A: "XResolution",
|
|
||||||
0x011B: "YResolution",
|
|
||||||
0x0128: "ResolutionUnit",
|
|
||||||
0x0111: "StripOffsets",
|
|
||||||
0x0116: "RowsPerStrip",
|
|
||||||
0x0117: "StripByteCounts",
|
|
||||||
0x0201: "JPEGInterchangeFormat",
|
|
||||||
0x0202: "JPEGInterchangeFormatLength",
|
|
||||||
0x012D: "TransferFunction",
|
|
||||||
0x013E: "WhitePoint",
|
|
||||||
0x013F: "PrimaryChromaticities",
|
|
||||||
0x0211: "YCbCrCoefficients",
|
|
||||||
0x0214: "ReferenceBlackWhite",
|
|
||||||
0x0132: "DateTime",
|
|
||||||
0x010E: "ImageDescription",
|
|
||||||
0x010F: "Make",
|
|
||||||
0x0110: "Model",
|
|
||||||
0x0131: "Software",
|
|
||||||
0x013B: "Artist",
|
|
||||||
0x8298: "Copyright"
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.GPSTags = {
|
|
||||||
0x0000: "GPSVersionID",
|
|
||||||
0x0001: "GPSLatitudeRef",
|
|
||||||
0x0002: "GPSLatitude",
|
|
||||||
0x0003: "GPSLongitudeRef",
|
|
||||||
0x0004: "GPSLongitude",
|
|
||||||
0x0005: "GPSAltitudeRef",
|
|
||||||
0x0006: "GPSAltitude",
|
|
||||||
0x0007: "GPSTimeStamp",
|
|
||||||
0x0008: "GPSSatellites",
|
|
||||||
0x0009: "GPSStatus",
|
|
||||||
0x000A: "GPSMeasureMode",
|
|
||||||
0x000B: "GPSDOP",
|
|
||||||
0x000C: "GPSSpeedRef",
|
|
||||||
0x000D: "GPSSpeed",
|
|
||||||
0x000E: "GPSTrackRef",
|
|
||||||
0x000F: "GPSTrack",
|
|
||||||
0x0010: "GPSImgDirectionRef",
|
|
||||||
0x0011: "GPSImgDirection",
|
|
||||||
0x0012: "GPSMapDatum",
|
|
||||||
0x0013: "GPSDestLatitudeRef",
|
|
||||||
0x0014: "GPSDestLatitude",
|
|
||||||
0x0015: "GPSDestLongitudeRef",
|
|
||||||
0x0016: "GPSDestLongitude",
|
|
||||||
0x0017: "GPSDestBearingRef",
|
|
||||||
0x0018: "GPSDestBearing",
|
|
||||||
0x0019: "GPSDestDistanceRef",
|
|
||||||
0x001A: "GPSDestDistance",
|
|
||||||
0x001B: "GPSProcessingMethod",
|
|
||||||
0x001C: "GPSAreaInformation",
|
|
||||||
0x001D: "GPSDateStamp",
|
|
||||||
0x001E: "GPSDifferential"
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.StringValues = {
|
|
||||||
ExposureProgram: {
|
|
||||||
0: "Not defined",
|
|
||||||
1: "Manual",
|
|
||||||
2: "Normal program",
|
|
||||||
3: "Aperture priority",
|
|
||||||
4: "Shutter priority",
|
|
||||||
5: "Creative program",
|
|
||||||
6: "Action program",
|
|
||||||
7: "Portrait mode",
|
|
||||||
8: "Landscape mode"
|
|
||||||
},
|
|
||||||
MeteringMode: {
|
|
||||||
0: "Unknown",
|
|
||||||
1: "Average",
|
|
||||||
2: "CenterWeightedAverage",
|
|
||||||
3: "Spot",
|
|
||||||
4: "MultiSpot",
|
|
||||||
5: "Pattern",
|
|
||||||
6: "Partial",
|
|
||||||
255: "Other"
|
|
||||||
},
|
|
||||||
LightSource: {
|
|
||||||
0: "Unknown",
|
|
||||||
1: "Daylight",
|
|
||||||
2: "Fluorescent",
|
|
||||||
3: "Tungsten (incandescent light)",
|
|
||||||
4: "Flash",
|
|
||||||
9: "Fine weather",
|
|
||||||
10: "Cloudy weather",
|
|
||||||
11: "Shade",
|
|
||||||
12: "Daylight fluorescent (D 5700 - 7100K)",
|
|
||||||
13: "Day white fluorescent (N 4600 - 5400K)",
|
|
||||||
14: "Cool white fluorescent (W 3900 - 4500K)",
|
|
||||||
15: "White fluorescent (WW 3200 - 3700K)",
|
|
||||||
17: "Standard light A",
|
|
||||||
18: "Standard light B",
|
|
||||||
19: "Standard light C",
|
|
||||||
20: "D55",
|
|
||||||
21: "D65",
|
|
||||||
22: "D75",
|
|
||||||
23: "D50",
|
|
||||||
24: "ISO studio tungsten",
|
|
||||||
255: "Other"
|
|
||||||
},
|
|
||||||
Flash: {
|
|
||||||
0x0000: "Flash did not fire",
|
|
||||||
0x0001: "Flash fired",
|
|
||||||
0x0005: "Strobe return light not detected",
|
|
||||||
0x0007: "Strobe return light detected",
|
|
||||||
0x0009: "Flash fired, compulsory flash mode",
|
|
||||||
0x000D: "Flash fired, compulsory flash mode, return light not detected",
|
|
||||||
0x000F: "Flash fired, compulsory flash mode, return light detected",
|
|
||||||
0x0010: "Flash did not fire, compulsory flash mode",
|
|
||||||
0x0018: "Flash did not fire, auto mode",
|
|
||||||
0x0019: "Flash fired, auto mode",
|
|
||||||
0x001D: "Flash fired, auto mode, return light not detected",
|
|
||||||
0x001F: "Flash fired, auto mode, return light detected",
|
|
||||||
0x0020: "No flash function",
|
|
||||||
0x0041: "Flash fired, red-eye reduction mode",
|
|
||||||
0x0045: "Flash fired, red-eye reduction mode, return light not detected",
|
|
||||||
0x0047: "Flash fired, red-eye reduction mode, return light detected",
|
|
||||||
0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
|
|
||||||
0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
|
|
||||||
0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
|
|
||||||
0x0059: "Flash fired, auto mode, red-eye reduction mode",
|
|
||||||
0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
|
|
||||||
0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
|
|
||||||
},
|
|
||||||
SensingMethod: {
|
|
||||||
1: "Not defined",
|
|
||||||
2: "One-chip color area sensor",
|
|
||||||
3: "Two-chip color area sensor",
|
|
||||||
4: "Three-chip color area sensor",
|
|
||||||
5: "Color sequential area sensor",
|
|
||||||
7: "Trilinear sensor",
|
|
||||||
8: "Color sequential linear sensor"
|
|
||||||
},
|
|
||||||
SceneCaptureType: {
|
|
||||||
0: "Standard",
|
|
||||||
1: "Landscape",
|
|
||||||
2: "Portrait",
|
|
||||||
3: "Night scene"
|
|
||||||
},
|
|
||||||
SceneType: {
|
|
||||||
1: "Directly photographed"
|
|
||||||
},
|
|
||||||
CustomRendered: {
|
|
||||||
0: "Normal process",
|
|
||||||
1: "Custom process"
|
|
||||||
},
|
|
||||||
WhiteBalance: {
|
|
||||||
0: "Auto white balance",
|
|
||||||
1: "Manual white balance"
|
|
||||||
},
|
|
||||||
GainControl: {
|
|
||||||
0: "None",
|
|
||||||
1: "Low gain up",
|
|
||||||
2: "High gain up",
|
|
||||||
3: "Low gain down",
|
|
||||||
4: "High gain down"
|
|
||||||
},
|
|
||||||
Contrast: {
|
|
||||||
0: "Normal",
|
|
||||||
1: "Soft",
|
|
||||||
2: "Hard"
|
|
||||||
},
|
|
||||||
Saturation: {
|
|
||||||
0: "Normal",
|
|
||||||
1: "Low saturation",
|
|
||||||
2: "High saturation"
|
|
||||||
},
|
|
||||||
Sharpness: {
|
|
||||||
0: "Normal",
|
|
||||||
1: "Soft",
|
|
||||||
2: "Hard"
|
|
||||||
},
|
|
||||||
SubjectDistanceRange: {
|
|
||||||
0: "Unknown",
|
|
||||||
1: "Macro",
|
|
||||||
2: "Close view",
|
|
||||||
3: "Distant view"
|
|
||||||
},
|
|
||||||
FileSource: {
|
|
||||||
3: "DSC"
|
|
||||||
},
|
|
||||||
Components: {
|
|
||||||
0: "",
|
|
||||||
1: "Y",
|
|
||||||
2: "Cb",
|
|
||||||
3: "Cr",
|
|
||||||
4: "R",
|
|
||||||
5: "G",
|
|
||||||
6: "B"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEvent(oElement, strEvent, fncHandler)
|
|
||||||
{
|
|
||||||
if (oElement.addEventListener) {
|
|
||||||
oElement.addEventListener(strEvent, fncHandler, false);
|
|
||||||
} else if (oElement.attachEvent) {
|
|
||||||
oElement.attachEvent("on" + strEvent, fncHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function imageHasData(oImg)
|
|
||||||
{
|
|
||||||
return !!(oImg.exifdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getImageData(oImg, fncCallback)
|
|
||||||
{
|
|
||||||
BinaryAjax(
|
|
||||||
oImg.src,
|
|
||||||
function(oHTTP) {
|
|
||||||
console.log('BINARY', oHTTP.binaryResponse);
|
|
||||||
var oEXIF = findEXIFinJPEG(oHTTP.binaryResponse);
|
|
||||||
oImg.exifdata = oEXIF || {};
|
|
||||||
if (fncCallback)
|
|
||||||
fncCallback();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getImageDataFromDataURL(oImg, fncCallback)
|
|
||||||
{
|
|
||||||
var byteString = atob(oImg.src.split(',')[1]);
|
|
||||||
var f = new BinaryFile(byteString, 0, byteString.length)
|
|
||||||
var oEXIF = findEXIFinJPEG(f);
|
|
||||||
oImg.exifdata = oEXIF || {};
|
|
||||||
if (fncCallback)
|
|
||||||
fncCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
function findEXIFinJPEG(oFile) {
|
|
||||||
var aMarkers = [];
|
|
||||||
|
|
||||||
if (oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) {
|
|
||||||
return false; // not a valid jpeg
|
|
||||||
}
|
|
||||||
|
|
||||||
var iOffset = 2;
|
|
||||||
var iLength = oFile.getLength();
|
|
||||||
while (iOffset < iLength) {
|
|
||||||
if (oFile.getByteAt(iOffset) != 0xFF) {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Not a valid marker at offset " + iOffset + ", found: " + oFile.getByteAt(iOffset));
|
|
||||||
return false; // not a valid marker, something is wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
var iMarker = oFile.getByteAt(iOffset + 1);
|
|
||||||
|
|
||||||
// we could implement handling for other markers here,
|
|
||||||
// but we're only looking for 0xFFE1 for EXIF data
|
|
||||||
|
|
||||||
if (iMarker == 22400) {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Found 0xFFE1 marker");
|
|
||||||
return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset + 2, true) - 2);
|
|
||||||
iOffset += 2 + oFile.getShortAt(iOffset + 2, true);
|
|
||||||
|
|
||||||
} else if (iMarker == 225) {
|
|
||||||
// 0xE1 = Application-specific 1 (for EXIF)
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Found 0xFFE1 marker");
|
|
||||||
return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset + 2, true) - 2);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
iOffset += 2 + oFile.getShortAt(iOffset + 2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function readTags(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd)
|
|
||||||
{
|
|
||||||
var iEntries = oFile.getShortAt(iDirStart, bBigEnd);
|
|
||||||
var oTags = {};
|
|
||||||
for (var i = 0; i < iEntries; i++) {
|
|
||||||
var iEntryOffset = iDirStart + i * 12 + 2;
|
|
||||||
var strTag = oStrings[oFile.getShortAt(iEntryOffset, bBigEnd)];
|
|
||||||
if (!strTag && bDebug)
|
|
||||||
console.log("Unknown tag: " + oFile.getShortAt(iEntryOffset, bBigEnd));
|
|
||||||
oTags[strTag] = readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd);
|
|
||||||
}
|
|
||||||
return oTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd)
|
|
||||||
{
|
|
||||||
var iType = oFile.getShortAt(iEntryOffset + 2, bBigEnd);
|
|
||||||
var iNumValues = oFile.getLongAt(iEntryOffset + 4, bBigEnd);
|
|
||||||
var iValueOffset = oFile.getLongAt(iEntryOffset + 8, bBigEnd) + iTIFFStart;
|
|
||||||
|
|
||||||
switch (iType) {
|
|
||||||
case 1: // byte, 8-bit unsigned int
|
|
||||||
case 7: // undefined, 8-bit byte, value depending on field
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getByteAt(iEntryOffset + 8, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getByteAt(iValOffset + n);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // ascii, 8-bit byte
|
|
||||||
var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
|
|
||||||
return oFile.getStringAt(iStringOffset, iNumValues - 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // short, 16 bit int
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getShortAt(iEntryOffset + 8, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8);
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getShortAt(iValOffset + 2 * n, bBigEnd);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // long, 32 bit int
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getLongAt(iEntryOffset + 8, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getLongAt(iValueOffset + 4 * n, bBigEnd);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5: // rational = two long values, first is numerator, second is denominator
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getLongAt(iValueOffset, bBigEnd) / oFile.getLongAt(iValueOffset + 4, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getLongAt(iValueOffset + 8 * n, bBigEnd) / oFile.getLongAt(iValueOffset + 4 + 8 * n, bBigEnd);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 9: // slong, 32 bit signed int
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getSLongAt(iEntryOffset + 8, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getSLongAt(iValueOffset + 4 * n, bBigEnd);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 10: // signed rational, two slongs, first is numerator, second is denominator
|
|
||||||
if (iNumValues == 1) {
|
|
||||||
return oFile.getSLongAt(iValueOffset, bBigEnd) / oFile.getSLongAt(iValueOffset + 4, bBigEnd);
|
|
||||||
} else {
|
|
||||||
var aVals = [];
|
|
||||||
for (var n = 0; n < iNumValues; n++) {
|
|
||||||
aVals[n] = oFile.getSLongAt(iValueOffset + 8 * n, bBigEnd) / oFile.getSLongAt(iValueOffset + 4 + 8 * n, bBigEnd);
|
|
||||||
}
|
|
||||||
return aVals;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function readEXIFData(oFile, iStart, iLength)
|
|
||||||
{
|
|
||||||
if (oFile.getStringAt(iStart, 4) != "Exif") {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bBigEnd;
|
|
||||||
|
|
||||||
var iTIFFOffset = iStart + 6;
|
|
||||||
|
|
||||||
// test for TIFF validity and endianness
|
|
||||||
if (oFile.getShortAt(iTIFFOffset) == 0x4949) {
|
|
||||||
bBigEnd = false;
|
|
||||||
} else if (oFile.getShortAt(iTIFFOffset) == 0x4D4D) {
|
|
||||||
bBigEnd = true;
|
|
||||||
} else {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oFile.getShortAt(iTIFFOffset + 2, bBigEnd) != 0x002A) {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Not valid TIFF data! (no 0x002A)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oFile.getLongAt(iTIFFOffset + 4, bBigEnd) != 0x00000008) {
|
|
||||||
if (bDebug)
|
|
||||||
console.log("Not valid TIFF data! (First offset not 8)", oFile.getShortAt(iTIFFOffset + 4, bBigEnd));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oTags = readTags(oFile, iTIFFOffset, iTIFFOffset + 8, EXIF.TiffTags, bBigEnd);
|
|
||||||
|
|
||||||
if (oTags.ExifIFDPointer) {
|
|
||||||
var oEXIFTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd);
|
|
||||||
for (var strTag in oEXIFTags) {
|
|
||||||
switch (strTag) {
|
|
||||||
case "LightSource" :
|
|
||||||
case "Flash" :
|
|
||||||
case "MeteringMode" :
|
|
||||||
case "ExposureProgram" :
|
|
||||||
case "SensingMethod" :
|
|
||||||
case "SceneCaptureType" :
|
|
||||||
case "SceneType" :
|
|
||||||
case "CustomRendered" :
|
|
||||||
case "WhiteBalance" :
|
|
||||||
case "GainControl" :
|
|
||||||
case "Contrast" :
|
|
||||||
case "Saturation" :
|
|
||||||
case "Sharpness" :
|
|
||||||
case "SubjectDistanceRange" :
|
|
||||||
case "FileSource" :
|
|
||||||
oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "ExifVersion" :
|
|
||||||
case "FlashpixVersion" :
|
|
||||||
oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "ComponentsConfiguration" :
|
|
||||||
oEXIFTags[strTag] =
|
|
||||||
EXIF.StringValues.Components[oEXIFTags[strTag][0]]
|
|
||||||
+ EXIF.StringValues.Components[oEXIFTags[strTag][1]]
|
|
||||||
+ EXIF.StringValues.Components[oEXIFTags[strTag][2]]
|
|
||||||
+ EXIF.StringValues.Components[oEXIFTags[strTag][3]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
oTags[strTag] = oEXIFTags[strTag];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oTags.GPSInfoIFDPointer) {
|
|
||||||
var oGPSTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd);
|
|
||||||
for (var strTag in oGPSTags) {
|
|
||||||
switch (strTag) {
|
|
||||||
case "GPSVersionID" :
|
|
||||||
oGPSTags[strTag] = oGPSTags[strTag][0]
|
|
||||||
+ "." + oGPSTags[strTag][1]
|
|
||||||
+ "." + oGPSTags[strTag][2]
|
|
||||||
+ "." + oGPSTags[strTag][3];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
oTags[strTag] = oGPSTags[strTag];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return oTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EXIF.getData = function(oImg, fncCallback)
|
|
||||||
{
|
|
||||||
if (!oImg.complete)
|
|
||||||
return false;
|
|
||||||
if (!imageHasData(oImg)) {
|
|
||||||
getImageData(oImg, fncCallback);
|
|
||||||
} else {
|
|
||||||
if (fncCallback)
|
|
||||||
fncCallback();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.getDataFromDataURL = function(oImg, fncCallback)
|
|
||||||
{
|
|
||||||
if (!oImg.complete)
|
|
||||||
return false;
|
|
||||||
if (!imageHasData(oImg)) {
|
|
||||||
getImageDataFromDataURL(oImg, fncCallback);
|
|
||||||
} else {
|
|
||||||
if (fncCallback)
|
|
||||||
fncCallback();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.getTag = function(oImg, strTag)
|
|
||||||
{
|
|
||||||
if (!imageHasData(oImg))
|
|
||||||
return;
|
|
||||||
return oImg.exifdata[strTag];
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.getAllTags = function(oImg)
|
|
||||||
{
|
|
||||||
if (!imageHasData(oImg))
|
|
||||||
return {};
|
|
||||||
var oData = oImg.exifdata;
|
|
||||||
var oAllTags = {};
|
|
||||||
for (var a in oData) {
|
|
||||||
if (oData.hasOwnProperty(a)) {
|
|
||||||
oAllTags[a] = oData[a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return oAllTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.pretty = function(oImg)
|
|
||||||
{
|
|
||||||
if (!imageHasData(oImg))
|
|
||||||
return "";
|
|
||||||
var oData = oImg.exifdata;
|
|
||||||
var strPretty = "";
|
|
||||||
for (var a in oData) {
|
|
||||||
if (oData.hasOwnProperty(a)) {
|
|
||||||
if (typeof oData[a] == "object") {
|
|
||||||
strPretty += a + " : [" + oData[a].length + " values]\r\n";
|
|
||||||
} else {
|
|
||||||
strPretty += a + " : " + oData[a] + "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strPretty;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXIF.readFromBinaryFile = function(oFile) {
|
|
||||||
return findEXIFinJPEG(oFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadAllImages()
|
|
||||||
{
|
|
||||||
var aImages = document.getElementsByTagName("img");
|
|
||||||
for (var i = 0; i < aImages.length; i++) {
|
|
||||||
if (aImages[i].getAttribute("exif") == "true") {
|
|
||||||
if (!aImages[i].complete) {
|
|
||||||
addEvent(aImages[i], "load",
|
|
||||||
function() {
|
|
||||||
EXIF.getData(this);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
EXIF.getData(aImages[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// automatically load exif data for all images with exif=true when doc is ready
|
|
||||||
jQuery(document).ready(loadAllImages);
|
|
||||||
|
|
||||||
// load data for images manually
|
|
||||||
jQuery.fn.exifLoad = function(fncCallback) {
|
|
||||||
return this.each(function() {
|
|
||||||
EXIF.getData(this, fncCallback)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// load data for images manually
|
|
||||||
jQuery.fn.exifLoadFromDataURL = function(fncCallback) {
|
|
||||||
return this.each(function() {
|
|
||||||
EXIF.getDataFromDataURL(this, fncCallback)
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery.fn.exif = function(strTag) {
|
|
||||||
var aStrings = [];
|
|
||||||
this.each(function() {
|
|
||||||
aStrings.push(EXIF.getTag(this, strTag));
|
|
||||||
});
|
|
||||||
return aStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery.fn.exifAll = function() {
|
|
||||||
var aStrings = [];
|
|
||||||
this.each(function() {
|
|
||||||
aStrings.push(EXIF.getAllTags(this));
|
|
||||||
});
|
|
||||||
return aStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery.fn.exifPretty = function() {
|
|
||||||
var aStrings = [];
|
|
||||||
this.each(function() {
|
|
||||||
aStrings.push(EXIF.pretty(this));
|
|
||||||
});
|
|
||||||
return aStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
Loading…
Add table
Reference in a new issue