Skip to content

Commit

Permalink
Added browser support (Telerik-Verified-Plugins#172)
Browse files Browse the repository at this point in the history
-  Added browser support.
  • Loading branch information
fefc authored and anujraghuvanshi committed Nov 28, 2019
1 parent bbc1631 commit 94f0aac
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 0 deletions.
19 changes: 19 additions & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,23 @@
<framework src="src/android/ignorelinterrors.gradle" custom="true" type="gradleReference"/>
<framework src="src/android/androidtarget.gradle" custom="true" type="gradleReference"/>
</platform>

<!-- browser -->
<platform name="browser">
<config-file parent="/*" target="config.xml">
<feature name="ImagePicker">
<param name="browser-package" value="ImagePicker"/>
</feature>
</config-file>

<!-- Required for browserify: we always link module below as there is conditional reference
to this module from requestFileSystem and resolveLocalFileSystemURI modules. -->
<js-module src="www/browser/isChrome.js" name="isChrome">
<runs />
</js-module>

<js-module src="src/browser/ImagePicker.js" name="ImagePickerProxy">
<runs/>
</js-module>
</platform>
</plugin>
213 changes: 213 additions & 0 deletions src/browser/ImagePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
const cacheDirectory = (require('./isChrome')()) ? 'filesystem:' + window.location.origin + '/temporary/' : 'file:///temporary/';
const nonScalableTypes = ['video', 'gif'];

//Edge needs its own stuff
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function (callback, type, quality) {
var canvas = this;
setTimeout(function() {
var binStr = atob( canvas.toDataURL(type, quality).split(',')[1] ),
len = binStr.length,
arr = new Uint8Array(len);

for (var i = 0; i < len; i++ ) {
arr[i] = binStr.charCodeAt(i);
}

callback( new Blob( [arr], {type: type || 'image/png'} ) );
});
}
});
}

async function getPictures(successCallback, errorCallback, data) {
//Set default params just in case
var params = {
maximumImagesCount: 20,
width: 0,
height: 0,
quality: 100,
outputType: 0,
allow_video: false
}

//Read params from cordova
if (data[0].maximumImagesCount) params.maximumImagesCount = data[0].maximumImagesCount;
if (data[0].width) params.width = data[0].width;
if (data[0].height) params.height = data[0].height;
if (data[0].quality) params.quality = data[0].quality;
if (data[0].outputType) params.outputType = data[0].outputType;
if (data[0].allow_video) params.allow_video = data[0].allow_video;

openImagePicker(params.maximumImagesCount, params.width, params.height, params.quality, params.outputType, params.allow_video).then((images) => {
successCallback(images);
}).catch((error) => {
errorCallback(error);
});
}

//hasReadPermission is not relevant in browsers, let's just return a success so everyone is happy
async function hasReadPermission(successCallback, errorCallback, data) {
successCallback();
}

//requestReadPermission is not relevant in browsers, let's just return a success so everyone is happy
async function requestReadPermission(successCallback, errorCallback, data) {
successCallback();
}

function openImagePicker(maximumImagesCount, desiredWidth, desiredHeight, quality, outputType, allowVideo) {
return new Promise((resolve, reject) => {
let fileChooser = document.createElement('input');

fileChooser.type = 'file';
fileChooser.accept = 'image/png, image/jpeg, image/gif';
if (allowVideo) fileChooser.accept += ', video/mp4, video/webm, video/ogg';

if (maximumImagesCount > 1) {
fileChooser.setAttribute('multiple', '');
}

fileChooser.onchange = (event) => {
let resizeImagePromises = [];
let fileNames = []; //We need to store filenames as chrome deletes event.target.files to quickly

if (event.target.files.length <= maximumImagesCount) {
for (let file of event.target.files) {
fileNames.push('tmp_' + getDateTimeString() + '_' + file.name);
resizeImagePromises.push(resizeImage(file, desiredWidth, desiredHeight, quality, outputType));
}

Promise.all(resizeImagePromises).then((images) => {
if (outputType == 0) {
//If we need FILE_URI, we need to store the files in the temporary dir of the browser
//So we need to write the files there Before
let saveBlobPromises = [];

for (let i = 0; i < images.length; i++) {
saveBlobPromises.push(saveBlobToTemporaryFileSystem(images[i], fileNames[i]));
}

Promise.all(saveBlobPromises).then((fileURIs) => {
resolve(fileURIs);
}).catch((error) => {
reject('ERROR_WHILE_CREATING_FILES ' + error);
});
} else {
//resizeImages returns the images in base64, so no need to do anything
resolve(images);
}
}).catch((error) => {
reject('ERROR_WHILE_RESIZING ' + error);
});
} else {
reject('TO_MANY_IMAGES');
}
};

//Now that the event is hooked we can click it
fileChooser.click();
});
}

