From 68d68e9009551b89425c375d8a2a3cd0870d710e Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 3 Mar 2015 17:50:11 -0800 Subject: [PATCH] Add blueimp lib for html5's canvas-based image scaling --- bower.json | 9 +- .../blueimp-load-image/js/load-image.js | 301 +++++++++++++++++ js/components.js | 302 ++++++++++++++++++ 3 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 components/blueimp-load-image/js/load-image.js diff --git a/bower.json b/bower.json index e63d70f5..0fcb668a 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,8 @@ "momentjs": "~2.8.3", "indexeddb-backbonejs-adapter": "*", "intl-tel-input": "~4.0.1", - "backbone.typeahead": "mojotech/backbone.typeahead" + "backbone.typeahead": "mojotech/backbone.typeahead", + "blueimp-load-image": "~1.13.0" }, "devDependencies": { "mocha": "~2.0.1", @@ -88,6 +89,9 @@ ], "backbone.typeahead": [ "backbone.typeahead.js" + ], + "blueimp-load-image": [ + "js/load-image.js" ] }, "concat": { @@ -104,7 +108,8 @@ "libphonenumber-api", "momentjs", "intl-tel-input", - "backbone.typeahead" + "backbone.typeahead", + "blueimp-load-image" ], "libtextsecure": [ "jquery", diff --git a/components/blueimp-load-image/js/load-image.js b/components/blueimp-load-image/js/load-image.js new file mode 100644 index 00000000..adb63a98 --- /dev/null +++ b/components/blueimp-load-image/js/load-image.js @@ -0,0 +1,301 @@ +/* + * JavaScript Load Image 1.10.0 + * https://github.com/blueimp/JavaScript-Load-Image + * + * Copyright 2011, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true */ +/*global define, window, document, URL, webkitURL, Blob, File, FileReader */ + +(function ($) { + 'use strict'; + + // Loads an image for a given File object. + // Invokes the callback with an img or optional canvas + // element (if supported by the browser) as parameter: + var loadImage = function (file, callback, options) { + var img = document.createElement('img'), + url, + oUrl; + img.onerror = callback; + img.onload = function () { + if (oUrl && !(options && options.noRevoke)) { + loadImage.revokeObjectURL(oUrl); + } + if (callback) { + callback(loadImage.scale(img, options)); + } + }; + if (loadImage.isInstanceOf('Blob', file) || + // Files are also Blob instances, but some browsers + // (Firefox 3.6) support the File API but not Blobs: + loadImage.isInstanceOf('File', file)) { + url = oUrl = loadImage.createObjectURL(file); + // Store the file type for resize processing: + img._type = file.type; + } else if (typeof file === 'string') { + url = file; + if (options && options.crossOrigin) { + img.crossOrigin = options.crossOrigin; + } + } else { + return false; + } + if (url) { + img.src = url; + return img; + } + return loadImage.readFile(file, function (e) { + var target = e.target; + if (target && target.result) { + img.src = target.result; + } else { + if (callback) { + callback(e); + } + } + }); + }, + // The check for URL.revokeObjectURL fixes an issue with Opera 12, + // which provides URL.createObjectURL but doesn't properly implement it: + urlAPI = (window.createObjectURL && window) || + (window.URL && URL.revokeObjectURL && URL) || + (window.webkitURL && webkitURL); + + loadImage.isInstanceOf = function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }; + + // Transform image coordinates, allows to override e.g. + // the canvas orientation based on the orientation option, + // gets canvas, options passed as arguments: + loadImage.transformCoordinates = function () { + return; + }; + + // Returns transformed options, allows to override e.g. + // maxWidth, maxHeight and crop options based on the aspectRatio. + // gets img, options passed as arguments: + loadImage.getTransformedOptions = function (img, options) { + var aspectRatio = options.aspectRatio, + newOptions, + i, + width, + height; + if (!aspectRatio) { + return options; + } + newOptions = {}; + for (i in options) { + if (options.hasOwnProperty(i)) { + newOptions[i] = options[i]; + } + } + newOptions.crop = true; + width = img.naturalWidth || img.width; + height = img.naturalHeight || img.height; + if (width / height > aspectRatio) { + newOptions.maxWidth = height * aspectRatio; + newOptions.maxHeight = height; + } else { + newOptions.maxWidth = width; + newOptions.maxHeight = width / aspectRatio; + } + return newOptions; + }; + + // Canvas render method, allows to override the + // rendering e.g. to work around issues on iOS: + loadImage.renderImageToCanvas = function ( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ) { + canvas.getContext('2d').drawImage( + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ); + return canvas; + }; + + // This method is used to determine if the target image + // should be a canvas element: + loadImage.hasCanvasOption = function (options) { + return options.canvas || options.crop || options.aspectRatio; + }; + + // Scales and/or crops the given image (img or canvas HTML element) + // using the given options. + // Returns a canvas object if the browser supports canvas + // and the hasCanvasOption method returns true or a canvas + // object is passed as image, else the scaled image: + loadImage.scale = function (img, options) { + options = options || {}; + var canvas = document.createElement('canvas'), + useCanvas = img.getContext || + (loadImage.hasCanvasOption(options) && canvas.getContext), + width = img.naturalWidth || img.width, + height = img.naturalHeight || img.height, + destWidth = width, + destHeight = height, + maxWidth, + maxHeight, + minWidth, + minHeight, + sourceWidth, + sourceHeight, + sourceX, + sourceY, + tmp, + scaleUp = function () { + var scale = Math.max( + (minWidth || destWidth) / destWidth, + (minHeight || destHeight) / destHeight + ); + if (scale > 1) { + destWidth = destWidth * scale; + destHeight = destHeight * scale; + } + }, + scaleDown = function () { + var scale = Math.min( + (maxWidth || destWidth) / destWidth, + (maxHeight || destHeight) / destHeight + ); + if (scale < 1) { + destWidth = destWidth * scale; + destHeight = destHeight * scale; + } + }; + if (useCanvas) { + options = loadImage.getTransformedOptions(img, options); + sourceX = options.left || 0; + sourceY = options.top || 0; + if (options.sourceWidth) { + sourceWidth = options.sourceWidth; + if (options.right !== undefined && options.left === undefined) { + sourceX = width - sourceWidth - options.right; + } + } else { + sourceWidth = width - sourceX - (options.right || 0); + } + if (options.sourceHeight) { + sourceHeight = options.sourceHeight; + if (options.bottom !== undefined && options.top === undefined) { + sourceY = height - sourceHeight - options.bottom; + } + } else { + sourceHeight = height - sourceY - (options.bottom || 0); + } + destWidth = sourceWidth; + destHeight = sourceHeight; + } + maxWidth = options.maxWidth; + maxHeight = options.maxHeight; + minWidth = options.minWidth; + minHeight = options.minHeight; + if (useCanvas && maxWidth && maxHeight && options.crop) { + destWidth = maxWidth; + destHeight = maxHeight; + tmp = sourceWidth / sourceHeight - maxWidth / maxHeight; + if (tmp < 0) { + sourceHeight = maxHeight * sourceWidth / maxWidth; + if (options.top === undefined && options.bottom === undefined) { + sourceY = (height - sourceHeight) / 2; + } + } else if (tmp > 0) { + sourceWidth = maxWidth * sourceHeight / maxHeight; + if (options.left === undefined && options.right === undefined) { + sourceX = (width - sourceWidth) / 2; + } + } + } else { + if (options.contain || options.cover) { + minWidth = maxWidth = maxWidth || minWidth; + minHeight = maxHeight = maxHeight || minHeight; + } + if (options.cover) { + scaleDown(); + scaleUp(); + } else { + scaleUp(); + scaleDown(); + } + } + if (useCanvas) { + canvas.width = destWidth; + canvas.height = destHeight; + loadImage.transformCoordinates( + canvas, + options + ); + return loadImage.renderImageToCanvas( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + 0, + 0, + destWidth, + destHeight + ); + } + img.width = destWidth; + img.height = destHeight; + return img; + }; + + loadImage.createObjectURL = function (file) { + return urlAPI ? urlAPI.createObjectURL(file) : false; + }; + + loadImage.revokeObjectURL = function (url) { + return urlAPI ? urlAPI.revokeObjectURL(url) : false; + }; + + // Loads a given File object via FileReader interface, + // invokes the callback with the event object (load or error). + // The result can be read via event.target.result: + loadImage.readFile = function (file, callback, method) { + if (window.FileReader) { + var fileReader = new FileReader(); + fileReader.onload = fileReader.onerror = callback; + method = method || 'readAsDataURL'; + if (fileReader[method]) { + fileReader[method](file); + return fileReader; + } + } + return false; + }; + + if (typeof define === 'function' && define.amd) { + define(function () { + return loadImage; + }); + } else { + $.loadImage = loadImage; + } +}(this)); diff --git a/js/components.js b/js/components.js index 9232a79d..833793d7 100644 --- a/js/components.js +++ b/js/components.js @@ -27501,3 +27501,305 @@ JSON.stringify(result); }).call(this); //# sourceMappingURL=backbone.typeahead.js.map + +/* + * JavaScript Load Image 1.10.0 + * https://github.com/blueimp/JavaScript-Load-Image + * + * Copyright 2011, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true */ +/*global define, window, document, URL, webkitURL, Blob, File, FileReader */ + +(function ($) { + 'use strict'; + + // Loads an image for a given File object. + // Invokes the callback with an img or optional canvas + // element (if supported by the browser) as parameter: + var loadImage = function (file, callback, options) { + var img = document.createElement('img'), + url, + oUrl; + img.onerror = callback; + img.onload = function () { + if (oUrl && !(options && options.noRevoke)) { + loadImage.revokeObjectURL(oUrl); + } + if (callback) { + callback(loadImage.scale(img, options)); + } + }; + if (loadImage.isInstanceOf('Blob', file) || + // Files are also Blob instances, but some browsers + // (Firefox 3.6) support the File API but not Blobs: + loadImage.isInstanceOf('File', file)) { + url = oUrl = loadImage.createObjectURL(file); + // Store the file type for resize processing: + img._type = file.type; + } else if (typeof file === 'string') { + url = file; + if (options && options.crossOrigin) { + img.crossOrigin = options.crossOrigin; + } + } else { + return false; + } + if (url) { + img.src = url; + return img; + } + return loadImage.readFile(file, function (e) { + var target = e.target; + if (target && target.result) { + img.src = target.result; + } else { + if (callback) { + callback(e); + } + } + }); + }, + // The check for URL.revokeObjectURL fixes an issue with Opera 12, + // which provides URL.createObjectURL but doesn't properly implement it: + urlAPI = (window.createObjectURL && window) || + (window.URL && URL.revokeObjectURL && URL) || + (window.webkitURL && webkitURL); + + loadImage.isInstanceOf = function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }; + + // Transform image coordinates, allows to override e.g. + // the canvas orientation based on the orientation option, + // gets canvas, options passed as arguments: + loadImage.transformCoordinates = function () { + return; + }; + + // Returns transformed options, allows to override e.g. + // maxWidth, maxHeight and crop options based on the aspectRatio. + // gets img, options passed as arguments: + loadImage.getTransformedOptions = function (img, options) { + var aspectRatio = options.aspectRatio, + newOptions, + i, + width, + height; + if (!aspectRatio) { + return options; + } + newOptions = {}; + for (i in options) { + if (options.hasOwnProperty(i)) { + newOptions[i] = options[i]; + } + } + newOptions.crop = true; + width = img.naturalWidth || img.width; + height = img.naturalHeight || img.height; + if (width / height > aspectRatio) { + newOptions.maxWidth = height * aspectRatio; + newOptions.maxHeight = height; + } else { + newOptions.maxWidth = width; + newOptions.maxHeight = width / aspectRatio; + } + return newOptions; + }; + + // Canvas render method, allows to override the + // rendering e.g. to work around issues on iOS: + loadImage.renderImageToCanvas = function ( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ) { + canvas.getContext('2d').drawImage( + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ); + return canvas; + }; + + // This method is used to determine if the target image + // should be a canvas element: + loadImage.hasCanvasOption = function (options) { + return options.canvas || options.crop || options.aspectRatio; + }; + + // Scales and/or crops the given image (img or canvas HTML element) + // using the given options. + // Returns a canvas object if the browser supports canvas + // and the hasCanvasOption method returns true or a canvas + // object is passed as image, else the scaled image: + loadImage.scale = function (img, options) { + options = options || {}; + var canvas = document.createElement('canvas'), + useCanvas = img.getContext || + (loadImage.hasCanvasOption(options) && canvas.getContext), + width = img.naturalWidth || img.width, + height = img.naturalHeight || img.height, + destWidth = width, + destHeight = height, + maxWidth, + maxHeight, + minWidth, + minHeight, + sourceWidth, + sourceHeight, + sourceX, + sourceY, + tmp, + scaleUp = function () { + var scale = Math.max( + (minWidth || destWidth) / destWidth, + (minHeight || destHeight) / destHeight + ); + if (scale > 1) { + destWidth = destWidth * scale; + destHeight = destHeight * scale; + } + }, + scaleDown = function () { + var scale = Math.min( + (maxWidth || destWidth) / destWidth, + (maxHeight || destHeight) / destHeight + ); + if (scale < 1) { + destWidth = destWidth * scale; + destHeight = destHeight * scale; + } + }; + if (useCanvas) { + options = loadImage.getTransformedOptions(img, options); + sourceX = options.left || 0; + sourceY = options.top || 0; + if (options.sourceWidth) { + sourceWidth = options.sourceWidth; + if (options.right !== undefined && options.left === undefined) { + sourceX = width - sourceWidth - options.right; + } + } else { + sourceWidth = width - sourceX - (options.right || 0); + } + if (options.sourceHeight) { + sourceHeight = options.sourceHeight; + if (options.bottom !== undefined && options.top === undefined) { + sourceY = height - sourceHeight - options.bottom; + } + } else { + sourceHeight = height - sourceY - (options.bottom || 0); + } + destWidth = sourceWidth; + destHeight = sourceHeight; + } + maxWidth = options.maxWidth; + maxHeight = options.maxHeight; + minWidth = options.minWidth; + minHeight = options.minHeight; + if (useCanvas && maxWidth && maxHeight && options.crop) { + destWidth = maxWidth; + destHeight = maxHeight; + tmp = sourceWidth / sourceHeight - maxWidth / maxHeight; + if (tmp < 0) { + sourceHeight = maxHeight * sourceWidth / maxWidth; + if (options.top === undefined && options.bottom === undefined) { + sourceY = (height - sourceHeight) / 2; + } + } else if (tmp > 0) { + sourceWidth = maxWidth * sourceHeight / maxHeight; + if (options.left === undefined && options.right === undefined) { + sourceX = (width - sourceWidth) / 2; + } + } + } else { + if (options.contain || options.cover) { + minWidth = maxWidth = maxWidth || minWidth; + minHeight = maxHeight = maxHeight || minHeight; + } + if (options.cover) { + scaleDown(); + scaleUp(); + } else { + scaleUp(); + scaleDown(); + } + } + if (useCanvas) { + canvas.width = destWidth; + canvas.height = destHeight; + loadImage.transformCoordinates( + canvas, + options + ); + return loadImage.renderImageToCanvas( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + 0, + 0, + destWidth, + destHeight + ); + } + img.width = destWidth; + img.height = destHeight; + return img; + }; + + loadImage.createObjectURL = function (file) { + return urlAPI ? urlAPI.createObjectURL(file) : false; + }; + + loadImage.revokeObjectURL = function (url) { + return urlAPI ? urlAPI.revokeObjectURL(url) : false; + }; + + // Loads a given File object via FileReader interface, + // invokes the callback with the event object (load or error). + // The result can be read via event.target.result: + loadImage.readFile = function (file, callback, method) { + if (window.FileReader) { + var fileReader = new FileReader(); + fileReader.onload = fileReader.onerror = callback; + method = method || 'readAsDataURL'; + if (fileReader[method]) { + fileReader[method](file); + return fileReader; + } + } + return false; + }; + + if (typeof define === 'function' && define.amd) { + define(function () { + return loadImage; + }); + } else { + $.loadImage = loadImage; + } +}(this));