| /* |
| * |
| * 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. |
| * |
| */ |
| |
| /*global require, module*/ |
| |
| var MediaFile = require('cordova-plugin-media-capture.MediaFile'); |
| var MediaFileData = require('cordova-plugin-media-capture.MediaFileData'); |
| var CaptureError = require('cordova-plugin-media-capture.CaptureError'); |
| |
| /** |
| * Helper function that converts data URI to Blob |
| * @param {String} dataURI Data URI to convert |
| * @return {Blob} Blob, covnerted from DataURI String |
| */ |
| function dataURItoBlob(dataURI) { |
| // convert base64 to raw binary data held in a string |
| // doesn't handle URLEncoded DataURIs |
| var byteString = atob(dataURI.split(',')[1]); |
| |
| // separate out the mime component |
| var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; |
| |
| // write the bytes of the string to an ArrayBuffer |
| var ab = new ArrayBuffer(byteString.length); |
| var ia = new Uint8Array(ab); |
| for (var i = 0; i < byteString.length; i++) { |
| ia[i] = byteString.charCodeAt(i); |
| } |
| |
| // write the ArrayBuffer to a blob, and you're done |
| return new Blob([ab], { type: mimeString }); |
| } |
| |
| /** |
| * Creates basic camera UI with preview 'video' element and 'Cancel' button |
| * Capture starts, when you clicking on preview. |
| */ |
| function CameraUI() { |
| |
| // Root element for preview |
| var container = document.createElement('div'); |
| container.style.cssText = "left: 0px; top: 0px; width: 100%; height: 100%; position: fixed; z-index:9999;" + |
| "padding: 40px; background-color: rgba(0,0,0,0.75);" + |
| "text-align:center; visibility: hidden"; |
| |
| // Set up root element contetnts |
| container.innerHTML = |
| '<div id="captureHint" style="height:100%; position:relative; display:inline-flex; align-content:flex-start;">' + |
| '<h2 style="position: absolute; width: 100%; background-color: rgba(255,255,255,0.25); margin: 0">' + |
| 'Click on preview to capture image. Click outside of preview to cancel.</h1>' + |
| '<video id="capturePreview" style="height: 100%"></video>' + |
| '</div>'; |
| |
| // Add container element to DOM but do not display it since visibility == hidden |
| document.body.appendChild(container); |
| |
| // Create fullscreen preview |
| var preview = document.getElementById('capturePreview'); |
| preview.autoplay = true; |
| // We'll show preview only when video element content |
| // is fully loaded to avoid glitches |
| preview.onplay = function () { |
| container.style.visibility = 'visible'; |
| }; |
| |
| this.container = container; |
| this.preview = preview; |
| } |
| |
| /** |
| * Displays capture preview |
| * @param {Number} count Number of images to take |
| * @param {Function} successCB Success callback, that accepts data URL of captured image |
| * @param {Function} errorCB Error callback |
| */ |
| CameraUI.prototype.startPreview = function(count, successCB, errorCB) { |
| var that = this; |
| |
| this.preview.onclick = function (e) { |
| // proceed with capture here |
| // We don't need to propagate click event to parent elements. |
| // Otherwise click on vieo element will trigger click event handler |
| // for preview root element and cause preview cancellation |
| e.stopPropagation(); |
| // Create canvas element, put video frame on it |
| // and save its contant as Data URL |
| var canvas = document.createElement('canvas'); |
| canvas.width = this.videoWidth; |
| canvas.height = this.videoHeight; |
| canvas.getContext('2d').drawImage(that.preview, 0, 0); |
| successCB(canvas.toDataURL('image/jpeg')); |
| }; |
| |
| this.container.onclick = function () { |
| // Cancel capture here |
| errorCB(new CaptureError(CaptureError.CAPTURE_NO_MEDIA_FILES)); |
| }; |
| |
| navigator.getUserMedia({video: true}, function (previewStream) { |
| // Save video stream to be able to stop it later |
| that._previewStream = previewStream; |
| that.preview.src = URL.createObjectURL(previewStream); |
| // We don't need to set visibility = true for preview element |
| // since this will be done automatically in onplay event handler |
| }, function (/*err*/) { |
| errorCB(new CaptureError(CaptureError.CAPTURE_INTERNAL_ERR)); |
| }); |
| }; |
| |
| /** |
| * Destroys camera preview, removes all elements created |
| */ |
| CameraUI.prototype.destroyPreview = function () { |
| this.preview.pause(); |
| this.preview.src = null; |
| this._previewStream.stop(); |
| this._previewStream = null; |
| if (this.container) { |
| document.body.removeChild(this.container); |
| } |
| }; |
| |
| |
| module.exports = { |
| captureAudio:function(successCallback, errorCallback) { |
| if (errorCallback) { |
| errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED)); |
| } |
| }, |
| |
| captureVideo:function (successCallback, errorCallback) { |
| if (errorCallback) { |
| errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED)); |
| } |
| }, |
| |
| captureImage:function (successCallback, errorCallback, args) { |
| |
| var fail = function (code) { |
| if (errorCallback) { |
| errorCallback(new CaptureError(code || CaptureError.CAPTURE_INTERNAL_ERR)); |
| } |
| }; |
| |
| var options = args[0]; |
| |
| var limit = options.limit || 1; |
| if (typeof limit !== 'number' || limit < 1) { |
| fail(CaptureError.CAPTURE_INVALID_ARGUMENT); |
| return; |
| } |
| |
| // Counter for already taken images |
| var imagesTaken = 0; |
| |
| navigator.getUserMedia = navigator.getUserMedia || |
| navigator.webkitGetUserMedia || |
| navigator.mozGetUserMedia || |
| navigator.msGetUserMedia; |
| |
| if (!navigator.getUserMedia) { |
| fail(CaptureError.CAPTURE_NOT_SUPPORTED); |
| return; |
| } |
| |
| var ui = new CameraUI(); |
| ui.startPreview(limit, function (data) { |
| // Check if we're done with capture. If so, then destroy UI |
| if (++imagesTaken >= limit) { |
| ui.destroyPreview(); |
| } |
| |
| // Array of resultant MediaFiles |
| var mediaFiles = []; |
| |
| // save data to file here |
| window.requestFileSystem(window.TEMPORARY, data.length * limit, function (fileSystem) { |
| // If we need to capture multiple files, then append counter to filename |
| var fileName = limit <= 1 ? 'image.jpg' : 'image' + imagesTaken + '.jpg'; |
| fileSystem.root.getFile(fileName, {create: true}, function (file) { |
| file.createWriter(function (writer) { |
| writer.onwriteend = function () { |
| file.getMetadata(function (meta) { |
| mediaFiles.push(new MediaFile(file.name, file.toURL(), 'image/jpeg', meta.modificationTime, meta.size)); |
| // Check if we're done with capture. If so, then call a successCallback |
| if (imagesTaken >= limit) { |
| successCallback(mediaFiles); |
| } |
| }, fail); |
| }; |
| writer.onerror = fail; |
| // Since success callback for start preview returns |
| // a base64 encoded string, we need to convert it to blob first |
| writer.write(dataURItoBlob(data)); |
| }); |
| }, fail); |
| }, fail); |
| }, function (err) { |
| ui.destroyPreview(); |
| fail(err.code); |
| }); |
| }, |
| |
| getFormatData: function (successCallback, errorCallback, args) { |
| |
| var img = document.createElement('img'); |
| img.src = args[0]; |
| img.onload = function () { |
| if (successCallback) { |
| successCallback(new MediaFileData(null, 0, img.height, img.width, 0)); |
| } |
| }; |
| } |
| }; |
| |
| require("cordova/exec/proxy").add("Capture",module.exports); |