BarcodeScannerProxy.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /*
  2. * Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
  3. *
  4. * Licensed 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
  5. *
  6. * http://www.apache.org/licenses/LICENSE-2.0
  7. *
  8. * 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.
  9. */
  10. var urlutil = require('cordova/urlutil');
  11. var CAMERA_STREAM_STATE_CHECK_RETRY_TIMEOUT = 200; // milliseconds
  12. var OPERATION_IS_IN_PROGRESS = -2147024567;
  13. var REGDB_E_CLASSNOTREG = -2147221164;
  14. var INITIAL_FOCUS_DELAY = 200; // milliseconds
  15. var CHECK_PLAYING_TIMEOUT = 100; // milliseconds
  16. /**
  17. * List of supported barcode formats from ZXing library. Used to return format
  18. * name instead of number code as per plugin spec.
  19. *
  20. * @enum {String}
  21. */
  22. var BARCODE_FORMAT = {
  23. 1: 'AZTEC',
  24. 2: 'CODABAR',
  25. 4: 'CODE_39',
  26. 8: 'CODE_93',
  27. 16: 'CODE_128',
  28. 32: 'DATA_MATRIX',
  29. 64: 'EAN_8',
  30. 128: 'EAN_13',
  31. 256: 'ITF',
  32. 512: 'MAXICODE',
  33. 1024: 'PDF_417',
  34. 2048: 'QR_CODE',
  35. 4096: 'RSS_14',
  36. 8192: 'RSS_EXPANDED',
  37. 16384: 'UPC_A',
  38. 32768: 'UPC_E',
  39. 61918: 'All_1D',
  40. 65536: 'UPC_EAN_EXTENSION',
  41. 131072: 'MSI',
  42. 262144: 'PLESSEY'
  43. };
  44. /**
  45. * Detects the first appropriate camera located at the back panel of device. If
  46. * there is no back cameras, returns the first available.
  47. *
  48. * @returns {Promise<String>} Camera id
  49. */
  50. function findCamera() {
  51. var Devices = Windows.Devices.Enumeration;
  52. // Enumerate cameras and add them to the list
  53. return Devices.DeviceInformation.findAllAsync(Devices.DeviceClass.videoCapture)
  54. .then(function (cameras) {
  55. if (!cameras || cameras.length === 0) {
  56. throw new Error("No cameras found");
  57. }
  58. var backCameras = cameras.filter(function (camera) {
  59. return camera.enclosureLocation && camera.enclosureLocation.panel === Devices.Panel.back;
  60. });
  61. // If there is back cameras, return the id of the first,
  62. // otherwise take the first available device's id
  63. return (backCameras[0] || cameras[0]).id;
  64. });
  65. }
  66. /**
  67. * @param {Windows.Graphics.Display.DisplayOrientations} displayOrientation
  68. * @return {Number}
  69. */
  70. function videoPreviewRotationLookup(displayOrientation, isMirrored) {
  71. var degreesToRotate;
  72. switch (displayOrientation) {
  73. case Windows.Graphics.Display.DisplayOrientations.landscape:
  74. degreesToRotate = 0;
  75. break;
  76. case Windows.Graphics.Display.DisplayOrientations.portrait:
  77. if (isMirrored) {
  78. degreesToRotate = 270;
  79. } else {
  80. degreesToRotate = 90;
  81. }
  82. break;
  83. case Windows.Graphics.Display.DisplayOrientations.landscapeFlipped:
  84. degreesToRotate = 180;
  85. break;
  86. case Windows.Graphics.Display.DisplayOrientations.portraitFlipped:
  87. if (isMirrored) {
  88. degreesToRotate = 90;
  89. } else {
  90. degreesToRotate = 270;
  91. }
  92. break;
  93. default:
  94. degreesToRotate = 0;
  95. break;
  96. }
  97. return degreesToRotate;
  98. }
  99. /**
  100. * The pure JS implementation of barcode reader from WinRTBarcodeReader.winmd.
  101. * Works only on Windows 10 devices and more efficient than original one.
  102. *
  103. * @class {BarcodeReader}
  104. */
  105. function BarcodeReader () {
  106. this._promise = null;
  107. this._cancelled = false;
  108. }
  109. /**
  110. * Returns an instance of Barcode reader, depending on capabilities of Media
  111. * Capture API
  112. *
  113. * @static
  114. * @constructs {BarcodeReader}
  115. *
  116. * @param {MediaCapture} mediaCaptureInstance Instance of
  117. * Windows.Media.Capture.MediaCapture class
  118. *
  119. * @return {BarcodeReader} BarcodeReader instance that could be used for
  120. * scanning
  121. */
  122. BarcodeReader.get = function (mediaCaptureInstance) {
  123. if (mediaCaptureInstance.getPreviewFrameAsync && ZXing.BarcodeReader) {
  124. return new BarcodeReader();
  125. }
  126. // If there is no corresponding API (Win8/8.1/Phone8.1) use old approach with WinMD library
  127. return new WinRTBarcodeReader.Reader();
  128. };
  129. /**
  130. * Initializes instance of reader.
  131. *
  132. * @param {MediaCapture} capture Instance of
  133. * Windows.Media.Capture.MediaCapture class, used for acquiring images/ video
  134. * stream for barcode scanner.
  135. * @param {Number} width Video/image frame width
  136. * @param {Number} height Video/image frame height
  137. */
  138. BarcodeReader.prototype.init = function (capture, width, height) {
  139. this._capture = capture;
  140. this._width = width;
  141. this._height = height;
  142. this._zxingReader = new ZXing.BarcodeReader();
  143. this._zxingReader.tryHarder = true;
  144. };
  145. /**
  146. * Starts barcode search routines asyncronously.
  147. *
  148. * @return {Promise<ScanResult>} barcode scan result or null if search
  149. * cancelled.
  150. */
  151. BarcodeReader.prototype.readCode = function () {
  152. /**
  153. * Grabs a frame from preview stream uning Win10-only API and tries to
  154. * get a barcode using zxing reader provided. If there is no barcode
  155. * found, returns null.
  156. */
  157. function scanBarcodeAsync(mediaCapture, zxingReader, frameWidth, frameHeight) {
  158. // Shortcuts for namespaces
  159. var Imaging = Windows.Graphics.Imaging;
  160. var Streams = Windows.Storage.Streams;
  161. var frame = new Windows.Media.VideoFrame(Imaging.BitmapPixelFormat.bgra8, frameWidth, frameHeight);
  162. return mediaCapture.getPreviewFrameAsync(frame)
  163. .then(function (capturedFrame) {
  164. // Copy captured frame to buffer for further deserialization
  165. var bitmap = capturedFrame.softwareBitmap;
  166. var rawBuffer = new Streams.Buffer(bitmap.pixelWidth * bitmap.pixelHeight * 4);
  167. capturedFrame.softwareBitmap.copyToBuffer(rawBuffer);
  168. capturedFrame.close();
  169. // Get raw pixel data from buffer
  170. var data = new Uint8Array(rawBuffer.length);
  171. var dataReader = Streams.DataReader.fromBuffer(rawBuffer);
  172. dataReader.readBytes(data);
  173. dataReader.close();
  174. return zxingReader.decode(data, frameWidth, frameHeight, ZXing.BitmapFormat.bgra32);
  175. });
  176. }
  177. var self = this;
  178. return scanBarcodeAsync(this._capture, this._zxingReader, this._width, this._height)
  179. .then(function (result) {
  180. if (self._cancelled)
  181. return null;
  182. return result || (self._promise = self.readCode());
  183. });
  184. };
  185. /**
  186. * Stops barcode search
  187. */
  188. BarcodeReader.prototype.stop = function () {
  189. this._cancelled = true;
  190. };
  191. function degreesToRotation(degrees) {
  192. switch (degrees) {
  193. // portrait
  194. case 90:
  195. return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
  196. // landscape
  197. case 0:
  198. return Windows.Media.Capture.VideoRotation.none;
  199. // portrait-flipped
  200. case 270:
  201. return Windows.Media.Capture.VideoRotation.clockwise270Degrees;
  202. // landscape-flipped
  203. case 180:
  204. return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
  205. default:
  206. // Falling back to portrait default
  207. return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
  208. }
  209. }
  210. module.exports = {
  211. /**
  212. * Scans image via device camera and retieves barcode from it.
  213. * @param {function} success Success callback
  214. * @param {function} fail Error callback
  215. * @param {array} args Arguments array
  216. */
  217. scan: function (success, fail, args) {
  218. var capturePreview,
  219. capturePreviewAlignmentMark,
  220. captureCancelButton,
  221. navigationButtonsDiv,
  222. previewMirroring,
  223. closeButton,
  224. capture,
  225. reader;
  226. // Save call state for suspend/resume
  227. BarcodeReader.scanCallArgs = {
  228. success: success,
  229. fail: fail,
  230. args: args
  231. };
  232. function updatePreviewForRotation(evt) {
  233. if (!capture) {
  234. return;
  235. }
  236. var displayInformation = (evt && evt.target) || Windows.Graphics.Display.DisplayInformation.getForCurrentView();
  237. var currentOrientation = displayInformation.currentOrientation;
  238. previewMirroring = capture.getPreviewMirroring();
  239. // Lookup up the rotation degrees.
  240. var rotDegree = videoPreviewRotationLookup(currentOrientation, previewMirroring);
  241. capture.setPreviewRotation(degreesToRotation(rotDegree));
  242. return WinJS.Promise.as();
  243. }
  244. /**
  245. * Creates a preview frame and necessary objects
  246. */
  247. function createPreview() {
  248. // Create fullscreen preview
  249. var capturePreviewFrameStyle = document.createElement('link');
  250. capturePreviewFrameStyle.rel = "stylesheet";
  251. capturePreviewFrameStyle.type = "text/css";
  252. capturePreviewFrameStyle.href = urlutil.makeAbsolute("/www/css/plugin-barcodeScanner.css");
  253. document.head.appendChild(capturePreviewFrameStyle);
  254. capturePreviewFrame = document.createElement('div');
  255. capturePreviewFrame.className = "barcode-scanner-wrap";
  256. capturePreview = document.createElement("video");
  257. capturePreview.className = "barcode-scanner-preview";
  258. capturePreview.addEventListener('click', function () {
  259. focus();
  260. });
  261. capturePreviewAlignmentMark = document.createElement('div');
  262. capturePreviewAlignmentMark.className = "barcode-scanner-mark";
  263. navigationButtonsDiv = document.createElement("div");
  264. navigationButtonsDiv.className = "barcode-scanner-app-bar";
  265. navigationButtonsDiv.onclick = function (e) {
  266. e.cancelBubble = true;
  267. };
  268. closeButton = document.createElement("div");
  269. closeButton.innerText = "close";
  270. closeButton.className = "app-bar-action action-close";
  271. navigationButtonsDiv.appendChild(closeButton);
  272. BarcodeReader.scanCancelled = false;
  273. closeButton.addEventListener("click", cancelPreview, false);
  274. document.addEventListener('backbutton', cancelPreview, false);
  275. [capturePreview, capturePreviewAlignmentMark, navigationButtonsDiv].forEach(function (element) {
  276. capturePreviewFrame.appendChild(element);
  277. });
  278. }
  279. function focus(controller) {
  280. var result = WinJS.Promise.wrap();
  281. if (!capturePreview || capturePreview.paused) {
  282. // If the preview is not yet playing, there is no sense in running focus
  283. return result;
  284. }
  285. if (!controller) {
  286. try {
  287. controller = capture && capture.videoDeviceController;
  288. } catch (err) {
  289. console.log('Failed to access focus control for current camera: ' + err);
  290. return result;
  291. }
  292. }
  293. if (!controller.focusControl || !controller.focusControl.supported) {
  294. console.log('Focus control for current camera is not supported');
  295. return result;
  296. }
  297. // Multiple calls to focusAsync leads to internal focusing hang on some Windows Phone 8.1 devices
  298. // Also need to wrap in try/catch to avoid crash on Surface 3 - looks like focusState property
  299. // somehow is not accessible there. See https://github.com/phonegap/phonegap-plugin-barcodescanner/issues/288
  300. try {
  301. if (controller.focusControl.focusState === Windows.Media.Devices.MediaCaptureFocusState.searching) {
  302. return result;
  303. }
  304. } catch (e) {
  305. // Nothing to do - just continue w/ focusing
  306. }
  307. // The delay prevents focus hang on slow devices
  308. return WinJS.Promise.timeout(INITIAL_FOCUS_DELAY)
  309. .then(function () {
  310. try {
  311. return controller.focusControl.focusAsync().then(function () {
  312. return result;
  313. }, function (e) {
  314. // This happens on mutliple taps
  315. if (e.number !== OPERATION_IS_IN_PROGRESS) {
  316. console.error('focusAsync failed: ' + e);
  317. return WinJS.Promise.wrapError(e);
  318. }
  319. return result;
  320. });
  321. } catch (e) {
  322. // This happens on mutliple taps
  323. if (e.number !== OPERATION_IS_IN_PROGRESS) {
  324. console.error('focusAsync failed: ' + e);
  325. return WinJS.Promise.wrapError(e);
  326. }
  327. return result;
  328. }
  329. });
  330. }
  331. function setupFocus(focusControl) {
  332. function supportsFocusMode(mode) {
  333. return focusControl.supportedFocusModes.indexOf(mode).returnValue;
  334. }
  335. if (!focusControl || !focusControl.supported || !focusControl.configure) {
  336. return WinJS.Promise.wrap();
  337. }
  338. var FocusMode = Windows.Media.Devices.FocusMode;
  339. var focusConfig = new Windows.Media.Devices.FocusSettings();
  340. focusConfig.autoFocusRange = Windows.Media.Devices.AutoFocusRange.normal;
  341. // Determine a focus position if the focus search fails:
  342. focusConfig.disableDriverFallback = false;
  343. if (supportsFocusMode(FocusMode.continuous)) {
  344. console.log("Device supports continuous focus mode");
  345. focusConfig.mode = FocusMode.continuous;
  346. } else if (supportsFocusMode(FocusMode.auto)) {
  347. console.log("Device doesn\'t support continuous focus mode, switching to autofocus mode");
  348. focusConfig.mode = FocusMode.auto;
  349. }
  350. focusControl.configure(focusConfig);
  351. // Continuous focus should start only after preview has started. See 'Remarks' at
  352. // https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.focuscontrol.configure.aspx
  353. function waitForIsPlaying() {
  354. var isPlaying = !capturePreview.paused && !capturePreview.ended && capturePreview.readyState > 2;
  355. if (!isPlaying) {
  356. return WinJS.Promise.timeout(CHECK_PLAYING_TIMEOUT)
  357. .then(function () {
  358. return waitForIsPlaying();
  359. });
  360. }
  361. return focus();
  362. }
  363. return waitForIsPlaying();
  364. }
  365. function disableZoomAndScroll() {
  366. document.body.classList.add('no-zoom');
  367. document.body.classList.add('no-scroll');
  368. }
  369. function enableZoomAndScroll() {
  370. document.body.classList.remove('no-zoom');
  371. document.body.classList.remove('no-scroll');
  372. }
  373. /**
  374. * Starts stream transmission to preview frame and then run barcode search
  375. */
  376. function startPreview() {
  377. return findCamera()
  378. .then(function (id) {
  379. var captureSettings;
  380. try {
  381. captureSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
  382. } catch (e) {
  383. if (e.number === REGDB_E_CLASSNOTREG) {
  384. throw new Error('Ensure that you have Windows Media Player and Media Feature pack installed.');
  385. }
  386. throw e;
  387. }
  388. captureSettings.streamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.video;
  389. captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.videoPreview;
  390. captureSettings.videoDeviceId = id;
  391. capture = new Windows.Media.Capture.MediaCapture();
  392. return capture.initializeAsync(captureSettings);
  393. })
  394. .then(function () {
  395. var controller = capture.videoDeviceController;
  396. var deviceProps = controller.getAvailableMediaStreamProperties(Windows.Media.Capture.MediaStreamType.videoPreview);
  397. deviceProps = Array.prototype.slice.call(deviceProps);
  398. deviceProps = deviceProps.filter(function (prop) {
  399. // filter out streams with "unknown" subtype - causes errors on some devices
  400. return prop.subtype !== "Unknown";
  401. }).sort(function (propA, propB) {
  402. // sort properties by resolution
  403. return propB.width - propA.width;
  404. });
  405. var preferredProps = deviceProps.filter(function(prop){
  406. // Filter out props where frame size is between 640*480 and 1280*720
  407. return prop.width >= 640 && prop.height >= 480 && prop.width <= 1280 && prop.height <= 720;
  408. });
  409. // prefer video frame size between between 640*480 and 1280*720
  410. // use maximum resolution otherwise
  411. var maxResProps = preferredProps[0] || deviceProps[0];
  412. return controller.setMediaStreamPropertiesAsync(Windows.Media.Capture.MediaStreamType.videoPreview, maxResProps)
  413. .then(function () {
  414. return {
  415. capture: capture,
  416. width: maxResProps.width,
  417. height: maxResProps.height
  418. };
  419. });
  420. })
  421. .then(function (captureSettings) {
  422. capturePreview.msZoom = true;
  423. capturePreview.src = URL.createObjectURL(capture);
  424. capturePreview.play();
  425. // Insert preview frame and controls into page
  426. document.body.appendChild(capturePreviewFrame);
  427. disableZoomAndScroll();
  428. return setupFocus(captureSettings.capture.videoDeviceController.focusControl)
  429. .then(function () {
  430. Windows.Graphics.Display.DisplayInformation.getForCurrentView().addEventListener("orientationchanged", updatePreviewForRotation, false);
  431. return updatePreviewForRotation();
  432. })
  433. .then(function () {
  434. if (!Windows.Media.Devices.CameraStreamState) {
  435. // CameraStreamState is available starting with Windows 10 so skip this check for 8.1
  436. // https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.camerastreamstate
  437. return WinJS.Promise.as();
  438. }
  439. function checkCameraStreamState() {
  440. if (capture.cameraStreamState !== Windows.Media.Devices.CameraStreamState.streaming) {
  441. // Using loop as MediaCapture.CameraStreamStateChanged does not fire with CameraStreamState.streaming state.
  442. return WinJS.Promise.timeout(CAMERA_STREAM_STATE_CHECK_RETRY_TIMEOUT)
  443. .then(function () {
  444. return checkCameraStreamState();
  445. });
  446. }
  447. return WinJS.Promise.as();
  448. }
  449. // Ensure CameraStreamState is Streaming
  450. return checkCameraStreamState();
  451. })
  452. .then(function () {
  453. return captureSettings;
  454. });
  455. });
  456. }
  457. /**
  458. * Removes preview frame and corresponding objects from window
  459. */
  460. function destroyPreview() {
  461. var promise = WinJS.Promise.as();
  462. Windows.Graphics.Display.DisplayInformation.getForCurrentView().removeEventListener("orientationchanged", updatePreviewForRotation, false);
  463. document.removeEventListener('backbutton', cancelPreview);
  464. if (capturePreview) {
  465. var isPlaying = !capturePreview.paused && !capturePreview.ended && capturePreview.readyState > 2;
  466. if (isPlaying) {
  467. capturePreview.pause();
  468. }
  469. // http://stackoverflow.com/a/28060352/4177762
  470. capturePreview.src = "";
  471. if (capturePreview.load) {
  472. capturePreview.load();
  473. }
  474. }
  475. if (capturePreviewFrame) {
  476. try {
  477. document.body.removeChild(capturePreviewFrame);
  478. } catch (e) {
  479. // Catching NotFoundError
  480. console.error(e);
  481. }
  482. }
  483. capturePreviewFrame = null;
  484. reader && reader.stop();
  485. reader = null;
  486. if (capture) {
  487. try {
  488. promise = capture.stopRecordAsync();
  489. } catch (e) {
  490. // Catching NotFoundError
  491. console.error(e);
  492. }
  493. }
  494. capture = null;
  495. enableZoomAndScroll();
  496. return promise;
  497. }
  498. /**
  499. * Stops preview and then call success callback with cancelled=true
  500. * See https://github.com/phonegap-build/BarcodeScanner#using-the-plugin
  501. */
  502. function cancelPreview() {
  503. BarcodeReader.scanCancelled = true;
  504. reader && reader.stop();
  505. }
  506. function checkCancelled() {
  507. if (BarcodeReader.scanCancelled || BarcodeReader.suspended) {
  508. throw new Error('Canceled');
  509. }
  510. }
  511. // Timeout is needed so that the .done finalizer below can be attached to the promise.
  512. BarcodeReader.scanPromise = WinJS.Promise.timeout()
  513. .then(function() {
  514. createPreview();
  515. checkCancelled();
  516. return startPreview();
  517. })
  518. .then(function (captureSettings) {
  519. checkCancelled();
  520. reader = BarcodeReader.get(captureSettings.capture);
  521. reader.init(captureSettings.capture, captureSettings.width, captureSettings.height);
  522. // Add a small timeout before capturing first frame otherwise
  523. // we would get an 'Invalid state' error from 'getPreviewFrameAsync'
  524. return WinJS.Promise.timeout(200)
  525. .then(function () {
  526. checkCancelled();
  527. return reader.readCode();
  528. });
  529. })
  530. .then(function (result) {
  531. // Suppress null result (cancel) on suspending
  532. if (BarcodeReader.suspended) {
  533. return;
  534. }
  535. destroyPreview();
  536. success({
  537. text: result && result.text,
  538. format: result && BARCODE_FORMAT[result.barcodeFormat],
  539. cancelled: !result
  540. });
  541. });
  542. // Catching any errors here
  543. BarcodeReader.scanPromise.done(function () { }, function (error) {
  544. // Suppress null result (cancel) on suspending
  545. if (BarcodeReader.suspended) {
  546. return;
  547. }
  548. destroyPreview();
  549. if (error.message == 'Canceled') {
  550. success({
  551. cancelled: true
  552. });
  553. } else {
  554. fail(error);
  555. }
  556. });
  557. BarcodeReader.videoPreviewIsVisible = function () {
  558. return capturePreviewFrame !== null;
  559. }
  560. BarcodeReader.destroyPreview = destroyPreview;
  561. },
  562. /**
  563. * Encodes specified data into barcode
  564. * @param {function} success Success callback
  565. * @param {function} fail Error callback
  566. * @param {array} args Arguments array
  567. */
  568. encode: function (success, fail, args) {
  569. fail("Not implemented yet");
  570. }
  571. };
  572. var app = WinJS.Application;
  573. function waitForScanEnd() {
  574. return BarcodeReader.scanPromise || WinJS.Promise.as();
  575. }
  576. function suspend(args) {
  577. BarcodeReader.suspended = true;
  578. if (args) {
  579. args.setPromise(BarcodeReader.destroyPreview()
  580. .then(waitForScanEnd, waitForScanEnd));
  581. } else {
  582. BarcodeReader.destroyPreview();
  583. }
  584. }
  585. function resume() {
  586. BarcodeReader.suspended = false;
  587. module.exports.scan(BarcodeReader.scanCallArgs.success, BarcodeReader.scanCallArgs.fail, BarcodeReader.scanCallArgs.args);
  588. }
  589. function onVisibilityChanged() {
  590. if (document.visibilityState === 'hidden'
  591. && BarcodeReader.videoPreviewIsVisible && BarcodeReader.videoPreviewIsVisible() && BarcodeReader.destroyPreview) {
  592. suspend();
  593. } else if (BarcodeReader.suspended) {
  594. resume();
  595. }
  596. }
  597. // Windows 8.1 projects
  598. document.addEventListener('msvisibilitychange', onVisibilityChanged);
  599. // Windows 10 projects
  600. document.addEventListener('visibilitychange', onVisibilityChanged);
  601. // About to be suspended
  602. app.addEventListener('checkpoint', function (args) {
  603. if (BarcodeReader.videoPreviewIsVisible && BarcodeReader.videoPreviewIsVisible() && BarcodeReader.destroyPreview) {
  604. suspend(args);
  605. }
  606. });
  607. // Resuming from a user suspension
  608. Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function () {
  609. if (BarcodeReader.suspended) {
  610. resume();
  611. }
  612. }, false);
  613. require("cordova/exec/proxy").add("BarcodeScanner", module.exports);