From f31fc68169c098dacec9ded0bfc393c267d51911 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Hoelt Date: Thu, 30 Oct 2014 23:50:49 +0200 Subject: [PATCH] Add ESLint + Fix raised issues --- .eslintrc | 18 + .jshintrc | 9 + Makefile | 12 +- doc/api.md | 16 +- package.json | 1 + src/js/constants.js | 2 +- src/js/error-callbacks.js | 4 +- src/js/error.js | 4 +- src/js/get.js | 2 +- src/js/log.js | 2 +- src/js/off.js | 3 +- src/js/once.js | 4 +- src/js/order.js | 9 +- src/js/platforms/android-adapter.js | 14 +- src/js/platforms/android-bridge.js | 52 +-- src/js/platforms/ios-adapter.js | 45 +- src/js/platforms/ios-bridge.js | 52 ++- src/js/product-internal.js | 2 +- src/js/product.js | 26 +- src/js/products.js | 2 +- src/js/queries.js | 16 +- src/js/ready.js | 2 +- src/js/refresh.js | 6 +- src/js/register.js | 7 +- src/js/store-ios.js | 2 +- src/js/store.js | 36 +- src/js/trigger.js | 2 +- src/js/utils.js | 8 +- src/js/validator.js | 10 +- src/js/when.js | 14 +- test/js/helper.js | 36 +- test/js/run.js | 47 +- test/js/test-android.js | 8 +- test/js/test-error.js | 3 + test/js/test-finish.js | 5 + test/js/test-ios.js | 7 +- test/js/test-off.js | 4 +- test/js/test-order.js | 3 + test/js/test-queries.js | 55 ++- test/js/test-ready.js | 3 + test/js/test-register.js | 8 +- test/js/test-utils.js | 23 +- test/js/test-verify.js | 21 +- test/js/test-when.js | 30 +- www/store-android.js | 116 ++--- www/store-ios.js | 655 ++++++++++++++-------------- 46 files changed, 754 insertions(+), 652 deletions(-) create mode 100644 .eslintrc create mode 100644 .jshintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..6b3a2d2f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,18 @@ +{ + "env" : { + "node" : true, + "browser" : true + }, + "rules" : { + "strict": 1, + "quotes": 0, + "no-underscore-dangle": 0, + "no-multi-spaces": 0, + "key-spacing": 0, + "curly": 0, + "no-use-before-define": [ 1, "nofunc" ] + }, + "globals" : { + "store": true + } +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..c18c257f --- /dev/null +++ b/.jshintrc @@ -0,0 +1,9 @@ +{ + "node": true, + "browser": true, + "undef": true, + "globals": { + "store": true, + "cordova": true + } +} diff --git a/Makefile b/Makefile index dc4b3640..19894f59 100644 --- a/Makefile +++ b/Makefile @@ -33,15 +33,19 @@ prepare-test-js: @cp src/js/platforms/*-adapter.js test/tmp/ @#node_modules/.bin/istanbul instrument --no-compact --output test/tmp/store-test.js test/store-test-src.js -jshint: check-jshint +jshint: check-jshint sync-android @echo "- JSHint" - @node_modules/.bin/jshint src/js/*.js test/js/*.js + @node_modules/.bin/jshint --config .jshintrc src/js/*.js src/js/platforms/*.js test/js/*.js -test-js: jshint prepare-test-js +eslint: jshint + @echo "- ESLint" + @node_modules/.bin/eslint --config .eslintrc src/js/*.js src/js/platforms/*.js test/js/*.js + +test-js: jshint eslint prepare-test-js @echo "- Mocha" @node_modules/.bin/istanbul test --root test/tmp test/js/run.js -test-js-coverage: jshint prepare-test-js +test-js-coverage: jshint eslint prepare-test-js @echo "- Mocha / Instanbul" @node_modules/.bin/istanbul cover --root test/ test/js/run.js @node_modules/.bin/coveralls < coverage/lcov.info diff --git a/doc/api.md b/doc/api.md index 6f22a24e..1969c9a3 100644 --- a/doc/api.md +++ b/doc/api.md @@ -61,7 +61,7 @@ be contacted to load informations about the registered products: human readable `title` and `description`, `price`, etc. This isn't an optional step as some despotic store owners (like Apple) require you -to display information about a product as retrieved from their server: no +to display information about a product as retrieved from their server: no hard-coding of price and title allowed! This is also convenient for you as you can change the price of your items knowing that it'll be reflected instantly on your clients' devices. @@ -80,9 +80,9 @@ Let's demonstrate this with an example: render(); store.when("cc.fovea.test1").updated(render); } - + function render() { - + // Get the product from the pool. var product = store.get("cc.fovea.test1"); @@ -102,13 +102,13 @@ Let's demonstrate this with an example: + "
" + product.description + "
" + "
" + product.price + "
" ); - + // Is this product owned? Give him a special class. if (product.owned) $el.addClass("owned"); else $el.removeClass("owned"); - + // Is an order for this product in progress? Can't be ordered right now? if (product.canPurchase) $el.addClass("can-purchase"); @@ -116,7 +116,7 @@ Let's demonstrate this with an example: $el.removeClass("can-purchase"); } } - + // method called when the view is hidden function hide() { // stop monitoring the product @@ -387,7 +387,7 @@ A Promise with the following methods: - `error(function(err){})` - validation failed, either because of expiry or communication failure. - - `err` is a [store.Error object](#errors), with a code expected to be + - `err` is a [store.Error object](#errors), with a code expected to be `store.ERR_PAYMENT_EXPIRED` or `store.ERR_VERIFICATION_FAILED`. @@ -904,7 +904,7 @@ Only supports JSON requests. Options: -* `url`: +* `url`: * `method`: HTTP method to use (GET, POST, ...) * `success`: callback(data) * `error`: callback(statusCode, statusText) diff --git a/package.json b/package.json index d1a6556b..fe74f359 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "homepage": "https://github.com/j3k0/PhoneGap-InAppPurchase-iOS", "devDependencies": { "cordova": "^3.6.3-0.2.13", + "eslint": "^0.9.1", "istanbul": "^0.3.2", "jshint": "^2.5.6", "mocha": "^1.21.4", diff --git a/src/js/constants.js b/src/js/constants.js index 5ba39b33..789b41cd 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -69,4 +69,4 @@ var ERROR_CODES_BASE = 6777000; /*///*/ store.CONNECTION_FAILED = 6778002; /*///*/ store.PURCHASE_EXPIRED = 6778003; -}).call(this); +})(); diff --git a/src/js/error-callbacks.js b/src/js/error-callbacks.js index bcc478ab..801c4e70 100644 --- a/src/js/error-callbacks.js +++ b/src/js/error-callbacks.js @@ -1,7 +1,7 @@ (function(){ 'use strict'; -/// +/// /// ## *store.error.callbacks* array /// /// Array of user registered error callbacks. @@ -47,4 +47,4 @@ function deferThrow(err) { setTimeout(function() { throw err; }, 1); } -}).call(this); +})(); diff --git a/src/js/error.js b/src/js/error.js index 49680cbe..312e7ff0 100644 --- a/src/js/error.js +++ b/src/js/error.js @@ -3,7 +3,7 @@ /// /// ## *store.Error* object -/// +/// /// All error callbacks takes an `error` object as parameter. store.Error = function(options) { @@ -80,4 +80,4 @@ store.error.unregister = function(cb) { store.error.callbacks.unregister(cb); }; -}).call(this); +})(); diff --git a/src/js/get.js b/src/js/get.js index 0abffa83..de6b9bfa 100644 --- a/src/js/get.js +++ b/src/js/get.js @@ -15,4 +15,4 @@ store.get = function(id) { return product; }; -}).call(this); +})(); diff --git a/src/js/log.js b/src/js/log.js index 98f7dc62..fab33d7d 100644 --- a/src/js/log.js +++ b/src/js/log.js @@ -41,4 +41,4 @@ store.log = { debug: function(o) { log(store.DEBUG, o); } }; -}).call(this); +})(); diff --git a/src/js/off.js b/src/js/off.js index bbdbd138..eb06eec9 100644 --- a/src/js/off.js +++ b/src/js/off.js @@ -34,5 +34,4 @@ store.off = function(callback) { store.error.unregister(callback); }; -}).call(this); - +})(); diff --git a/src/js/once.js b/src/js/once.js index 2d615bb4..49bc0658 100644 --- a/src/js/once.js +++ b/src/js/once.js @@ -2,7 +2,7 @@ "use strict"; /// ## *store.once(query)* -/// +/// /// Identical to [`store.when`](#when), but the callback will be called only once. /// After being called, the callback will be unregistered. store.once = function(query, action, callback) { @@ -25,4 +25,4 @@ store.once = function(query, action, callback) { store.once.unregister = store.when.unregister; -}).call(this); +})(); diff --git a/src/js/order.js b/src/js/order.js index 9ac59764..6c56d44b 100644 --- a/src/js/order.js +++ b/src/js/order.js @@ -13,7 +13,7 @@ var callbackId = 0; /// Initiate the purchase of a product. /// /// The `product` argument can be either: -/// +/// /// - the `store.Product` object /// - the product `id` /// - the product `alias` @@ -23,9 +23,8 @@ var callbackId = 0; /// store.order = function(pid) { - var that = this; var p = pid; - + if (typeof pid === "string") { p = store.products.byId[pid] || store.products.byAlias[pid]; if (!p) { @@ -75,7 +74,7 @@ store.order = function(pid) { if (!localCallback.error) return; done(); - cb(p); + cb(err); }); return this; } @@ -97,4 +96,4 @@ store.order.unregister = function(cb) { } }; -}).call(this); +})(); diff --git a/src/js/platforms/android-adapter.js b/src/js/platforms/android-adapter.js index 3487752d..5fea6f25 100644 --- a/src/js/platforms/android-adapter.js +++ b/src/js/platforms/android-adapter.js @@ -1,6 +1,9 @@ (function() { "use strict"; +var initialized = false; +var skus = []; + store.when("refreshed", function() { if (!initialized) init(); }); @@ -9,10 +12,7 @@ store.when("re-refreshed", function() { iabGetPurchases(); }); -var initialized = false; -var skus = []; - -var init = function () { +function init() { if (initialized) return; initialized = true; @@ -30,7 +30,7 @@ var init = function () { showLog: store.verbosity >= store.DEBUG ? true : false }, skus); -}; +} function iabReady() { store.log.debug("android -> ready"); @@ -225,7 +225,7 @@ store.when("product", "finished", function(product) { function(err, code) { // error // can't finish. store.error({ - code: code || ERR_UNKNOWN, + code: code || store.ERR_UNKNOWN, message: err }); }, @@ -236,4 +236,4 @@ store.when("product", "finished", function(product) { } }); -}).call(this); +})(); diff --git a/src/js/platforms/android-bridge.js b/src/js/platforms/android-bridge.js index f010fdcf..1d18d4aa 100644 --- a/src/js/platforms/android-bridge.js +++ b/src/js/platforms/android-bridge.js @@ -2,6 +2,7 @@ * Copyright (C) 2012-2013 by Guillaume Charhon * Modifications 10/16/2013 by Brian Thurlow */ +/*global cordova */ (function() { "use strict"; @@ -21,38 +22,39 @@ InAppBilling.prototype.init = function (success, fail, options, skus) { this.options = { showLog: options.showLog !== false }; - + if (this.options.showLog) { log('setup ok'); } - + var hasSKUs = false; //Optional Load SKUs to Inventory. if(typeof skus !== "undefined"){ if (typeof skus === "string") { - skus = [skus]; - } - if (skus.length > 0) { - if (typeof skus[0] !== 'string') { - var msg = 'invalid productIds: ' + JSON.stringify(skus); - if (this.options.showLog) { - log(msg); - } + skus = [skus]; + } + if (skus.length > 0) { + if (typeof skus[0] !== 'string') { + var msg = 'invalid productIds: ' + JSON.stringify(skus); + if (this.options.showLog) { + log(msg); + } fail(msg, store.ERR_INVALID_PRODUCT_ID); - return; - } - if (this.options.showLog) { - log('load ' + JSON.stringify(skus)); - } + return; + } + if (this.options.showLog) { + log('load ' + JSON.stringify(skus)); + } hasSKUs = true; - } + } } - - if(hasSKUs){ - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", [skus]); - }else { + + if (hasSKUs) { + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", [skus]); + } + else { //No SKUs - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", []); + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", []); } }; InAppBilling.prototype.getPurchases = function (success, fail) { @@ -89,7 +91,7 @@ InAppBilling.prototype.getProductDetails = function (success, fail, skus) { if (this.options.showLog) { log('getProductDetails called!'); } - + if (typeof skus === "string") { skus = [skus]; } @@ -104,9 +106,9 @@ InAppBilling.prototype.getProductDetails = function (success, fail, skus) { return; } if (this.options.showLog) { - log('load ' + JSON.stringify(skus)); + log('load ' + JSON.stringify(skus)); } - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getProductDetails", [skus]); + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getProductDetails", [skus]); } }; @@ -140,4 +142,4 @@ window.inappbilling = new InAppBilling(); try { store.android = window.inappbilling; } catch (e) {} -}).call(this); +})(); diff --git a/src/js/platforms/ios-adapter.js b/src/js/platforms/ios-adapter.js index cae75ea6..630782d3 100644 --- a/src/js/platforms/ios-adapter.js +++ b/src/js/platforms/ios-adapter.js @@ -1,3 +1,4 @@ +/*global storekit */ (function() { "use strict"; @@ -119,7 +120,7 @@ store.when("expired", function(product) { //! var initialized = false; var initializing = false; -var storekitInit = function () { +function storekitInit() { if (initialized || initializing) return; initializing = true; store.log.debug("ios -> initializing storekit"); @@ -133,7 +134,7 @@ var storekitInit = function () { restoreCompleted: storekitRestoreCompleted, restoreFailed: storekitRestoreFailed }, storekitReady, storekitInitFailed); -}; +} //! //! ## *storekit* events handlers @@ -145,22 +146,22 @@ var storekitInit = function () { //! //! Loads all registered products, triggers `storekitLoaded()` when done. //! -var storekitReady = function () { +function storekitReady() { store.log.info("ios -> storekit ready"); initializing = false; initialized = true; storekitLoad(); -}; +} -var storekitInitFailed = function() { +function storekitInitFailed() { store.log.warn("ios -> storekit init failed"); initializing = false; retry(storekitInit); -}; +} var loaded = false; var loading = false; -var storekitLoad = function() { +function storekitLoad() { if (!initialized) return; if (loaded || loading) return; loading = true; @@ -169,7 +170,7 @@ var storekitLoad = function() { products.push(store.products[i].id); store.log.debug("ios -> loading products"); storekit.load(products, storekitLoaded, storekitLoadFailed); -}; +} //! ### *storekitLoaded()* //! @@ -180,7 +181,7 @@ var storekitLoad = function() { //! 3. Set the products state to `OWNED` (if it is so) //! 4. Set the store status to "ready". //! -var storekitLoaded = function (validProducts, invalidProductIds) { +function storekitLoaded(validProducts, invalidProductIds) { store.log.debug("ios -> products loaded"); var p; for (var i = 0; i < validProducts.length; ++i) { @@ -215,13 +216,13 @@ var storekitLoaded = function (validProducts, invalidProductIds) { storekit.loaded = true; store.ready(true); }, 1); -}; +} -var storekitLoadFailed = function() { +function storekitLoadFailed() { store.log.warn("ios -> loading products failed"); loading = false; retry(storekitLoad); -}; +} //! ### *storekitPurchasing()* //! @@ -229,7 +230,7 @@ var storekitLoadFailed = function() { //! //! It will set the product state to `INITIATED`. //! -var storekitPurchasing = function (productId) { +function storekitPurchasing(productId) { store.log.debug("ios -> is purchasing " + productId); store.ready(function() { var product = store.get(productId); @@ -240,7 +241,7 @@ var storekitPurchasing = function (productId) { if (product.state !== store.INITIATED) product.set("state", store.INITIATED); }); -}; +} //! ### *storekitPurchased()* //! @@ -249,7 +250,7 @@ var storekitPurchasing = function (productId) { //! It will set the product state to `APPROVED` and associates the product //! with the order's transaction identifier. //! -var storekitPurchased = function (transactionId, productId) { +function storekitPurchased(transactionId, productId) { store.ready(function() { var product = store.get(productId); if (!product) { @@ -279,7 +280,7 @@ var storekitPurchased = function (transactionId, productId) { store.log.info("ios -> transaction " + transactionId + " purchased (" + product.transactions.length + " in the queue for " + productId + ")"); product.set("state", store.APPROVED); }); -}; +} //! ### *storekitError()* //! @@ -287,9 +288,9 @@ var storekitPurchased = function (transactionId, productId) { //! //! Will convert storekit errors to a [`store.Error`](api.md/#errors). //! -var storekitError = function(errorCode, errorText, options) { +function storekitError(errorCode, errorText, options) { - var i,p; + var i, p; if (!options) options = {}; @@ -328,7 +329,7 @@ var storekitError = function(errorCode, errorText, options) { code: errorCode, message: errorText }); -}; +} // Restore purchases. // store.restore = function() { @@ -346,7 +347,7 @@ function storekitRestoreCompleted() { store.log.info("ios -> restore completed"); } -function storekitRestoreFailed(errorCode) { +function storekitRestoreFailed(/*errorCode*/) { store.log.warn("ios -> restore failed"); store.error({ code: store.ERR_REFRESH, @@ -369,7 +370,7 @@ store._prepareForValidation = function(product, callback) { }); }; -//! +//! //! ## Persistance of the *OWNED* status //! @@ -423,4 +424,4 @@ document.addEventListener("online", function() { } }, false); -}).call(this); +})(); diff --git a/src/js/platforms/ios-bridge.js b/src/js/platforms/ios-bridge.js index 4bf0ecf6..ea286dc6 100644 --- a/src/js/platforms/ios-bridge.js +++ b/src/js/platforms/ios-bridge.js @@ -1,4 +1,4 @@ -/** +/** * A plugin to enable iOS In-App Purchases. * * Copyright (c) Matt Kane 2011 @@ -6,14 +6,21 @@ * Copyright (c) Jean-Christophe Hoelt 2013 */ +/*eslint camelcase:0 */ +/*global cordova, window */ +(function(){ +"use strict"; + var exec = function (methodName, options, success, error) { cordova.exec(success, error, "InAppPurchase", methodName, options); }; var protectCall = function (callback, context) { - if (!callback) return; + if (!callback) { + return; + } try { - var args = Array.prototype.slice.call(arguments, 2); + var args = Array.prototype.slice.call(arguments, 2); callback.apply(this, args); } catch (err) { @@ -99,13 +106,13 @@ InAppPurchase.prototype.init = function (options, success, error) { }; /** - * Makes an in-app purchase. - * + * Makes an in-app purchase. + * * @param {String} productId The product identifier. e.g. "com.example.MyApp.myproduct" - * @param {int} quantity + * @param {int} quantity */ InAppPurchase.prototype.purchase = function (productId, quantity) { - quantity = (quantity|0) || 1; + quantity = (quantity | 0) || 1; var options = this.options; // Many people forget to load information about their products from apple's servers before allowing @@ -127,23 +134,23 @@ InAppPurchase.prototype.purchase = function (productId, quantity) { } }; var purchaseFailed = function () { - var msg = 'Purchasing ' + productId + ' failed'; - log(msg); + var errmsg = 'Purchasing ' + productId + ' failed'; + log(errmsg); if (typeof options.error === 'function') { - protectCall(options.error, 'options.error', InAppPurchase.prototype.ERR_PURCHASE, msg, productId, quantity); + protectCall(options.error, 'options.error', InAppPurchase.prototype.ERR_PURCHASE, errmsg, productId, quantity); } }; - return exec('purchase', [productId, quantity], purchaseOk, purchaseFailed); + exec('purchase', [productId, quantity], purchaseOk, purchaseFailed); }; /** * Asks the payment queue to restore previously completed purchases. * The restored transactions are passed to the onRestored callback, so make sure you define a handler for that first. - * + * */ InAppPurchase.prototype.restore = function() { this.needRestoreNotification = true; - return exec('restoreCompletedTransactions', []); + exec('restoreCompletedTransactions', []); }; /** @@ -233,13 +240,13 @@ InAppPurchase.prototype.processPendingUpdates = function() { }; // This is called from native. -// +// // Note that it may eventually be called before initialization... unfortunately. // In this case, we'll just keep pending updates in a list for later processing. InAppPurchase.prototype.updatedTransactionCallback = function (state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt) { if (!initialized) { - var args = Array.prototype.slice.call(arguments); + var args = Array.prototype.slice.call(arguments); pendingUpdates.push(args); return; } @@ -258,7 +265,7 @@ InAppPurchase.prototype.updatedTransactionCallback = function (state, errorCode, return; case "PaymentTransactionStatePurchased": protectCall(this.options.purchase, 'options.purchase', transactionIdentifier, productId); - return; + return; case "PaymentTransactionStateFailed": protectCall(this.options.error, 'options.error', errorCode, errorText, { productId: productId @@ -300,7 +307,7 @@ InAppPurchase.prototype.refreshReceipts = function() { var error = function(errMessage) { log('refresh receipt failed: ' + errMessage); - protectcall(options.error, 'options.error', InAppPurchase.prototype.ERR_REFRESH_RECEIPTS, 'Failed to refresh receipt: ' + errMessage); + protectCall(that.options.error, 'options.error', InAppPurchase.prototype.ERR_REFRESH_RECEIPTS, 'Failed to refresh receipt: ' + errMessage); }; exec('appStoreRefreshReceipt', [], loaded, error); @@ -318,10 +325,10 @@ InAppPurchase.prototype.loadReceipts = function (callback) { var error = function (errMessage) { log('load failed: ' + errMessage); - protectCall(options.error, 'options.error', InAppPurchase.prototype.ERR_LOAD_RECEIPTS, 'Failed to load receipt: ' + errMessage); + protectCall(that.options.error, 'options.error', InAppPurchase.prototype.ERR_LOAD_RECEIPTS, 'Failed to load receipt: ' + errMessage); }; - var callCallback = function () { + function callCallback() { if (callback) { protectCall(callback, 'loadReceipts.callback', { appStoreReceipt: that.appStoreReceipt, @@ -333,13 +340,13 @@ InAppPurchase.prototype.loadReceipts = function (callback) { } }); } - }; + } exec('appStoreReceipt', [], loaded, error); }; /* - * This queue stuff is here because we may be sent events before listeners have been registered. This is because if we have + * This queue stuff is here because we may be sent events before listeners have been registered. This is because if we have * incomplete transactions when we quit, the app will try to run these when we resume. If we don't register to receive these * right away then they may be missed. As soon as a callback has been registered then it will be sent any events waiting * in the queue. @@ -357,7 +364,7 @@ InAppPurchase.prototype.runQueue = function () { this.updatedTransactionCallback.apply(this, args); args = queue.shift(); } - if (!this.eventQueue.length) { + if (!this.eventQueue.length) { this.unWatchQueue(); } }; @@ -382,3 +389,4 @@ InAppPurchase.prototype.eventQueue = []; InAppPurchase.prototype.timer = null; window.storekit = new InAppPurchase(); +})(); diff --git a/src/js/product-internal.js b/src/js/product-internal.js index e0c91f7a..4b247b66 100644 --- a/src/js/product-internal.js +++ b/src/js/product-internal.js @@ -49,4 +49,4 @@ store.Product.prototype.trigger = function(action, args) { store.trigger(this, action, args); }; -}).call(this); +})(); diff --git a/src/js/product.js b/src/js/product.js index 9b4900e8..556f42de 100644 --- a/src/js/product.js +++ b/src/js/product.js @@ -1,8 +1,15 @@ (function() { 'use strict'; +function defer(thisArg, cb, delay) { + setTimeout(function() { + cb.call(thisArg); + }, delay || 1); +} +var delay = defer; + /// ## *store.Product* object ## -/// +/// /// Most events methods give you access to a `product` object. store.Product = function(options) { @@ -132,7 +139,7 @@ store.Product.prototype.verify = function() { var tryValidation = function() { // No need to verifiy a which status isn't approved - // It means it already has been + // It means it already has been if (that.state !== store.APPROVED) return; @@ -215,7 +222,7 @@ store.Product.prototype.verify = function() { /// - `error(function(err){})` /// - validation failed, either because of expiry or communication /// failure. - /// - `err` is a [store.Error object](#errors), with a code expected to be + /// - `err` is a [store.Error object](#errors), with a code expected to be /// `store.ERR_PAYMENT_EXPIRED` or `store.ERR_VERIFICATION_FAILED`. error: function(cb) { errorCb = cb; return this; } }; @@ -224,14 +231,7 @@ store.Product.prototype.verify = function() { return ret; }; -var defer = function(thisArg, cb, delay) { - setTimeout(function() { - cb.call(thisArg); - }, delay || 1); -}; -var delay = defer; - -/// +/// /// ### life-cycle /// /// A product will change state during the application execution. @@ -268,8 +268,8 @@ var delay = defer; /// #### state changes /// /// Each time the product changes state, appropriate events is triggered. -/// +/// /// Learn more about events [here](#events) and about listening to events [here](#when). /// -}).call(this); +})(); diff --git a/src/js/products.js b/src/js/products.js index a997a316..063c2e46 100644 --- a/src/js/products.js +++ b/src/js/products.js @@ -45,4 +45,4 @@ store.products.reset = function() { this.byId = {}; }; -}).call(this); +})(); diff --git a/src/js/queries.js b/src/js/queries.js index 62e55d02..74713230 100644 --- a/src/js/queries.js +++ b/src/js/queries.js @@ -42,7 +42,7 @@ store._queries = { callbacks: { /// #### *store._queries.callbacks.byQuery* dictionary /// Dictionary of: - /// + /// /// - *key*: a string equals to `query + " " + action` /// - *value*: array of callbacks /// @@ -55,7 +55,7 @@ store._queries = { /// #### *store._queries.callbacks.add(query, action, callback, once)* /// Simplify the query with `uniqueQuery()`, then add it to the dictionary. - /// + /// /// `action` is concatenated to the `query` string to create the key. add: function(query, action, cb, once) { var fullQuery = store._queries.uniqueQuery(query ? query + " " + action : action); @@ -112,12 +112,12 @@ store._queries = { /// The method generates all possible queries for the given `product` and `action`. var queries = []; - /// + /// /// - product.id + " " + action if (product && product.id) queries.push(product.id + " " + action); /// - product.alias + " " + action - if (product && product.alias && product.alias != product.id) + if (product && product.alias && product.alias !== product.id) queries.push(product.alias + " " + action); /// - product.type + " " + action if (product && product.type) @@ -133,7 +133,7 @@ store._queries = { queries.push("invalid " + action); /// - action queries.push(action); - + /// /// Then, for each query: /// @@ -162,14 +162,14 @@ store._queries = { store._queries.callbacks.byQuery[q] = cbs.filter(isNotOnce); } } - + /// /// **Note**: All events also trigger the `updated` event if (action !== "updated" && action !== 'error') this.triggerWhenProduct(product, "updated", [ product ]); } /// - + }; // isNotOnce return true iff a callback should be called more than once. @@ -181,4 +181,4 @@ function deferThrow(err) { setTimeout(function() { throw err; }, 1); } -}).call(this); +})(); diff --git a/src/js/ready.js b/src/js/ready.js index 43b50143..bdab3b2e 100644 --- a/src/js/ready.js +++ b/src/js/ready.js @@ -56,4 +56,4 @@ store.ready.reset = function() { callbacks = []; }; -}).call(this); +})(); diff --git a/src/js/refresh.js b/src/js/refresh.js index fe2851ab..539c4bce 100644 --- a/src/js/refresh.js +++ b/src/js/refresh.js @@ -48,7 +48,7 @@ store.refresh = function() { store.log.debug(" in state '" + p.state + "'"); // resend the "approved" event to all approved purchases. - // give user a chance to try delivering the content again and + // give user a chance to try delivering the content again and // finish the purchase. if (p.state === store.APPROVED) p.trigger(store.APPROVED); @@ -59,9 +59,9 @@ store.refresh = function() { else if (p.state === store.OWNED && (p.type === store.FREE_SUBSCRIPTION || p.type === store.PAID_SUBSCRIPTION)) p.set("state", store.APPROVED); } - + store.trigger("re-refreshed"); }; -}).call(this); +})(); diff --git a/src/js/register.js b/src/js/register.js index 35b1cb6e..973d32f7 100644 --- a/src/js/register.js +++ b/src/js/register.js @@ -18,8 +18,9 @@ store.register = function(product) { if (!product) return; if (!product.length) - return store.register([product]); - registerProducts(product); + store.register([product]); + else + registerProducts(product); }; /// ##### example usage @@ -95,4 +96,4 @@ function hasKeyword(string) { return false; } -}).call(this); +})(); diff --git a/src/js/store-ios.js b/src/js/store-ios.js index a8942c76..284efa12 100644 --- a/src/js/store-ios.js +++ b/src/js/store-ios.js @@ -1,5 +1,5 @@ //! # iOS Implementation -//! +//! //! The implementation of the unified API is a small layer //! built on top of the legacy "PhoneGap-InAppPurchase-iOS" plugin. //! diff --git a/src/js/store.js b/src/js/store.js index 57b3436f..a6dd4fbb 100644 --- a/src/js/store.js +++ b/src/js/store.js @@ -18,12 +18,12 @@ /// /// This event provides a generic way to track the statuses of your purchases, /// to unlock features when needed and to refresh your views accordingly. -/// +/// /// ### Registering products /// /// The store needs to know the type and identifiers of your products before you /// can use them in your code. -/// +/// /// Use [`store.register()`](#register) before your first call to /// [`store.refresh()`](#refresh). /// @@ -57,7 +57,7 @@ /// readable `title` and `description`, `price`, etc. /// /// This isn't an optional step as some despotic store owners (like Apple) require you -/// to display information about a product as retrieved from their server: no +/// to display information about a product as retrieved from their server: no /// hard-coding of price and title allowed! This is also convenient for you /// as you can change the price of your items knowing that it'll be reflected instantly /// on your clients' devices. @@ -76,9 +76,9 @@ /// render(); /// store.when("cc.fovea.test1").updated(render); /// } -/// +/// /// function render() { -/// +/// /// // Get the product from the pool. /// var product = store.get("cc.fovea.test1"); /// @@ -98,13 +98,13 @@ /// + "
" + product.description + "
" /// + "
" + product.price + "
" /// ); -/// +/// /// // Is this product owned? Give him a special class. /// if (product.owned) /// $el.addClass("owned"); /// else /// $el.removeClass("owned"); -/// +/// /// // Is an order for this product in progress? Can't be ordered right now? /// if (product.canPurchase) /// $el.addClass("can-purchase"); @@ -112,7 +112,7 @@ /// $el.removeClass("can-purchase"); /// } /// } -/// +/// /// // method called when the view is hidden /// function hide() { /// // stop monitoring the product @@ -174,7 +174,7 @@ /// #### simple case /// /// In the most simple case, where: -/// +/// /// - delivery of purchases is only local ; /// - you don't want to implement receipt validation ; /// @@ -225,7 +225,7 @@ /// /// If the validator returns a `store.PURCHASE_EXPIRED` error code, the subscription will /// automatically loose its `owned` status. -/// +/// /// Typically, you'll enable and disable access to your content this way. /// ```js /// store.when("cc.fovea.subcription").updated(function(product) { @@ -237,26 +237,26 @@ /// ``` // ### Security -// +// // You will initiate a purchase with `store.order("product.id")`. -// +// // 99% of the times, the purchase will be approved immediately by billing system. // // However, connection can be lost between you sending a purchase request // and the server answering to you. In that case, the purchase shouldn't // be lost (because the user paid for it), that's why the store will notify // you of an approved purchase during the next application startup. -// +// // The same can also happen if the user bought a product from another device, using his // same account. -// -// For that reason, you should register all your features-unlocking listeners at +// +// For that reason, you should register all your features-unlocking listeners at // startup, before the first call to `store.refresh()` // -/// +/// /// # *store* object ## -/// +/// /// `store` is the global object exported by the purchase plugin. /// /// As with any other plugin, this object shouldn't be used before @@ -270,7 +270,7 @@ var store = {}; /// ## *store.verbosity* /// /// The `verbosity` property defines how much you want `store.js` to write on the console. Set to: -/// +/// /// - `store.QUIET` or `0` to disable all logging (default) /// - `store.ERROR` or `1` to show only error messages /// - `store.WARNING` or `2` to show warnings and errors diff --git a/src/js/trigger.js b/src/js/trigger.js index b6fac7dc..5719914c 100644 --- a/src/js/trigger.js +++ b/src/js/trigger.js @@ -41,4 +41,4 @@ store.trigger = function(product, action, args) { store._queries.triggerWhenProduct(product, action, args); }; -}).call(this); +})(); diff --git a/src/js/utils.js b/src/js/utils.js index c8d04830..6e6fa00c 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -56,17 +56,17 @@ store.utils = { /// /// Options: /// - /// * `url`: + /// * `url`: /// * `method`: HTTP method to use (GET, POST, ...) /// * `success`: callback(data) /// * `error`: callback(statusCode, statusText) /// * `data`: body of your request - /// + /// ajax: function(options) { var doneCb = function(){}; var xhr = new XMLHttpRequest(); xhr.open(options.method || 'POST', options.url, true); - xhr.onreadystatechange = function(event) { + xhr.onreadystatechange = function(/*event*/) { try { if (xhr.readyState === 4) { if (xhr.status === 200) { @@ -99,4 +99,4 @@ store.utils = { } }; -}).call(this); +})(); diff --git a/src/js/validator.js b/src/js/validator.js index 6923093b..cfe045be 100644 --- a/src/js/validator.js +++ b/src/js/validator.js @@ -13,7 +13,7 @@ /// ```js /// store.validator = "http://store.fovea.cc:1980/check-purchase"; /// ``` -/// +/// /// ```js /// store.validator = function(product, callback) { /// @@ -38,9 +38,9 @@ /// Validation error codes are [documented here](#validation-error-codes). store.validator = null; -// +// // ## store._validator -// +// // Execute the internal validation call, either to a webservice // or to the provided callback. // @@ -86,10 +86,10 @@ store._validator = function(product, callback, isPrepared) { /// - store specific data /// /// Refer to [this documentation for iOS](https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1). -/// +/// /// Start [here for Android](https://developer.android.com/google/play/billing/billing_integrate.html#billing-security). /// /// Another option is to use [Fovea's reeceipt validation service](http://reeceipt.fovea.cc/) that implements all the best practices to secure your transactions. /// -}).call(this); +})(); diff --git a/src/js/when.js b/src/js/when.js index abaedf78..4fcb111a 100644 --- a/src/js/when.js +++ b/src/js/when.js @@ -2,11 +2,11 @@ 'use strict'; /// ## *store.when(query)* -/// +/// /// Register a callback for a product-related event. /// store.when = function(query, once, callback) { - + // No arguments, will match all products. if (typeof query === 'undefined') query = ''; @@ -28,7 +28,7 @@ store.when = function(query, once, callback) { }; }; - /// + /// /// ### return value /// /// Return a Promise with methods to register callbacks for @@ -61,7 +61,7 @@ store.when = function(query, once, callback) { /// - `cancelled(product)` /// - Called when a product [order](#order) is cancelled by the user. addPromise('cancelled'); - + /// - `refunded(product)` /// - Called when an order is refunded by the user. addPromise('refunded'); @@ -131,7 +131,7 @@ store.when.unregister = function(cb) { /// - `"subscription"` - all subscriptions. /// - `"free subscription"` - all free subscriptions. /// - `"paid subscription"` - all paid subscriptions. -/// +/// /// Filter by product state: /// /// - `"valid"` - all products in the VALID state. @@ -155,6 +155,6 @@ store.when.unregister = function(cb) { /// - equivalent to just `"cc.fovea.inapp1"` /// - `"invalid product"` - an invalid product /// - equivalent to just `"invalid"` -/// +/// -}).call(this); +})(); diff --git a/test/js/helper.js b/test/js/helper.js index 9d8729e1..2b9bf73a 100644 --- a/test/js/helper.js +++ b/test/js/helper.js @@ -1,18 +1,22 @@ -var timeFactor = 1; -var pSetTimeout = global.setTimeout; -global.setTimeout = function(fn, delay) { - pSetTimeout(fn, delay / timeFactor); -}; +(function(){ + "use strict"; -exports.resetTest = function() { - var store = require("../tmp/store-test"); - store._queries.callbacks.byQuery = {}; - store.ready.reset(); - store.products.reset(); - store.error.callbacks.reset(); - timeFactor = 1; -}; + var timeFactor = 1; + var pSetTimeout = global.setTimeout; + global.setTimeout = function(fn, delay) { + pSetTimeout(fn, delay / timeFactor); + }; -exports.setTimeoutFactor = function(value) { - timeFactor = value; -}; + exports.resetTest = function() { + var store = require("../tmp/store-test"); + store._queries.callbacks.byQuery = {}; + store.ready.reset(); + store.products.reset(); + store.error.callbacks.reset(); + timeFactor = 1; + }; + + exports.setTimeoutFactor = function(value) { + timeFactor = value; + }; +})(); diff --git a/test/js/run.js b/test/js/run.js index 1960ee49..d39dc9b8 100644 --- a/test/js/run.js +++ b/test/js/run.js @@ -1,26 +1,29 @@ -var Mocha = require('mocha'); +(function() { + "use strict"; + var Mocha = require('mocha'); -var mocha = new Mocha({ - ui: 'bdd' -}); + var mocha = new Mocha({ + ui: 'bdd' + }); -mocha.reporter('list'); + mocha.reporter('list'); -mocha.addFile('test/js/test-ready.js'); -mocha.addFile('test/js/test-error.js'); -mocha.addFile('test/js/test-off.js'); -mocha.addFile('test/js/test-order.js'); -mocha.addFile('test/js/test-queries.js'); -mocha.addFile('test/js/test-register.js'); -mocha.addFile('test/js/test-when.js'); -mocha.addFile('test/js/test-finish.js'); -mocha.addFile('test/js/test-verify.js'); -mocha.addFile('test/js/test-utils.js'); -mocha.addFile('test/js/test-ios.js'); -mocha.addFile('test/js/test-android.js'); + mocha.addFile('test/js/test-ready.js'); + mocha.addFile('test/js/test-error.js'); + mocha.addFile('test/js/test-off.js'); + mocha.addFile('test/js/test-order.js'); + mocha.addFile('test/js/test-queries.js'); + mocha.addFile('test/js/test-register.js'); + mocha.addFile('test/js/test-when.js'); + mocha.addFile('test/js/test-finish.js'); + mocha.addFile('test/js/test-verify.js'); + mocha.addFile('test/js/test-utils.js'); + mocha.addFile('test/js/test-ios.js'); + mocha.addFile('test/js/test-android.js'); -mocha.run(function(){ - console.log('done'); -}).on('pass', function(test){ - // console.log('... %s', test.title); -}); + mocha.run(function(){ + console.log('done'); + }).on('pass', function(/*test*/){ + // console.log('... %s', test.title); + }); +})(); diff --git a/test/js/test-android.js b/test/js/test-android.js index d85d2f61..b9092d7f 100644 --- a/test/js/test-android.js +++ b/test/js/test-android.js @@ -1,19 +1,23 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); global.store = store; global.document = { - addEventListener: function(event, callback) {} + addEventListener: function(/*event, callback*/) { + "use strict"; + } }; global.localStorage = {}; require("../tmp/android-adapter"); describe('Android', function(){ + "use strict"; describe('#init', function(){ it('should', function() { assert.ok(true); }); }); }); - diff --git a/test/js/test-error.js b/test/js/test-error.js index 856cb9c5..eb56d573 100644 --- a/test/js/test-error.js +++ b/test/js/test-error.js @@ -1,7 +1,10 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Errors', function(){ + "use strict"; beforeEach(function() { store.error.callbacks.reset(); diff --git a/test/js/test-finish.js b/test/js/test-finish.js index 92358a71..2f16214e 100644 --- a/test/js/test-finish.js +++ b/test/js/test-finish.js @@ -1,7 +1,11 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Finish', function() { + "use strict"; + before(function() { require("./helper").resetTest(); store.register({ @@ -19,6 +23,7 @@ describe('Finish', function() { p.set('state', store.VALID); }); store.once(p).valid(function(p) { + assert.equal("pf-consumable", p.id); done(); }); p.finish(); diff --git a/test/js/test-ios.js b/test/js/test-ios.js index 46cc8f34..11291fbe 100644 --- a/test/js/test-ios.js +++ b/test/js/test-ios.js @@ -1,10 +1,12 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach, storekit */ var assert = require("assert"); var store = require("../tmp/store-test"); var helper = require("./helper"); global.store = store; global.document = { - addEventListener: function(event, callback) {} + addEventListener: function(/*event, callback*/) { "use strict"; } }; global.localStorage = {}; @@ -12,6 +14,7 @@ global.localStorage = {}; global.storekit = { initShouldFail: false, init: function(options, success, error) { + "use strict"; this.options = options; this.initCalled = (this.initCalled || 0) + 1; if (this.initShouldFail) { @@ -25,6 +28,7 @@ global.storekit = { }, loadShouldFail: false, load: function(products, success, error) { + "use strict"; this.products = products; this.loadCalled = (this.loadCalled || 0) + 1; if (this.loadShouldFail) { @@ -43,6 +47,7 @@ global.storekit = { }; describe('iOS', function(){ + "use strict"; before(helper.resetTest); after(helper.resetTest); diff --git a/test/js/test-off.js b/test/js/test-off.js index e2500082..5fd03169 100644 --- a/test/js/test-off.js +++ b/test/js/test-off.js @@ -1,7 +1,10 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Off', function(){ + "use strict"; var product = { id: "p1", @@ -92,4 +95,3 @@ describe('Off', function(){ }); }); - diff --git a/test/js/test-order.js b/test/js/test-order.js index 22f10f28..efeb7663 100644 --- a/test/js/test-order.js +++ b/test/js/test-order.js @@ -1,7 +1,10 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Order', function(){ + "use strict"; describe('#order()', function(){ diff --git a/test/js/test-queries.js b/test/js/test-queries.js index 9198d187..1d4ca2ed 100644 --- a/test/js/test-queries.js +++ b/test/js/test-queries.js @@ -1,7 +1,10 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Queries', function(){ + "use strict"; describe('#uniqueQuery()', function(){ @@ -41,12 +44,12 @@ describe('Queries', function(){ approved = true; }); assert.equal(false, approved); - store._queries.triggerWhenProduct({id:"full version"}, "approved"); + store._queries.triggerWhenProduct({id: "full version"}, "approved"); assert.equal(true, approved); approved = false; assert.equal(false, approved); - store._queries.triggerWhenProduct({id:"full version"}, "approved"); + store._queries.triggerWhenProduct({id: "full version"}, "approved"); assert.equal(true, approved); }); @@ -58,12 +61,12 @@ describe('Queries', function(){ approved = true; }, true); assert.equal(false, approved); - store._queries.triggerWhenProduct({id:"lite version"}, "approved"); + store._queries.triggerWhenProduct({id: "lite version"}, "approved"); assert.equal(true, approved); approved = false; assert.equal(false, approved); - store._queries.triggerWhenProduct({id:"lite version"}, "approved"); + store._queries.triggerWhenProduct({id: "lite version"}, "approved"); assert.equal(false, approved); }); @@ -75,30 +78,34 @@ describe('Queries', function(){ approved = true; }); assert.equal(false, approved); - store._queries.triggerWhenProduct({id:"anything", type:store.FREE_SUBSCRIPTION}, "approved"); + store._queries.triggerWhenProduct({id: "anything", type: store.FREE_SUBSCRIPTION}, "approved"); assert.equal(true, approved); }); it('should execute all callbacks even if one fails with an exception', function() { - var f1_called = false; - var f1_returned = false; - var f2_called = false; - var f2_returned = false; + var f1Called = false; + var f1Returned = false; + var f2Called = false; + var f2Returned = false; store._queries.callbacks.add("", "yyy", function() { - f1_called = true; - if (f1_called) throw "ERROR"; - f1_returned = true; + f1Called = true; + if (f1Called) { + throw "ERROR"; + } + f1Returned = true; }, true); store._queries.callbacks.add("", "yyy", function() { - f2_called = true; - if (f2_called) throw "ERROR"; - f2_returned = true; + f2Called = true; + if (f2Called) { + throw "ERROR"; + } + f2Returned = true; }, true); store._queries.triggerAction("yyy"); - assert.equal(true, f1_called); - assert.equal(false, f1_returned); - assert.equal(true, f2_called); - assert.equal(false, f2_returned); + assert.equal(true, f1Called); + assert.equal(false, f1Returned); + assert.equal(true, f2Called); + assert.equal(false, f2Returned); }); it('should trigger an update for any event', function(done) { @@ -106,24 +113,24 @@ describe('Queries', function(){ assert.equal("test.updated1", product.id); done(); }); - store._queries.triggerWhenProduct({id:"test.updated1"}, "xyz"); + store._queries.triggerWhenProduct({id: "test.updated1"}, "xyz"); }); it('should not trigger an update for error events', function(done) { - store._queries.callbacks.add("product", "updated", function(product) { - console.log("updated2"); + store._queries.callbacks.add("product", "updated", function(/*product*/) { assert(false, "updated shouldn't be called"); }); - store._queries.triggerWhenProduct({id:"test.updated2"}, "error"); + store._queries.triggerWhenProduct({id: "test.updated2"}, "error"); setTimeout(done, 6); }); it('should not trigger "updated" events twice', function(done) { var nCalls = 0; store._queries.callbacks.add("product", "updated", function(product) { + assert.equal("test.updated2", product.id); nCalls++; }); - store._queries.triggerWhenProduct({id:"test.updated2"}, "udpated"); + store._queries.triggerWhenProduct({id: "test.updated2"}, "udpated"); setTimeout(function() { assert.equal(1, nCalls); done(); diff --git a/test/js/test-ready.js b/test/js/test-ready.js index b960c199..64cc7da3 100644 --- a/test/js/test-ready.js +++ b/test/js/test-ready.js @@ -1,7 +1,10 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Ready', function(){ + "use strict"; describe('#ready()', function(){ diff --git a/test/js/test-register.js b/test/js/test-register.js index d29a0add..f289c7db 100644 --- a/test/js/test-register.js +++ b/test/js/test-register.js @@ -1,7 +1,14 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Register', function(){ + "use strict"; + + var helper = require("./helper"); + before(helper.resetTest); + after(helper.resetTest); describe('#register()', function(){ @@ -43,4 +50,3 @@ describe('Register', function(){ }); }); }); - diff --git a/test/js/test-utils.js b/test/js/test-utils.js index 477cef02..592446d9 100644 --- a/test/js/test-utils.js +++ b/test/js/test-utils.js @@ -1,18 +1,21 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); describe('Utils', function(){ + "use strict"; describe('#ajax', function(){ - XMLHttpRequest = function() { + global.XMLHttpRequest = function() { this.open = function(method, url) { this.method = method; this.url = url; }; - this.setRequestHeader = function(key,value) {}; - this.onreadystatechange = function(event) {}; - this.send = function(data) { + this.setRequestHeader = function(/*key,value*/) {}; + this.onreadystatechange = function(/*event*/) {}; + this.send = function(/*data*/) { this.readyState = 1; this.onreadystatechange({}); this.readyState = 2; @@ -43,7 +46,7 @@ describe('Utils', function(){ assert.ok(data.success, "request should send success status"); done(); }, - error: function(status, message) { + error: function(/*status, message*/) { assert.ok(false, "this request shouldn't fail"); } }); @@ -54,11 +57,12 @@ describe('Utils', function(){ url: 'error404', method: 'POST', data: { a: 1, b: 2 }, - success: function(data) { + success: function(/*data*/) { assert.ok(false, "request should not send success status"); }, error: function(status, message) { assert.equal(404, status, "this request should fail with status 404"); + assert.ok(message, "this request should send an error message"); done(); } }); @@ -69,10 +73,10 @@ describe('Utils', function(){ url: 'dummy', method: 'POST', data: { a: 1, b: 2 }, - success: function(data) { - a.b.c = 0; + success: function(/*data*/) { + throw new Error(); }, - error: function(status, message) { + error: function(/*status, message*/) { assert.ok(false, "Error callback shoudn't be called"); } }).done(function() { @@ -81,4 +85,3 @@ describe('Utils', function(){ }); }); }); - diff --git a/test/js/test-verify.js b/test/js/test-verify.js index 5dacb100..de0df255 100644 --- a/test/js/test-verify.js +++ b/test/js/test-verify.js @@ -1,8 +1,12 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); var helper = require("./helper"); describe('Verify', function() { + "use strict"; + before(function() { require("./helper").resetTest(); store.register({ @@ -34,8 +38,9 @@ describe('Verify', function() { callback(true, product); }; - step1(); - function step1() { + var step1, step2; + + step1 = function() { p.set('state', store.VALID); var doneCalled = 0; var errorCalled = 0; @@ -51,9 +56,9 @@ describe('Verify', function() { assert.equal(1, errorCalled, "error should be called"); step2(); }, 1500); - } + }; - function step2() { + step2 = function() { p.set('state', store.APPROVED); var successCalled = 0; var doneCalled = 0; @@ -68,7 +73,9 @@ describe('Verify', function() { assert.equal(1, doneCalled, "done should be called"); done(); }, 1500); - } + }; + + step1(); }); it('should retry 5 times when validator fails', function(done) { @@ -80,7 +87,6 @@ describe('Verify', function() { }; p.set('state', store.APPROVED); helper.setTimeoutFactor(4000); - step(); function step() { var successCalled = 0; @@ -105,7 +111,8 @@ describe('Verify', function() { done(); }, 200000); } + + step(); }); }); }); - diff --git a/test/js/test-when.js b/test/js/test-when.js index aa6bda34..6077b55b 100644 --- a/test/js/test-when.js +++ b/test/js/test-when.js @@ -1,8 +1,11 @@ +/*eslint-env mocha */ +/*global describe, it, before, beforeEach, after, afterEach */ var assert = require("assert"); var store = require("../tmp/store-test"); var helper = require("./helper"); describe('When', function(){ + "use strict"; var product = { id: "p1", @@ -24,7 +27,6 @@ describe('When', function(){ approved(nop). updated(nop). cancelled(nop); - }); it('should be called on id', function(){ @@ -33,15 +35,16 @@ describe('When', function(){ var loaded = false; store.when("p1").loaded(function(product) { loaded = true; + assert.equal(product.id, "p1"); }); - assert.equal(loaded, false); - store._queries.triggerWhenProduct(product, "loaded"); - assert.equal(loaded, true); + assert.equal(loaded, false, "loaded should not be called initially"); + store._queries.triggerWhenProduct(product, "loaded", [product]); + assert.equal(loaded, true, "loaded should be called after the 'loaded' event"); loaded = false; - store._queries.triggerWhenProduct(product, "loaded"); - assert.equal(loaded, true); + store._queries.triggerWhenProduct(product, "loaded", [product]); + assert.equal(loaded, true, "loaded should be called every 'loaded' event"); }); it('should be called on aliases', function(){ @@ -49,15 +52,16 @@ describe('When', function(){ var loaded = false; store.when("product").loaded(function(product) { + assert.equal(product.id, "p1"); loaded = true; }); assert.equal(loaded, false); - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, true); loaded = false; - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, true); }); @@ -89,15 +93,16 @@ describe('When', function(){ var loaded = false; store.once("p1").loaded(function(product) { + assert.equal(product.id, "p1"); loaded = true; }); assert.equal(loaded, false); - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, true); loaded = false; - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, false); }); @@ -106,15 +111,16 @@ describe('When', function(){ var loaded = false; store.once("product").loaded(function(product) { + assert.equal(product.id, "p1"); loaded = true; }); assert.equal(loaded, false); - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, true); loaded = false; - store._queries.triggerWhenProduct(product, "loaded"); + store._queries.triggerWhenProduct(product, "loaded", [product]); assert.equal(loaded, false); }); }); diff --git a/www/store-android.js b/www/store-android.js index f4fedccd..55924677 100644 --- a/www/store-android.js +++ b/www/store-android.js @@ -44,10 +44,16 @@ store.verbosity = 0; store.INVALID_PAYLOAD = 6778001; store.CONNECTION_FAILED = 6778002; store.PURCHASE_EXPIRED = 6778003; -}).call(this); +})(); (function() { "use strict"; + function defer(thisArg, cb, delay) { + setTimeout(function() { + cb.call(thisArg); + }, delay || 1); + } + var delay = defer; store.Product = function(options) { if (!options) options = {}; this.id = options.id || null; @@ -78,10 +84,11 @@ store.verbosity = 0; store.Product.prototype.verify = function() { var that = this; var nRetry = 0; - var doneCb = function() {}; - var successCb = function() {}; - var expiredCb = function() {}; - var errorCb = function() {}; + var noop = function() {}; + var doneCb = noop; + var successCb = noop; + var expiredCb = noop; + var errorCb = noop; var tryValidation = function() { if (that.state !== store.APPROVED) return; store._validator(that, function(success, data) { @@ -94,20 +101,20 @@ store.verbosity = 0; } else { store.log.debug("verify -> error: " + JSON.stringify(data)); var msg = data && data.error && data.error.message ? data.error.message : ""; - var err = new Error({ + var err = new store.Error({ code: store.ERR_VERIFICATION_FAILED, message: "Transaction verification failed: " + msg }); if (data.code === store.PURCHASE_EXPIRED) { - err = new Error({ + err = new store.Error({ code: store.ERR_PAYMENT_EXPIRED, message: "Transaction expired: " + msg }); } - store.error(err); - store.utils.callExternal("verify.error", errorCb, err); - store.utils.callExternal("verify.done", doneCb, that); if (data.code === store.PURCHASE_EXPIRED) { + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); that.trigger("expired"); that.set("state", store.VALID); store.utils.callExternal("verify.expired", expiredCb, that); @@ -115,11 +122,27 @@ store.verbosity = 0; nRetry += 1; delay(this, tryValidation, 1e3 * nRetry * nRetry); } else { + store.log.debug("validation failed 5 times, stop retrying, trigger an error"); + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); that.trigger("unverified"); } } }); }; + defer(this, function() { + if (that.state !== store.APPROVED) { + var err = new store.Error({ + code: store.ERR_VERIFICATION_FAILED, + message: "Product isn't in the APPROVED state" + }); + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); + return; + } + }); delay(this, tryValidation, 1e3); var ret = { done: function(cb) { @@ -141,13 +164,7 @@ store.verbosity = 0; }; return ret; }; - var defer = function(thisArg, cb, delay) { - window.setTimeout(function() { - cb.call(thisArg); - }, delay || 1); - }; - var delay = defer; -}).call(this); +})(); (function() { "use strict"; @@ -169,14 +186,13 @@ store.verbosity = 0; store.error.unregister = function(cb) { store.error.callbacks.unregister(cb); }; -}).call(this); +})(); (function() { "use strict"; store.register = function(product) { if (!product) return; - if (!product.length) return store.register([ product ]); - registerProducts(product); + if (!product.length) store.register([ product ]); else registerProducts(product); }; function registerProducts(products) { for (var i = 0; i < products.length; ++i) { @@ -201,7 +217,7 @@ store.verbosity = 0; } return false; } -}).call(this); +})(); (function() { "use strict"; @@ -209,7 +225,7 @@ store.verbosity = 0; var product = store.products.byId[id] || store.products.byAlias[id]; return product; }; -}).call(this); +})(); (function() { "use strict"; @@ -250,7 +266,7 @@ store.verbosity = 0; store.when.unregister = function(cb) { store._queries.callbacks.unregister(cb); }; -}).call(this); +})(); (function() { "use strict"; @@ -264,14 +280,13 @@ store.verbosity = 0; } }; store.once.unregister = store.when.unregister; -}).call(this); +})(); (function() { "use strict"; var callbacks = {}; var callbackId = 0; store.order = function(pid) { - var that = this; var p = pid; if (typeof pid === "string") { p = store.products.byId[pid] || store.products.byAlias[pid]; @@ -308,7 +323,7 @@ store.verbosity = 0; store.once(p.id, "error", function(err) { if (!localCallback.error) return; done(); - cb(p); + cb(err); }); return this; } @@ -320,7 +335,7 @@ store.verbosity = 0; if (callbacks[i].error === cb) delete callbacks[i].error; } }; -}).call(this); +})(); (function() { "use strict"; @@ -355,7 +370,7 @@ store.verbosity = 0; isReady = false; callbacks = []; }; -}).call(this); +})(); (function() { "use strict"; @@ -365,7 +380,7 @@ store.verbosity = 0; store.order.unregister(callback); store.error.unregister(callback); }; -}).call(this); +})(); (function() { "use strict"; @@ -394,7 +409,7 @@ store.verbosity = 0; store.validator(product, callback); } }; -}).call(this); +})(); (function() { "use strict"; @@ -414,7 +429,7 @@ store.verbosity = 0; } store.trigger("re-refreshed"); }; -}).call(this); +})(); (function() { "use strict"; @@ -443,7 +458,7 @@ store.verbosity = 0; log(store.DEBUG, o); } }; -}).call(this); +})(); (function() { "use strict"; @@ -460,7 +475,7 @@ store.verbosity = 0; this.byAlias = {}; this.byId = {}; }; -}).call(this); +})(); (function() { "use strict"; @@ -496,7 +511,7 @@ store.verbosity = 0; store.Product.prototype.trigger = function(action, args) { store.trigger(this, action, args); }; -}).call(this); +})(); (function() { "use strict"; @@ -551,7 +566,7 @@ store.verbosity = 0; triggerWhenProduct: function(product, action, args) { var queries = []; if (product && product.id) queries.push(product.id + " " + action); - if (product && product.alias && product.alias != product.id) queries.push(product.alias + " " + action); + if (product && product.alias && product.alias !== product.id) queries.push(product.alias + " " + action); if (product && product.type) queries.push(product.type + " " + action); if (product && product.type && (product.type === store.FREE_SUBSCRIPTION || product.type === store.PAID_SUBSCRIPTION)) queries.push("subscription " + action); if (product && product.valid === true) queries.push("valid " + action); @@ -585,7 +600,7 @@ store.verbosity = 0; throw err; }, 1); } -}).call(this); +})(); (function() { "use strict"; @@ -607,7 +622,7 @@ store.verbosity = 0; } store._queries.triggerWhenProduct(product, action, args); }; -}).call(this); +})(); (function() { "use strict"; @@ -639,7 +654,7 @@ store.verbosity = 0; throw err; }, 1); } -}).call(this); +})(); (function() { "use strict"; @@ -654,7 +669,6 @@ store.verbosity = 0; }, callExternal: function(name, callback) { try { - store.log.debug("calling " + name); var args = Array.prototype.slice.call(arguments, 2); if (callback) callback.apply(this, args); } catch (e) { @@ -665,7 +679,7 @@ store.verbosity = 0; var doneCb = function() {}; var xhr = new XMLHttpRequest(); xhr.open(options.method || "POST", options.url, true); - xhr.onreadystatechange = function(event) { + xhr.onreadystatechange = function() { try { if (xhr.readyState === 4) { if (xhr.status === 200) { @@ -696,7 +710,7 @@ store.verbosity = 0; }; } }; -}).call(this); +})(); (function() { "use strict"; @@ -735,9 +749,9 @@ store.verbosity = 0; } } if (hasSKUs) { - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", [ skus ]); + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", [ skus ]); } else { - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", []); + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "init", []); } }; InAppBilling.prototype.getPurchases = function(success, fail) { @@ -789,7 +803,7 @@ store.verbosity = 0; if (this.options.showLog) { log("load " + JSON.stringify(skus)); } - return cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getProductDetails", [ skus ]); + cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getProductDetails", [ skus ]); } }; function errorCb(fail) { @@ -809,19 +823,19 @@ store.verbosity = 0; try { store.android = window.inappbilling; } catch (e) {} -}).call(this); +})(); (function() { "use strict"; + var initialized = false; + var skus = []; store.when("refreshed", function() { if (!initialized) init(); }); store.when("re-refreshed", function() { iabGetPurchases(); }); - var initialized = false; - var skus = []; - var init = function() { + function init() { if (initialized) return; initialized = true; for (var i = 0; i < store.products.length; ++i) skus.push(store.products[i].id); @@ -833,7 +847,7 @@ store.verbosity = 0; }, { showLog: store.verbosity >= store.DEBUG ? true : false }, skus); - }; + } function iabReady() { store.log.debug("android -> ready"); store.android.getAvailableProducts(iabLoaded, function(err) { @@ -955,7 +969,7 @@ store.verbosity = 0; product.set("state", store.VALID); }, function(err, code) { store.error({ - code: code || ERR_UNKNOWN, + code: code || store.ERR_UNKNOWN, message: err }); }, product.id); @@ -963,7 +977,7 @@ store.verbosity = 0; product.set("state", store.OWNED); } }); -}).call(this); +})(); if (window) { window.store = store; diff --git a/www/store-ios.js b/www/store-ios.js index 7eae3bcd..8e084ed6 100644 --- a/www/store-ios.js +++ b/www/store-ios.js @@ -44,10 +44,16 @@ store.verbosity = 0; store.INVALID_PAYLOAD = 6778001; store.CONNECTION_FAILED = 6778002; store.PURCHASE_EXPIRED = 6778003; -}).call(this); +})(); (function() { "use strict"; + function defer(thisArg, cb, delay) { + setTimeout(function() { + cb.call(thisArg); + }, delay || 1); + } + var delay = defer; store.Product = function(options) { if (!options) options = {}; this.id = options.id || null; @@ -78,10 +84,11 @@ store.verbosity = 0; store.Product.prototype.verify = function() { var that = this; var nRetry = 0; - var doneCb = function() {}; - var successCb = function() {}; - var expiredCb = function() {}; - var errorCb = function() {}; + var noop = function() {}; + var doneCb = noop; + var successCb = noop; + var expiredCb = noop; + var errorCb = noop; var tryValidation = function() { if (that.state !== store.APPROVED) return; store._validator(that, function(success, data) { @@ -94,20 +101,20 @@ store.verbosity = 0; } else { store.log.debug("verify -> error: " + JSON.stringify(data)); var msg = data && data.error && data.error.message ? data.error.message : ""; - var err = new Error({ + var err = new store.Error({ code: store.ERR_VERIFICATION_FAILED, message: "Transaction verification failed: " + msg }); if (data.code === store.PURCHASE_EXPIRED) { - err = new Error({ + err = new store.Error({ code: store.ERR_PAYMENT_EXPIRED, message: "Transaction expired: " + msg }); } - store.error(err); - store.utils.callExternal("verify.error", errorCb, err); - store.utils.callExternal("verify.done", doneCb, that); if (data.code === store.PURCHASE_EXPIRED) { + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); that.trigger("expired"); that.set("state", store.VALID); store.utils.callExternal("verify.expired", expiredCb, that); @@ -115,11 +122,27 @@ store.verbosity = 0; nRetry += 1; delay(this, tryValidation, 1e3 * nRetry * nRetry); } else { + store.log.debug("validation failed 5 times, stop retrying, trigger an error"); + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); that.trigger("unverified"); } } }); }; + defer(this, function() { + if (that.state !== store.APPROVED) { + var err = new store.Error({ + code: store.ERR_VERIFICATION_FAILED, + message: "Product isn't in the APPROVED state" + }); + store.error(err); + store.utils.callExternal("verify.error", errorCb, err); + store.utils.callExternal("verify.done", doneCb, that); + return; + } + }); delay(this, tryValidation, 1e3); var ret = { done: function(cb) { @@ -141,13 +164,7 @@ store.verbosity = 0; }; return ret; }; - var defer = function(thisArg, cb, delay) { - window.setTimeout(function() { - cb.call(thisArg); - }, delay || 1); - }; - var delay = defer; -}).call(this); +})(); (function() { "use strict"; @@ -169,14 +186,13 @@ store.verbosity = 0; store.error.unregister = function(cb) { store.error.callbacks.unregister(cb); }; -}).call(this); +})(); (function() { "use strict"; store.register = function(product) { if (!product) return; - if (!product.length) return store.register([ product ]); - registerProducts(product); + if (!product.length) store.register([ product ]); else registerProducts(product); }; function registerProducts(products) { for (var i = 0; i < products.length; ++i) { @@ -201,7 +217,7 @@ store.verbosity = 0; } return false; } -}).call(this); +})(); (function() { "use strict"; @@ -209,7 +225,7 @@ store.verbosity = 0; var product = store.products.byId[id] || store.products.byAlias[id]; return product; }; -}).call(this); +})(); (function() { "use strict"; @@ -250,7 +266,7 @@ store.verbosity = 0; store.when.unregister = function(cb) { store._queries.callbacks.unregister(cb); }; -}).call(this); +})(); (function() { "use strict"; @@ -264,14 +280,13 @@ store.verbosity = 0; } }; store.once.unregister = store.when.unregister; -}).call(this); +})(); (function() { "use strict"; var callbacks = {}; var callbackId = 0; store.order = function(pid) { - var that = this; var p = pid; if (typeof pid === "string") { p = store.products.byId[pid] || store.products.byAlias[pid]; @@ -308,7 +323,7 @@ store.verbosity = 0; store.once(p.id, "error", function(err) { if (!localCallback.error) return; done(); - cb(p); + cb(err); }); return this; } @@ -320,7 +335,7 @@ store.verbosity = 0; if (callbacks[i].error === cb) delete callbacks[i].error; } }; -}).call(this); +})(); (function() { "use strict"; @@ -355,7 +370,7 @@ store.verbosity = 0; isReady = false; callbacks = []; }; -}).call(this); +})(); (function() { "use strict"; @@ -365,7 +380,7 @@ store.verbosity = 0; store.order.unregister(callback); store.error.unregister(callback); }; -}).call(this); +})(); (function() { "use strict"; @@ -394,7 +409,7 @@ store.verbosity = 0; store.validator(product, callback); } }; -}).call(this); +})(); (function() { "use strict"; @@ -414,7 +429,7 @@ store.verbosity = 0; } store.trigger("re-refreshed"); }; -}).call(this); +})(); (function() { "use strict"; @@ -443,7 +458,7 @@ store.verbosity = 0; log(store.DEBUG, o); } }; -}).call(this); +})(); (function() { "use strict"; @@ -460,7 +475,7 @@ store.verbosity = 0; this.byAlias = {}; this.byId = {}; }; -}).call(this); +})(); (function() { "use strict"; @@ -496,7 +511,7 @@ store.verbosity = 0; store.Product.prototype.trigger = function(action, args) { store.trigger(this, action, args); }; -}).call(this); +})(); (function() { "use strict"; @@ -551,7 +566,7 @@ store.verbosity = 0; triggerWhenProduct: function(product, action, args) { var queries = []; if (product && product.id) queries.push(product.id + " " + action); - if (product && product.alias && product.alias != product.id) queries.push(product.alias + " " + action); + if (product && product.alias && product.alias !== product.id) queries.push(product.alias + " " + action); if (product && product.type) queries.push(product.type + " " + action); if (product && product.type && (product.type === store.FREE_SUBSCRIPTION || product.type === store.PAID_SUBSCRIPTION)) queries.push("subscription " + action); if (product && product.valid === true) queries.push("valid " + action); @@ -585,7 +600,7 @@ store.verbosity = 0; throw err; }, 1); } -}).call(this); +})(); (function() { "use strict"; @@ -607,7 +622,7 @@ store.verbosity = 0; } store._queries.triggerWhenProduct(product, action, args); }; -}).call(this); +})(); (function() { "use strict"; @@ -639,7 +654,7 @@ store.verbosity = 0; throw err; }, 1); } -}).call(this); +})(); (function() { "use strict"; @@ -654,7 +669,6 @@ store.verbosity = 0; }, callExternal: function(name, callback) { try { - store.log.debug("calling " + name); var args = Array.prototype.slice.call(arguments, 2); if (callback) callback.apply(this, args); } catch (e) { @@ -665,7 +679,7 @@ store.verbosity = 0; var doneCb = function() {}; var xhr = new XMLHttpRequest(); xhr.open(options.method || "POST", options.url, true); - xhr.onreadystatechange = function(event) { + xhr.onreadystatechange = function() { try { if (xhr.readyState === 4) { if (xhr.status === 200) { @@ -696,305 +710,276 @@ store.verbosity = 0; }; } }; -}).call(this); - -var exec = function(methodName, options, success, error) { - cordova.exec(success, error, "InAppPurchase", methodName, options); -}; - -var protectCall = function(callback, context) { - if (!callback) return; - try { - var args = Array.prototype.slice.call(arguments, 2); - callback.apply(this, args); - } catch (err) { - log("exception in " + context + ': "' + err + '"'); - } -}; - -var InAppPurchase = function() { - this.options = {}; - this.receiptForTransaction = {}; - this.receiptForProduct = {}; - if (window.localStorage && window.localStorage.sk_receiptForTransaction) this.receiptForTransaction = JSON.parse(window.localStorage.sk_receiptForTransaction); - if (window.localStorage && window.localStorage.sk_receiptForProduct) this.receiptForProduct = JSON.parse(window.localStorage.sk_receiptForProduct); -}; - -var noop = function() {}; - -var log = noop; - -var ERROR_CODES_BASE = 6777e3; - -InAppPurchase.prototype.ERR_SETUP = ERROR_CODES_BASE + 1; - -InAppPurchase.prototype.ERR_LOAD = ERROR_CODES_BASE + 2; - -InAppPurchase.prototype.ERR_PURCHASE = ERROR_CODES_BASE + 3; - -InAppPurchase.prototype.ERR_LOAD_RECEIPTS = ERROR_CODES_BASE + 4; - -InAppPurchase.prototype.ERR_CLIENT_INVALID = ERROR_CODES_BASE + 5; +})(); -InAppPurchase.prototype.ERR_PAYMENT_CANCELLED = ERROR_CODES_BASE + 6; - -InAppPurchase.prototype.ERR_PAYMENT_INVALID = ERROR_CODES_BASE + 7; - -InAppPurchase.prototype.ERR_PAYMENT_NOT_ALLOWED = ERROR_CODES_BASE + 8; - -InAppPurchase.prototype.ERR_UNKNOWN = ERROR_CODES_BASE + 10; - -InAppPurchase.prototype.ERR_REFRESH_RECEIPTS = ERROR_CODES_BASE + 11; - -var initialized = false; - -InAppPurchase.prototype.init = function(options, success, error) { - this.options = { - error: options.error || noop, - ready: options.ready || noop, - purchase: options.purchase || noop, - purchaseEnqueued: options.purchaseEnqueued || noop, - purchasing: options.purchasing || noop, - finish: options.finish || noop, - restore: options.restore || noop, - receiptsRefreshed: options.receiptsRefreshed || noop, - restoreFailed: options.restoreFailed || noop, - restoreCompleted: options.restoreCompleted || noop - }; - if (options.debug) { - exec("debug", [], noop, noop); - log = function(msg) { - console.log("InAppPurchase[js]: " + msg); - }; - } - if (options.noAutoFinish) { - exec("noAutoFinish", [], noop, noop); - } - var that = this; - var setupOk = function() { - log("setup ok"); - protectCall(that.options.ready, "options.ready"); - protectCall(success, "init.success"); - initialized = true; - that.processPendingUpdates(); - }; - var setupFailed = function() { - log("setup failed"); - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_SETUP, "Setup failed"); - protectCall(error, "init.error"); +(function() { + "use strict"; + var exec = function(methodName, options, success, error) { + cordova.exec(success, error, "InAppPurchase", methodName, options); }; - exec("setup", [], setupOk, setupFailed); -}; - -InAppPurchase.prototype.purchase = function(productId, quantity) { - quantity = quantity | 0 || 1; - var options = this.options; - if (!InAppPurchase._productIds || InAppPurchase._productIds.indexOf(productId) < 0) { - var msg = "Purchasing " + productId + " failed. Ensure the product was loaded first with storekit.load(...)!"; - log(msg); - if (typeof options.error === "function") { - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_PURCHASE, "Trying to purchase a unknown product.", productId, quantity); + var protectCall = function(callback, context) { + if (!callback) { + return; } - return; - } - var purchaseOk = function() { - log("Purchased " + productId); - if (typeof options.purchaseEnqueued === "function") { - protectCall(options.purchaseEnqueued, "options.purchaseEnqueued", productId, quantity); + try { + var args = Array.prototype.slice.call(arguments, 2); + callback.apply(this, args); + } catch (err) { + log("exception in " + context + ': "' + err + '"'); } }; - var purchaseFailed = function() { - var msg = "Purchasing " + productId + " failed"; - log(msg); - if (typeof options.error === "function") { - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_PURCHASE, msg, productId, quantity); + var InAppPurchase = function() { + this.options = {}; + this.receiptForTransaction = {}; + this.receiptForProduct = {}; + if (window.localStorage && window.localStorage.sk_receiptForTransaction) this.receiptForTransaction = JSON.parse(window.localStorage.sk_receiptForTransaction); + if (window.localStorage && window.localStorage.sk_receiptForProduct) this.receiptForProduct = JSON.parse(window.localStorage.sk_receiptForProduct); + }; + var noop = function() {}; + var log = noop; + var ERROR_CODES_BASE = 6777e3; + InAppPurchase.prototype.ERR_SETUP = ERROR_CODES_BASE + 1; + InAppPurchase.prototype.ERR_LOAD = ERROR_CODES_BASE + 2; + InAppPurchase.prototype.ERR_PURCHASE = ERROR_CODES_BASE + 3; + InAppPurchase.prototype.ERR_LOAD_RECEIPTS = ERROR_CODES_BASE + 4; + InAppPurchase.prototype.ERR_CLIENT_INVALID = ERROR_CODES_BASE + 5; + InAppPurchase.prototype.ERR_PAYMENT_CANCELLED = ERROR_CODES_BASE + 6; + InAppPurchase.prototype.ERR_PAYMENT_INVALID = ERROR_CODES_BASE + 7; + InAppPurchase.prototype.ERR_PAYMENT_NOT_ALLOWED = ERROR_CODES_BASE + 8; + InAppPurchase.prototype.ERR_UNKNOWN = ERROR_CODES_BASE + 10; + InAppPurchase.prototype.ERR_REFRESH_RECEIPTS = ERROR_CODES_BASE + 11; + var initialized = false; + InAppPurchase.prototype.init = function(options, success, error) { + this.options = { + error: options.error || noop, + ready: options.ready || noop, + purchase: options.purchase || noop, + purchaseEnqueued: options.purchaseEnqueued || noop, + purchasing: options.purchasing || noop, + finish: options.finish || noop, + restore: options.restore || noop, + receiptsRefreshed: options.receiptsRefreshed || noop, + restoreFailed: options.restoreFailed || noop, + restoreCompleted: options.restoreCompleted || noop + }; + if (options.debug) { + exec("debug", [], noop, noop); + log = function(msg) { + console.log("InAppPurchase[js]: " + msg); + }; } + if (options.noAutoFinish) { + exec("noAutoFinish", [], noop, noop); + } + var that = this; + var setupOk = function() { + log("setup ok"); + protectCall(that.options.ready, "options.ready"); + protectCall(success, "init.success"); + initialized = true; + that.processPendingUpdates(); + }; + var setupFailed = function() { + log("setup failed"); + protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_SETUP, "Setup failed"); + protectCall(error, "init.error"); + }; + exec("setup", [], setupOk, setupFailed); }; - return exec("purchase", [ productId, quantity ], purchaseOk, purchaseFailed); -}; - -InAppPurchase.prototype.restore = function() { - this.needRestoreNotification = true; - return exec("restoreCompletedTransactions", []); -}; - -InAppPurchase.prototype.load = function(productIds, success, error) { - var options = this.options; - if (typeof productIds === "string") { - productIds = [ productIds ]; - } - if (!productIds) { - protectCall(success, "load.success", [], []); - } else if (!productIds.length) { - protectCall(success, "load.success", [], []); - } else { - if (typeof productIds[0] !== "string") { - var msg = "invalid productIds given to store.load: " + JSON.stringify(productIds); + InAppPurchase.prototype.purchase = function(productId, quantity) { + quantity = quantity | 0 || 1; + var options = this.options; + if (!InAppPurchase._productIds || InAppPurchase._productIds.indexOf(productId) < 0) { + var msg = "Purchasing " + productId + " failed. Ensure the product was loaded first with storekit.load(...)!"; log(msg); - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_LOAD, msg); - protectCall(error, "load.error", InAppPurchase.prototype.ERR_LOAD, msg); + if (typeof options.error === "function") { + protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_PURCHASE, "Trying to purchase a unknown product.", productId, quantity); + } return; } - log("load " + JSON.stringify(productIds)); - var loadOk = function(array) { - var valid = array[0]; - var invalid = array[1]; - log("load ok: { valid:" + JSON.stringify(valid) + " invalid:" + JSON.stringify(invalid) + " }"); - protectCall(success, "load.success", valid, invalid); + var purchaseOk = function() { + log("Purchased " + productId); + if (typeof options.purchaseEnqueued === "function") { + protectCall(options.purchaseEnqueued, "options.purchaseEnqueued", productId, quantity); + } }; - var loadFailed = function(errMessage) { - log("load failed"); - log(errMessage); - var message = "Load failed: " + errMessage; - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_LOAD, message); - protectCall(error, "load.error", InAppPurchase.prototype.ERR_LOAD, message); + var purchaseFailed = function() { + var errmsg = "Purchasing " + productId + " failed"; + log(errmsg); + if (typeof options.error === "function") { + protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_PURCHASE, errmsg, productId, quantity); + } }; - InAppPurchase._productIds = productIds; - exec("load", [ productIds ], loadOk, loadFailed); - } -}; - -InAppPurchase.prototype.finish = function(transactionId) { - exec("finishTransaction", [ transactionId ], noop, noop); -}; - -var pendingUpdates = []; - -InAppPurchase.prototype.processPendingUpdates = function() { - for (var i = 0; i < pendingUpdates.length; ++i) { - this.updatedTransactionCallback.apply(this, pendingUpdates[i]); - } - pendingUpdates = []; -}; - -InAppPurchase.prototype.updatedTransactionCallback = function(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt) { - if (!initialized) { - var args = Array.prototype.slice.call(arguments); - pendingUpdates.push(args); - return; - } - if (transactionReceipt) { - this.receiptForProduct[productId] = transactionReceipt; - this.receiptForTransaction[transactionIdentifier] = transactionReceipt; - if (window.localStorage) { - window.localStorage.sk_receiptForProduct = JSON.stringify(this.receiptForProduct); - window.localStorage.sk_receiptForTransaction = JSON.stringify(this.receiptForTransaction); + exec("purchase", [ productId, quantity ], purchaseOk, purchaseFailed); + }; + InAppPurchase.prototype.restore = function() { + this.needRestoreNotification = true; + exec("restoreCompletedTransactions", []); + }; + InAppPurchase.prototype.load = function(productIds, success, error) { + var options = this.options; + if (typeof productIds === "string") { + productIds = [ productIds ]; } - } - switch (state) { - case "PaymentTransactionStatePurchasing": - protectCall(this.options.purchasing, "options.purchasing", productId); - return; - - case "PaymentTransactionStatePurchased": - protectCall(this.options.purchase, "options.purchase", transactionIdentifier, productId); - return; - - case "PaymentTransactionStateFailed": - protectCall(this.options.error, "options.error", errorCode, errorText, { - productId: productId - }); - return; - - case "PaymentTransactionStateRestored": - protectCall(this.options.restore, "options.restore", transactionIdentifier, productId); - return; + if (!productIds) { + protectCall(success, "load.success", [], []); + } else if (!productIds.length) { + protectCall(success, "load.success", [], []); + } else { + if (typeof productIds[0] !== "string") { + var msg = "invalid productIds given to store.load: " + JSON.stringify(productIds); + log(msg); + protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_LOAD, msg); + protectCall(error, "load.error", InAppPurchase.prototype.ERR_LOAD, msg); + return; + } + log("load " + JSON.stringify(productIds)); + var loadOk = function(array) { + var valid = array[0]; + var invalid = array[1]; + log("load ok: { valid:" + JSON.stringify(valid) + " invalid:" + JSON.stringify(invalid) + " }"); + protectCall(success, "load.success", valid, invalid); + }; + var loadFailed = function(errMessage) { + log("load failed"); + log(errMessage); + var message = "Load failed: " + errMessage; + protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_LOAD, message); + protectCall(error, "load.error", InAppPurchase.prototype.ERR_LOAD, message); + }; + InAppPurchase._productIds = productIds; + exec("load", [ productIds ], loadOk, loadFailed); + } + }; + InAppPurchase.prototype.finish = function(transactionId) { + exec("finishTransaction", [ transactionId ], noop, noop); + }; + var pendingUpdates = []; + InAppPurchase.prototype.processPendingUpdates = function() { + for (var i = 0; i < pendingUpdates.length; ++i) { + this.updatedTransactionCallback.apply(this, pendingUpdates[i]); + } + pendingUpdates = []; + }; + InAppPurchase.prototype.updatedTransactionCallback = function(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt) { + if (!initialized) { + var args = Array.prototype.slice.call(arguments); + pendingUpdates.push(args); + return; + } + if (transactionReceipt) { + this.receiptForProduct[productId] = transactionReceipt; + this.receiptForTransaction[transactionIdentifier] = transactionReceipt; + if (window.localStorage) { + window.localStorage.sk_receiptForProduct = JSON.stringify(this.receiptForProduct); + window.localStorage.sk_receiptForTransaction = JSON.stringify(this.receiptForTransaction); + } + } + switch (state) { + case "PaymentTransactionStatePurchasing": + protectCall(this.options.purchasing, "options.purchasing", productId); + return; - case "PaymentTransactionStateFinished": - protectCall(this.options.finish, "options.finish", transactionIdentifier, productId); - return; - } -}; + case "PaymentTransactionStatePurchased": + protectCall(this.options.purchase, "options.purchase", transactionIdentifier, productId); + return; -InAppPurchase.prototype.restoreCompletedTransactionsFinished = function() { - if (this.needRestoreNotification) delete this.needRestoreNotification; else return; - protectCall(this.options.restoreCompleted, "options.restoreCompleted"); -}; + case "PaymentTransactionStateFailed": + protectCall(this.options.error, "options.error", errorCode, errorText, { + productId: productId + }); + return; -InAppPurchase.prototype.restoreCompletedTransactionsFailed = function(errorCode) { - if (this.needRestoreNotification) delete this.needRestoreNotification; else return; - protectCall(this.options.restoreFailed, "options.restoreFailed", errorCode); -}; + case "PaymentTransactionStateRestored": + protectCall(this.options.restore, "options.restore", transactionIdentifier, productId); + return; -InAppPurchase.prototype.refreshReceipts = function() { - var that = this; - that.appStoreReceipt = null; - var loaded = function(base64) { - that.appStoreReceipt = base64; - protectCall(that.options.receiptsRefreshed, "options.receiptsRefreshed", base64); + case "PaymentTransactionStateFinished": + protectCall(this.options.finish, "options.finish", transactionIdentifier, productId); + return; + } }; - var error = function(errMessage) { - log("refresh receipt failed: " + errMessage); - protectcall(options.error, "options.error", InAppPurchase.prototype.ERR_REFRESH_RECEIPTS, "Failed to refresh receipt: " + errMessage); + InAppPurchase.prototype.restoreCompletedTransactionsFinished = function() { + if (this.needRestoreNotification) delete this.needRestoreNotification; else return; + protectCall(this.options.restoreCompleted, "options.restoreCompleted"); }; - exec("appStoreRefreshReceipt", [], loaded, error); -}; - -InAppPurchase.prototype.loadReceipts = function(callback) { - var that = this; - that.appStoreReceipt = null; - var loaded = function(base64) { - that.appStoreReceipt = base64; - callCallback(); + InAppPurchase.prototype.restoreCompletedTransactionsFailed = function(errorCode) { + if (this.needRestoreNotification) delete this.needRestoreNotification; else return; + protectCall(this.options.restoreFailed, "options.restoreFailed", errorCode); }; - var error = function(errMessage) { - log("load failed: " + errMessage); - protectCall(options.error, "options.error", InAppPurchase.prototype.ERR_LOAD_RECEIPTS, "Failed to load receipt: " + errMessage); + InAppPurchase.prototype.refreshReceipts = function() { + var that = this; + that.appStoreReceipt = null; + var loaded = function(base64) { + that.appStoreReceipt = base64; + protectCall(that.options.receiptsRefreshed, "options.receiptsRefreshed", base64); + }; + var error = function(errMessage) { + log("refresh receipt failed: " + errMessage); + protectCall(that.options.error, "options.error", InAppPurchase.prototype.ERR_REFRESH_RECEIPTS, "Failed to refresh receipt: " + errMessage); + }; + exec("appStoreRefreshReceipt", [], loaded, error); }; - var callCallback = function() { - if (callback) { - protectCall(callback, "loadReceipts.callback", { - appStoreReceipt: that.appStoreReceipt, - forTransaction: function(transactionId) { - return that.receiptForTransaction[transactionId] || null; - }, - forProduct: function(productId) { - return that.receiptForProduct[productId] || null; - } - }); + InAppPurchase.prototype.loadReceipts = function(callback) { + var that = this; + that.appStoreReceipt = null; + var loaded = function(base64) { + that.appStoreReceipt = base64; + callCallback(); + }; + var error = function(errMessage) { + log("load failed: " + errMessage); + protectCall(that.options.error, "options.error", InAppPurchase.prototype.ERR_LOAD_RECEIPTS, "Failed to load receipt: " + errMessage); + }; + function callCallback() { + if (callback) { + protectCall(callback, "loadReceipts.callback", { + appStoreReceipt: that.appStoreReceipt, + forTransaction: function(transactionId) { + return that.receiptForTransaction[transactionId] || null; + }, + forProduct: function(productId) { + return that.receiptForProduct[productId] || null; + } + }); + } } + exec("appStoreReceipt", [], loaded, error); }; - exec("appStoreReceipt", [], loaded, error); -}; - -InAppPurchase.prototype.runQueue = function() { - if (!this.eventQueue.length || !this.onPurchased && !this.onFailed && !this.onRestored) { - return; - } - var args; - var queue = this.eventQueue.slice(); - this.eventQueue = []; - args = queue.shift(); - while (args) { - this.updatedTransactionCallback.apply(this, args); + InAppPurchase.prototype.runQueue = function() { + if (!this.eventQueue.length || !this.onPurchased && !this.onFailed && !this.onRestored) { + return; + } + var args; + var queue = this.eventQueue.slice(); + this.eventQueue = []; args = queue.shift(); - } - if (!this.eventQueue.length) { - this.unWatchQueue(); - } -}; - -InAppPurchase.prototype.watchQueue = function() { - if (this.timer) { - return; - } - this.timer = window.setInterval(function() { - window.storekit.runQueue(); - }, 1e4); -}; - -InAppPurchase.prototype.unWatchQueue = function() { - if (this.timer) { - window.clearInterval(this.timer); - this.timer = null; - } -}; - -InAppPurchase.prototype.eventQueue = []; - -InAppPurchase.prototype.timer = null; - -window.storekit = new InAppPurchase(); + while (args) { + this.updatedTransactionCallback.apply(this, args); + args = queue.shift(); + } + if (!this.eventQueue.length) { + this.unWatchQueue(); + } + }; + InAppPurchase.prototype.watchQueue = function() { + if (this.timer) { + return; + } + this.timer = window.setInterval(function() { + window.storekit.runQueue(); + }, 1e4); + }; + InAppPurchase.prototype.unWatchQueue = function() { + if (this.timer) { + window.clearInterval(this.timer); + this.timer = null; + } + }; + InAppPurchase.prototype.eventQueue = []; + InAppPurchase.prototype.timer = null; + window.storekit = new InAppPurchase(); +})(); (function() { "use strict"; @@ -1055,7 +1040,7 @@ window.storekit = new InAppPurchase(); }); var initialized = false; var initializing = false; - var storekitInit = function() { + function storekitInit() { if (initialized || initializing) return; initializing = true; store.log.debug("ios -> initializing storekit"); @@ -1069,21 +1054,21 @@ window.storekit = new InAppPurchase(); restoreCompleted: storekitRestoreCompleted, restoreFailed: storekitRestoreFailed }, storekitReady, storekitInitFailed); - }; - var storekitReady = function() { + } + function storekitReady() { store.log.info("ios -> storekit ready"); initializing = false; initialized = true; storekitLoad(); - }; - var storekitInitFailed = function() { + } + function storekitInitFailed() { store.log.warn("ios -> storekit init failed"); initializing = false; retry(storekitInit); - }; + } var loaded = false; var loading = false; - var storekitLoad = function() { + function storekitLoad() { if (!initialized) return; if (loaded || loading) return; loading = true; @@ -1091,8 +1076,8 @@ window.storekit = new InAppPurchase(); for (var i = 0; i < store.products.length; ++i) products.push(store.products[i].id); store.log.debug("ios -> loading products"); storekit.load(products, storekitLoaded, storekitLoadFailed); - }; - var storekitLoaded = function(validProducts, invalidProductIds) { + } + function storekitLoaded(validProducts, invalidProductIds) { store.log.debug("ios -> products loaded"); var p; for (var i = 0; i < validProducts.length; ++i) { @@ -1121,13 +1106,13 @@ window.storekit = new InAppPurchase(); storekit.loaded = true; store.ready(true); }, 1); - }; - var storekitLoadFailed = function() { + } + function storekitLoadFailed() { store.log.warn("ios -> loading products failed"); loading = false; retry(storekitLoad); - }; - var storekitPurchasing = function(productId) { + } + function storekitPurchasing(productId) { store.log.debug("ios -> is purchasing " + productId); store.ready(function() { var product = store.get(productId); @@ -1137,8 +1122,8 @@ window.storekit = new InAppPurchase(); } if (product.state !== store.INITIATED) product.set("state", store.INITIATED); }); - }; - var storekitPurchased = function(transactionId, productId) { + } + function storekitPurchased(transactionId, productId) { store.ready(function() { var product = store.get(productId); if (!product) { @@ -1162,8 +1147,8 @@ window.storekit = new InAppPurchase(); store.log.info("ios -> transaction " + transactionId + " purchased (" + product.transactions.length + " in the queue for " + productId + ")"); product.set("state", store.APPROVED); }); - }; - var storekitError = function(errorCode, errorText, options) { + } + function storekitError(errorCode, errorText, options) { var i, p; if (!options) options = {}; store.log.error("ios -> ERROR " + errorCode + ": " + errorText + " - " + JSON.stringify(options)); @@ -1191,7 +1176,7 @@ window.storekit = new InAppPurchase(); code: errorCode, message: errorText }); - }; + } store.when("re-refreshed", function() { storekit.restore(); }); @@ -1202,7 +1187,7 @@ window.storekit = new InAppPurchase(); function storekitRestoreCompleted() { store.log.info("ios -> restore completed"); } - function storekitRestoreFailed(errorCode) { + function storekitRestoreFailed() { store.log.warn("ios -> restore failed"); store.error({ code: store.ERR_REFRESH, @@ -1252,6 +1237,6 @@ window.storekit = new InAppPurchase(); a[i].fn.call(this); } }, false); -}).call(this); +})(); module.exports = store; \ No newline at end of file