//Promise resolving either a blob or a base64 string depending on outputType
function resizeImage(file, desiredWidth, desiredHeight, quality, outputType) {
return new Promise((resolve, reject) => {
if (outputType == 1 || ( (desiredWidth != 0 || desiredHeight != 0) && !nonScalableTypes.some(type => file.type.includes(type)) ) ) {
//BASE64_STRING required or
//Scaling required
//Base64 string needs to be jpeg formated so even if no scaling is required we need to do the conversion
let reader = new FileReader();
reader.readAsDataURL(file);

reader.onload = (e) => {
let img = new Image();
img.src = e.target.result;
img.onload = (pic) => {
let canvas = document.createElement('canvas');

if ((img.height > desiredHeight || img.width > desiredWidth) && (desiredWidth != 0 || desiredHeight != 0)) {
if ((img.height / desiredHeight) > (img.width / desiredWidth)) {
canvas.width = img.width / (img.height / desiredHeight)
canvas.height = desiredHeight;
} else {
canvas.width = desiredWidth
canvas.height = img.height / (img.width / desiredWidth) ;
}
} else {
canvas.width = img.width;
canvas.height = img.height;
}

let ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

if (outputType == 0) {
//FILE_URI required, resolve with a blob
ctx.canvas.toBlob((blob) => {
resolve(blob);
}, 'image/jpeg', (quality / 100));

} else {
//BASE64_STRING required, resolve with a string
//To be consistent with Android (and iOS) we remove the base64 header from the string
resolve(ctx.canvas.toDataURL('image/jpeg', (quality / 100)).replace('data:image/jpeg;base64,', ''));
}
};
};

reader.onerror = (error) => {
reject(error);
}
} else {
//No scaling required and FILE_URI required?
//FILE_URI required, resolve with a blob (a file is a blob)
resolve(file);
}
});
}

function saveBlobToTemporaryFileSystem(blob, fileName) {
return new Promise((resolve, reject) => {
//Just make sure file extensions matches file type if possible
var ext = fileName.split('.').pop();

if (blob.type && ext) {
if (!blob.type.includes(ext.toLowerCase())) fileName = fileName.replace(ext, blob.type.split('/').pop());
}

window.requestFileSystem(window.TEMPORARY, blob.size, (fs) => {
fs.root.getFile(fileName, {create: true, exclusive: false}, (fileEntry) => {
fileEntry.createWriter((fileWriter) => {
fileWriter.onwriteend = (e) => {
resolve(cacheDirectory + fileName);
};

fileWriter.onerror = (error) => {
reject(error);
};

if (blob) {
fileWriter.write(blob);
} else {
reject('ERROR_NO_DATA_TO_WRITE');
}
});
});
});
});
}

function getDateTimeString() {
let d = new Date();
return d.getDate().toString() + d.getMonth().toString() + d.getFullYear().toString() + '_' + d.getHours().toString() + d.getMinutes().toString() + d.getSeconds().toString();
}

module.exports = {
getPictures: getPictures,
hasReadPermission: hasReadPermission,
requestReadPermission: requestReadPermission
};

require( "cordova/exec/proxy" ).add( "ImagePicker", module.exports );
26 changes: 26 additions & 0 deletions www/browser/isChrome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/

module.exports = function () {
// window.webkitRequestFileSystem and window.webkitResolveLocalFileSystemURL are available only in Chrome and
// possibly a good flag to indicate that we're running in Chrome
return window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL;
};

0 comments on commit 94f0aac

Please sign in to comment.