diff --git a/README.md b/README.md
index 9ff7c1e..9251c93 100755
--- a/README.md
+++ b/README.md
@@ -662,16 +662,27 @@ 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 |
|----------|------|-------------|
-| `.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). |
| `.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
@@ -703,7 +714,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
@@ -732,6 +744,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
+ });
});
};
@@ -754,6 +775,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/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 66f2e04..d1f9e98 100755
--- a/index.js
+++ b/index.js
@@ -3,14 +3,17 @@
var configPageHtml = require('./tmp/config-page.html');
var toSource = require('tosource');
var standardComponents = require('./src/scripts/components');
+var deepcopy = require('deepcopy/build/deepcopy.min');
/**
* @param {Array} config - the Clay config
* @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 as `clayConfig.meta.userData`
* @constructor
*/
function Clay(config, customFn, options) {
@@ -26,15 +29,30 @@ function Clay(config, customFn, options) {
options = options || {};
- self.config = config;
+ self.config = deepcopy(config);
self.customFn = customFn || function() {};
self.components = {};
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') {
@@ -61,19 +79,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/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/src/scripts/lib/clay-item.js b/src/scripts/lib/clay-item.js
index 3d38138..c3ad27f 100644
--- a/src/scripts/lib/clay-item.js
+++ b/src/scripts/lib/clay-item.js
@@ -59,7 +59,7 @@ function ClayItem(config) {
};
// attach event methods
- ClayEvents.call(self, self.$element);
+ ClayEvents.call(self, self.$manipulatorTarget);
// attach the manipulator methods to the clayItem
_.eachObj(_component.manipulator, function(methodName, method) {
diff --git a/src/templates/components/checkboxgroup.tpl b/src/templates/components/checkboxgroup.tpl
index 2a7906b..6f7c15c 100755
--- a/src/templates/components/checkboxgroup.tpl
+++ b/src/templates/components/checkboxgroup.tpl
@@ -8,7 +8,6 @@
type="checkbox"
value="{{this.value}}"
name="clay-{{clayId}}"
- data-manipulator-target
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
/>
diff --git a/src/templates/components/radiogroup.tpl b/src/templates/components/radiogroup.tpl
index 48c15b9..f2e4ba8 100755
--- a/src/templates/components/radiogroup.tpl
+++ b/src/templates/components/radiogroup.tpl
@@ -8,7 +8,6 @@
type="radio"
value="{{this.value}}"
name="clay-{{clayId}}"
- data-manipulator-target
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
/>
diff --git a/test/fixture.js b/test/fixture.js
index 9eac5d2..20c9e99 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 as `clayConfig.meta.userData`
* @param {boolean} [destroyLocalStorage=true]
* @return {Clay}
*/
diff --git a/test/spec/components/index.js b/test/spec/components/index.js
index b916c45..f077c57 100644
--- a/test/spec/components/index.js
+++ b/test/spec/components/index.js
@@ -7,6 +7,7 @@ var HTML = require('../../../src/scripts/vendor/minified').HTML;
var components = require('../../../src/scripts/components');
var manipulators = require('../../../src/scripts/lib/manipulators');
var fixture = require('../../fixture');
+var sinon = require('sinon');
var componentSchema = Joi.object().keys({
name: Joi.string().required(),
@@ -39,6 +40,20 @@ describe('components', function() {
it('is able to be passed to ClayConfig', function() {
fixture.clayConfig([component.name]);
});
+
+ it('only has one $manipulatorTarget', function() {
+ var configItem = fixture.clayConfig([component.name]).getAllItems()[0];
+ assert.strictEqual(configItem.$manipulatorTarget.length, 1);
+ });
+
+ it('only dispatches change events once', function() {
+ var configItem = fixture.clayConfig([component.name]).getAllItems()[0];
+ var handler = sinon.spy();
+ configItem.on('change', handler);
+ configItem.trigger('change');
+ assert.strictEqual(handler.callCount, 1);
+ });
+
});
});
});
diff --git a/test/spec/index.js b/test/spec/index.js
index f868064..93e0a3c 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)
};
}
@@ -135,6 +123,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([]);
@@ -323,37 +320,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',