From b1a6f184ba6a3d90c46e41d314884f5c5810d8d2 Mon Sep 17 00:00:00 2001 From: Keegan Date: Fri, 11 Mar 2016 02:25:59 +1100 Subject: [PATCH 1/3] Stop exposing config as a reference. resolves #35 --- README.md | 2 +- gulpfile.js | 4 +++- index.js | 3 ++- package.json | 1 + test/spec/index.js | 9 +++++++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e77a613..1283f44 100755 --- a/README.md +++ b/README.md @@ -613,7 +613,7 @@ Pebble.addEventListener('webviewclosed', function(e) { | Property | Type | Description | |----------|------|-------------| -| `.config` | Array | Reference to the config passed to the constructor and used for generating the page. **WARNING** this is a direct reference, not a copy of the config so any modification you make to it, will be reflected on the original as well | +| `.config` | Array | Copy of the config passed to the constructor and used for generating the page. | | `.customFn` | Function | Reference to the custom function passed to the constructor. **WARNING** this is a direct reference, not a copy of the custom function so any modification you make to it, will be reflected on the original as well | | `.meta` | Object | Contains information about the current user and watch. **WARNING** This will only be populated in the `showConfiguration` event handler. (See example above) | | `.meta.activeWatchInfo` | watchinfo\|null | An object containing information on the currently connected Pebble smartwatch or null if unavailable. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getActiveWatchInfo). | diff --git a/gulpfile.js b/gulpfile.js index e2e891a..3bd50d7 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -94,7 +94,9 @@ gulp.task('clay', ['inlineHtml'], function() { .bundle() .pipe(source('clay.js')) .pipe(buffer()) - .pipe(uglify()) + .pipe(uglify({ + preserveComments: 'license' + })) .pipe(insert.prepend(versionMessage)) .pipe(gulp.dest('./dist/')); }); diff --git a/index.js b/index.js index a77ec45..e7402ff 100755 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var configPageHtml = require('./tmp/config-page.html'); var toSource = require('tosource'); var standardComponents = require('./src/scripts/components'); var utils = require('./src/scripts/lib/utils'); +var deepcopy = require('deepcopy/build/deepcopy.min'); /** * @param {Array} config - the Clay config @@ -27,7 +28,7 @@ function Clay(config, customFn, options) { options = options || {}; - self.config = config; + self.config = deepcopy(config); self.customFn = customFn || function() {}; self.components = {}; self.meta = { diff --git a/package.json b/package.json index ae8f2b8..831d2cc 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "browserify-istanbul": "^0.2.1", "chai": "^3.4.1", "deamdify": "^0.2.0", + "deepcopy": "^0.6.1", "del": "^2.0.2", "eslint": "^1.5.1", "eslint-config-pebble": "^1.2.0", diff --git a/test/spec/index.js b/test/spec/index.js index 099e378..7dcf8dd 100644 --- a/test/spec/index.js +++ b/test/spec/index.js @@ -135,6 +135,15 @@ describe('Clay', function() { }); }); + describe('.config', function() { + it('is a copy not a reference', function() { + var config = fixture.config(['input', 'text', 'color']); + var clay = fixture.clay(config); + assert.notStrictEqual(clay.config, config); + assert.deepEqual(clay.config, config); + }); + }); + describe('.registerComponent()', function() { it('adds the component to the this.components', function() { var clay = fixture.clay([]); From e5496c62d6d10984817a1ff47fd033ee5a97f31d Mon Sep 17 00:00:00 2001 From: Keegan Date: Fri, 11 Mar 2016 03:36:00 +1100 Subject: [PATCH 2/3] Add developers to send arbitrary data to the config page --- README.md | 24 ++++++++++++++++++++++- dev/custom-fn.js | 2 ++ dev/dev.js | 3 +++ index.js | 34 ++++++++++++++++++--------------- test/fixture.js | 18 ++++++++++++++---- test/spec/index.js | 47 ++++++++++++++++------------------------------ 6 files changed, 77 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 1283f44..1659445 100755 --- a/README.md +++ b/README.md @@ -609,6 +609,16 @@ Pebble.addEventListener('webviewclosed', function(e) { ### `Clay([Array] config, [function] customFn, [object] options)` +#### Constructor Parameters + +| Parameter | Type | Description | +|----------|-------|-------------| +| `config` | Array | The config that will be used to generate the configuration page | +| `customFn` | Function\|null | (Optional) The [custom function](#custom-function) to be injected into the generated configuration page. | +| `options` | Object | (Optional) See below for properties | +| `options.autoHandleEvents` | Boolean | (Optional) Defaults to `true`. If set to `false`, Clay will not [auto handle the `showConfiguration` and `webviewclosed` events](#handling-the-showconfiguration-and-webviewclosed-events-manually) | +| `options.userData` | Any | (Optional) Any arbitrary data you want to pass to your config page. It will be available in your custom function as `this.meta.userData` | + #### Properties | Property | Type | Description | @@ -619,6 +629,7 @@ Pebble.addEventListener('webviewclosed', function(e) { | `.meta.activeWatchInfo` | watchinfo\|null | An object containing information on the currently connected Pebble smartwatch or null if unavailable. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getActiveWatchInfo). | | `.meta.accountToken` | String | A unique account token that is associated with the Pebble account of the current user. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getAccountToken). | | `.meta.watchToken` | String | A unique token that can be used to identify a Pebble device. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getWatchToken). | +| `.meta.userData` | Any | A deep copy of the arbitrary data provided in the `options.userData`. Defaults to an empty object | #### Methods @@ -650,7 +661,8 @@ Make sure to always wait for the config page to be built before manipulating ite var Clay = require('./clay'); var clayConfig = require('./config'); var customClay = require('./custom-clay'); -var clay = new Clay(clayConfig, customClay); +var userData = {token: 'abc123'} +var clay = new Clay(clayConfig, customClay, {userData: userData}); ``` ##### custom-clay.js @@ -679,6 +691,15 @@ module.exports = function(minified) { if (!clayConfig.meta.activeWatchInfo || clayConfig.meta.activeWatchInfo.platform === 'aplite') { clayConfig.getItemByAppKey('background').hide(); } + + // Set the value of an item based on the userData + $.request('get', 'https://some.cool/api', {token: clayConfig.meta.userData.token}) + .then(function(result) { + // Do something interesting with the data from the server + }) + .error(function(status, statusText, responseText) { + // Handle the error + }); }); }; @@ -701,6 +722,7 @@ This is the main way of talking to your generated config page. An instance of th | `.meta.activeWatchInfo` | watchinfo\|null | An object containing information on the currently connected Pebble smartwatch or null if unavailable. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getActiveWatchInfo). | | `.meta.accountToken` | String | A unique account token that is associated with the Pebble account of the current user. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getAccountToken). | | `.meta.watchToken` | String | A unique token that can be used to identify a Pebble device. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getWatchToken). | +| `.meta.userData` | Any | The data passed in the `options.userData` of the [Clay constructor.](#clayarray-config-function-customfn-object-options) | #### Methods diff --git a/dev/custom-fn.js b/dev/custom-fn.js index 259bc60..fe004aa 100644 --- a/dev/custom-fn.js +++ b/dev/custom-fn.js @@ -21,4 +21,6 @@ module.exports = function() { toggleBackground.call(coolStuffToggle); coolStuffToggle.on('change', toggleBackground); }); + + console.log('userData: ', Clay.meta.userData); }; diff --git a/dev/dev.js b/dev/dev.js index bee8be2..72cccee 100644 --- a/dev/dev.js +++ b/dev/dev.js @@ -5,6 +5,9 @@ window.clayConfig = require('./config.js'); window.claySettings = {}; window.customFn = require('./custom-fn.js'); window.clayComponents = require('../src/scripts/components'); +window.clayMeta = require('../test/fixture').meta({ + userData: {foo: 'bar'} +}); var platform = window.navigator.userAgent.match(/Android/) ? 'android' : 'ios'; document.documentElement.classList.add('platform-' + platform); diff --git a/index.js b/index.js index e7402ff..ebb939b 100755 --- a/index.js +++ b/index.js @@ -11,8 +11,10 @@ var deepcopy = require('deepcopy/build/deepcopy.min'); * @param {function} [customFn] - Custom code to run from the config page. Will run * with the ClayConfig instance as context * @param {Object} [options] - Additional options to pass to Clay - * @param {boolean} [options.autoHandleEvents] - If false, Clay will not + * @param {boolean} [options.autoHandleEvents=true] - If false, Clay will not * automatically handle the 'showConfiguration' and 'webviewclosed' events + * @param {*} [options.userData={}] - Arbitrary data to pass to the config page. Will + * be available os `clayConfig.meta.userData` * @constructor */ function Clay(config, customFn, options) { @@ -34,9 +36,24 @@ function Clay(config, customFn, options) { self.meta = { activeWatchInfo: null, accountToken: '', - watchToken: '' + watchToken: '', + userData: {} }; + /** + * Populate the meta with data from the Pebble object. Make sure to run this inside + * either the "showConfiguration" or "ready" event handler + * @return {void} + */ + function _populateMeta() { + self.meta = { + activeWatchInfo: Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo(), + accountToken: Pebble.getAccountToken(), + watchToken: Pebble.getWatchToken(), + userData: deepcopy(options.userData || {}) + }; + } + // Let Clay handle all the magic if (options.autoHandleEvents !== false && typeof Pebble !== 'undefined') { @@ -63,19 +80,6 @@ function Clay(config, customFn, options) { }); } - /** - * Populate the meta with data from the Pebble object. Make sure to run this inside - * either the "showConfiguration" or "ready" event handler - * @return {void} - */ - function _populateMeta() { - self.meta = { - activeWatchInfo: Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo(), - accountToken: Pebble.getAccountToken(), - watchToken: Pebble.getWatchToken() - }; - } - /** * @private * @param {Clay~ConfigItem|Array} item diff --git a/test/fixture.js b/test/fixture.js index 9eac5d2..088a73c 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -11,12 +11,13 @@ var componentRegistry = require('../src/scripts/lib/component-registry'); var idCounter = 0; /** + * @param {Object} [extra] - add/replace keys in the meta * @returns {{accountToken: string, watchToken: string, activeWatchInfo: {platform: * string, model: string, language: string, firmware: {major: number, minor: - * number, patch: number, suffix: string}}}} + * number, patch: number, suffix: string}}, userData: {}}} */ -module.exports.meta = function() { - return { +module.exports.meta = function(extra) { + var result = { accountToken: '0123456789abcdef0123456789abcdef', watchToken: '0123456789abcdef0123456789abcdef', activeWatchInfo: { @@ -29,8 +30,15 @@ module.exports.meta = function() { patch: 2, suffix: '' } - } + }, + userData: {} }; + + _.eachObj(extra || {}, function(key, val) { + result[key] = val; + }); + + return result; }; /** @@ -106,6 +114,8 @@ module.exports.clayConfig = function(types, build, autoRegister, settings) { * @param {Object} [options] - Additional options to pass to Clay * @param {boolean} [options.autoHandleEvents] - If false, Clay will not * automatically handle the 'showConfiguration' and 'webviewclosed' events + * @param {*} [options.userData={}] - Arbitrary data to pass to the config page. Will + * be available os `clayConfig.meta.userData` * @param {boolean} [destroyLocalStorage=true] * @return {Clay} */ diff --git a/test/spec/index.js b/test/spec/index.js index 7dcf8dd..60c6a76 100644 --- a/test/spec/index.js +++ b/test/spec/index.js @@ -7,31 +7,19 @@ var standardComponents = require('../../src/scripts/components'); var sinon = require('sinon'); var toSource = require('tosource'); -var accountToken = '0123456789abcdef0123456789abcdef'; -var watchToken = '0123456789abcdef0123456789abcdef'; -var activeWatchInfo = { - platform: 'chalk', - model: 'qemu_platform_chalk', - language: 'en_US', - firmware: { - major: 3, - minor: 3, - patch: 2, - suffix: '' - } -}; - /** * @return {void} */ function stubPebble() { + var meta = fixture.meta(); + global.Pebble = { addEventListener: sinon.stub(), openURL: sinon.stub(), sendAppMessage: sinon.stub(), - getActiveWatchInfo: sinon.stub().returns(activeWatchInfo), - getAccountToken: sinon.stub().returns(accountToken), - getWatchToken: sinon.stub().returns(watchToken) + getActiveWatchInfo: sinon.stub().returns(meta.activeWatchInfo), + getAccountToken: sinon.stub().returns(meta.accountToken), + getWatchToken: sinon.stub().returns(meta.watchToken) }; } @@ -310,37 +298,34 @@ describe('Clay', function() { var emptyMeta = { activeWatchInfo: null, accountToken: '', - watchToken: '' + watchToken: '', + userData: {} }; it('populates the meta in the showConfiguration handler', function() { stubPebble(); - var clay = fixture.clay([]); + var userData = {foo: 'bar'}; + var clay = fixture.clay([], null, {userData: userData}); // meta only gets populated after showConfiguration happens assert.deepEqual(clay.meta, emptyMeta); Pebble.addEventListener.withArgs('showConfiguration').callArg(1); - - assert.deepEqual(clay.meta, { - activeWatchInfo: activeWatchInfo, - accountToken: accountToken, - watchToken: watchToken - }); + assert.deepEqual(clay.meta, fixture.meta({userData: userData})); }); it('populates the meta in the ready handler', function() { stubPebble(); - var clay = fixture.clay([], null, {autoHandleEvents: false}); + var userData = {foo: 'bar'}; + var clay = fixture.clay([], null, { + autoHandleEvents: false, + userData: userData + }); // meta only gets populated after ready happens assert.deepEqual(clay.meta, emptyMeta); Pebble.addEventListener.withArgs('ready').callArg(1); - assert.deepEqual(clay.meta, { - activeWatchInfo: activeWatchInfo, - accountToken: accountToken, - watchToken: watchToken - }); + assert.deepEqual(clay.meta, fixture.meta({userData: userData})); }); it('populates the meta with with empty values when there is no Pebble global', From 783bc847796f340397996b7a1ebc565c3b96dbb8 Mon Sep 17 00:00:00 2001 From: Keegan Date: Sat, 12 Mar 2016 10:22:01 +1100 Subject: [PATCH 3/3] fix typo in Clay JSdoc --- index.js | 2 +- test/fixture.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index ebb939b..d99256a 100755 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ var deepcopy = require('deepcopy/build/deepcopy.min'); * @param {boolean} [options.autoHandleEvents=true] - If false, Clay will not * automatically handle the 'showConfiguration' and 'webviewclosed' events * @param {*} [options.userData={}] - Arbitrary data to pass to the config page. Will - * be available os `clayConfig.meta.userData` + * be available as `clayConfig.meta.userData` * @constructor */ function Clay(config, customFn, options) { diff --git a/test/fixture.js b/test/fixture.js index 088a73c..20c9e99 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -115,7 +115,7 @@ module.exports.clayConfig = function(types, build, autoRegister, settings) { * @param {boolean} [options.autoHandleEvents] - If false, Clay will not * automatically handle the 'showConfiguration' and 'webviewclosed' events * @param {*} [options.userData={}] - Arbitrary data to pass to the config page. Will - * be available os `clayConfig.meta.userData` + * be available as `clayConfig.meta.userData` * @param {boolean} [destroyLocalStorage=true] * @return {Clay} */