diff --git a/.eslintignore b/.eslintignore
index 37fbf5d..1a5d6de 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,6 @@
coverage/
dist/
tmp/
+build/
src/scripts/vendor/
src/js/index.js
diff --git a/README.md b/README.md
index e64b421..7c81878 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Clay
Clay is a JavaScript library that makes it easy to add offline configuration pages to your Pebble apps. All you need to get started is a couple lines of JavaScript and a JSON file; no servers or HTML required.
-Clay will by default automatically handle the 'showConfiguration' and 'webviewclosed' events traditionally implemented by developers to relay configuration settings to the watch side of the app. This step is not required when using Clay, since each config item is given the same `appKey` as defined in `appinfo.json` (or PebbleKit JS Message Keys on CloudPebble), and is automatically transmitted once the configuration page is submitted by the user. Developers can override this behavior by [handling the events manually](#handling-the-showconfiguration-and-webviewclosed-events-manually).
+Clay will by default automatically handle the 'showConfiguration' and 'webviewclosed' events traditionally implemented by developers to relay configuration settings to the watch side of the app. This step is not required when using Clay, since each config item is given the same `messageKey` as defined in `appinfo.json` (or PebbleKit JS Message Keys on CloudPebble), and is automatically transmitted once the configuration page is submitted by the user. Developers can override this behavior by [handling the events manually](#handling-the-showconfiguration-and-webviewclosed-events-manually).
**Clay is still in early development and may be missing some features. We would love your feedback! Please submit any ideas or features via GitHub Issues.**
@@ -33,6 +33,7 @@ Clay is distributed as a [Pebble package](https://developer.pebble.com/guides/pe
```
5. Ensure `pebble.enableMultiJS` is set to true in your `package.json`.
6. Next is the fun part - creating your config page. Edit your `config.json` file to build a layout of elements as described in the sections below.
+7. Make sure you have defined all of your `messageKeys` in your `package.json`. More info on how that works [here.](https://developer.pebble.com/guides/communication/using-pebblekit-js/#defining-keys)
# Getting Started (CloudPebble)
@@ -54,6 +55,7 @@ NOTE these are similar to using the SDK but instead of a data file called config
var clay = new Clay(clayConfig);
```
6. Next is the fun part - creating your config page. Edit your `config.js` file to build a layout of elements as described in the sections below.
+7. Make sure you have defined all of your message keys using `Automatic assignment` in your project settings. More info on how that works [here.](https://developer.pebble.com/guides/communication/using-pebblekit-js/#defining-keys)
# Creating Your Config File
@@ -108,12 +110,12 @@ Sections help divide up the page into logical groups of items. It is recommended
},
{
"type": "input",
- "appKey": "email",
+ "messageKey": "email",
"label": "Email Address"
},
{
"type": "toggle",
- "appKey": "enableAnimations",
+ "messageKey": "enableAnimations",
"label": "Enable Animations"
}
]
@@ -134,7 +136,7 @@ Headings can be used in anywhere and can have their size adjusted to suit the co
|----------|------|-------------|
| type | string | Set to `heading`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| defaultValue | string/HTML | The heading's text. |
| size | int | Defaults to `4`. An integer from 1 to 6 where 1 is the largest size and 6 is the smallest. (represents HTML `
`, `
`, `
`, etc). |
| capabilities | array | Array of features that the connected watch must have for this item to be present |
@@ -165,7 +167,7 @@ Text is used to provide descriptions of sections or to explain complex parts of
|----------|------|-------------|
| type | string | Set to `text`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| defaultValue | string/HTML | The content of the text element. |
| capabilities | array | Array of features that the connected watch must have for this item to be present |
@@ -193,7 +195,7 @@ Standard text input field.
|----------|------|-------------|
| type | string | Set to `input`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| label | string | The label that should appear next to this item. |
| defaultValue | string | The default value of the input field. |
| description | string | Optional sub-text to include below the component |
@@ -206,7 +208,7 @@ Standard text input field.
```javascript
{
"type": "input",
- "appKey": "email",
+ "messageKey": "email",
"defaultValue": "",
"label": "Email Address",
"attributes": {
@@ -231,7 +233,7 @@ Switch for a single item.
|----------|------|-------------|
| type | string | Set to `toggle`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| label | string | The label that should appear next to this item. |
| defaultValue | int\|boolean | The default value of the toggle. Defaults to `false` if not specified. |
| description | string | Optional sub-text to include below the component |
@@ -243,7 +245,7 @@ Switch for a single item.
```javascript
{
"type": "toggle",
- "appKey": "invert",
+ "messageKey": "invert",
"label": "Invert Colors",
"defaultValue": true
}
@@ -263,7 +265,7 @@ A dropdown menu containing multiple options.
|----------|------|-------------|
| type | string | Set to `select`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| label | string | The label that should appear next to this item. |
| defaultValue | string | The default value of the dropdown menu. Must match a value in the `options` array. |
| description | string | Optional sub-text to include below the component |
@@ -275,7 +277,7 @@ A dropdown menu containing multiple options.
```javascript
{
"type": "select",
- "appKey": "flavor",
+ "messageKey": "flavor",
"defaultValue": "grape",
"label": "Favorite Flavor",
"options": [
@@ -304,7 +306,7 @@ If you wish to use optgroups, then use the following format:
```javascript
{
"type": "select",
- "appKey": "flavor",
+ "messageKey": "flavor",
"defaultValue": "grape",
"label": "Favorite Flavor",
"options": [
@@ -369,7 +371,7 @@ The color picker will automatically show a different layout depending on the wat
|----------|------|-------------|
| type | string | Set to `color`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| label | string | The label that should appear next to this item. |
| defaultValue | string OR int | The default color. One of the [64 colors](https://developer.pebble.com/guides/tools-and-resources/color-picker/) compatible with Pebble smartwatches. Always use the uncorrected value even if `sunlight` is true. The component will do the conversion internally. |
| description | string | Optional sub-text to include below the component |
@@ -383,7 +385,7 @@ The color picker will automatically show a different layout depending on the wat
```javascript
{
"type": "color",
- "appKey": "background",
+ "messageKey": "background",
"defaultValue": "ff0000",
"label": "Background Color",
"sunlight": true,
@@ -400,7 +402,7 @@ The color picker will automatically show a different layout depending on the wat
```javascript
{
"type": "color",
- "appKey": "background",
+ "messageKey": "background",
"defaultValue": "ffffff",
"label": "Background Color",
"sunlight": false,
@@ -413,7 +415,7 @@ The color picker will automatically show a different layout depending on the wat
```javascript
{
"type": "color",
- "appKey": "background",
+ "messageKey": "background",
"defaultValue": "aaaaaa",
"label": "Background Color",
"sunlight": false,
@@ -435,7 +437,7 @@ A list of options allowing the user can only choose one option to submit.
|----------|------|-------------|
| type | string | Set to `radiogroup`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
| label | string | The label that should appear next to this item. |
| defaultValue | string | The default selected item. Must match a value in the `options` array. |
| description | string | Optional sub-text to include below the component |
@@ -447,7 +449,7 @@ A list of options allowing the user can only choose one option to submit.
```javascript
{
"type": "radiogroup",
- "appKey": "favorite_food",
+ "messageKey": "favorite_food",
"label": "Favorite Food",
"options": [
{
@@ -480,11 +482,11 @@ A list of options where a user may choose more than one option to submit.
|----------|------|-------------|
| type | string | Set to `checkboxgroup`. |
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
-| appKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
+| messageKey | string (unique) | The AppMessage key matching the `messageKey` item defined in your `package.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByMessageKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. **NOTE:** The checkboxgroup component will expect you to have defined a `messageKey` in your `package.json` using array syntax. In the example below, the matching entry in the `package.json` would be `favorite_food[3]`. In CloudPebble, you must use `Automatic assignment` for message keys and the `Key Array Length` must match the number of options in the checkboxgroup |
| label | string | The label that should appear next to this item. |
-| defaultValue | array of strings | The default selected items. Each value must match one in the `options` array. |
+| defaultValue | array of booleans | The default selected items. |
| description | string | Optional sub-text to include below the component |
-| options | array of objects | The options you want to appear in the checkbox group. Each option is an object with a `label` and `value` property. |
+| options | array of strings | The labels for each checkbox you want to appear in the checkbox group. |
| capabilities | array | Array of features that the connected watch must have for this item to be present |
##### Example
@@ -492,26 +494,15 @@ A list of options where a user may choose more than one option to submit.
```javascript
{
"type": "checkboxgroup",
- "appKey": "favorite_food",
+ "messageKey": "favorite_food",
"label": "Favorite Food",
- "defaultValue": ["sushi", "burgers"],
- "options": [
- {
- "label": "Sushi",
- "value": "sushi"
- },
- {
- "label": "Pizza",
- "value": "pizza"
- },
- {
- "label": "Burgers",
- "value": "burgers"
- }
- ]
+ "defaultValue": [true, false, true],
+ "options": ["Sushi", "Pizza", "Burgers"]
}
```
+In the above example, Sushi and Burgers will be selected by default.
+
---
### Generic Button
@@ -571,7 +562,7 @@ you must just remember to divide the received value on the watch accordingly.
```javascript
{
"type": "slider",
- "appKey": "slider",
+ "messageKey": "slider",
"defaultValue": 15,
"label": "Slider",
"description": "This is the description for the slider",
@@ -608,11 +599,38 @@ The submit button for the page. You **MUST** include this component somewhere in
---
-### Coming Soon
+### Message keys and array syntax
-- Tabs
-- Footer
-- Dynamic + draggable list
+Clay supports (and requires in some cases) the use of array syntax for message keys. Also known as `Automatic assignment` in CloudPebble. More info [here.](https://developer.pebble.com/guides/communication/using-pebblekit-js/#defining-keys)
+
+You can assign any component that does not return an array to a particular position in your message key. This is done by appending the position (zero indexed) wrapped in brackets to the end of the `messageKey` property. Do **NOT** include any spaces in your `messageKey`.
+
+##### Example
+
+```javascript
+{
+ "type": "toggle",
+ "messageKey": "light_switch[1]",
+ "label": "Invert Colors",
+ "defaultValue": true
+}
+```
+In the above example, the value of the item will be accessible with `MESSAGE_KEY_light_switch + 1` in your C code.
+
+Components that return an array in their `.get()` method, such as the checkboxgroup will expect the corresponding message key to already be defined with the correct length. You **CAN NOT** use the array syntax in the `messageKey` property as this would be trying to create a 2 dimensional array.
+
+##### Example
+
+```javascript
+{
+ "type": "checkboxgroup",
+ "messageKey": "favorite_food",
+ "label": "Favorite Food",
+ "defaultValue": [true, false, true],
+ "options": ["Sushi", "Pizza", "Burgers"]
+}
+```
+In the above example, the value of the item will be accessible with `MESSAGE_KEY_favorite_food`, `MESSAGE_KEY_favorite_food + 1`, and `MESSAGE_KEY_favorite_food + 2` in your C code.
---
### Showing items for specific platforms and features
@@ -627,9 +645,9 @@ You can also prefix the capability with `NOT_` to negate the capability. Eg: `NO
will only be included in the page if the device does **NOT** support health.
**Warning:** Items that do not satisfy the capabilities will not be included in the page
-at all. You will not be able to use methods like `clayConfig.getItemByAppKey()` to
+at all. You will not be able to use methods like `clayConfig.getItemByMessageKey()` to
obtain a reference to them. However, this does mean that you will be able to have
-multiple items with the same `appKey`as long as they do not both satisfy the
+multiple items with the same `messageKey`as long as they do not both satisfy the
same conditions.
##### Examples
@@ -682,7 +700,7 @@ Each component has a **manipulator**. This is a set of methods used to talk to t
At a minimum, manipulators must have a `.get()` and `.set(value)` method however there are also methods to assist in interactivity such as `.hide()` and `.disable()`.
**NOTE:** There is currently no way to disable or hide an entire section. You must disable/hide each item in the section to achieve this effect.
-When the config page is closed, the `.get()` method is run on all components registered with an `appKey` to construct the object sent to the C app.
+When the config page is closed, the `.get()` method is run on all components registered with an `messageKey` to construct the object sent to the C app.
Many of these methods fire an event when the method is called. You can listen for these events with `ClayItem.on()`.
**NOTE** These events will only be fired if the state actually changes.
@@ -757,7 +775,7 @@ Eg: If you run the `.show()` manipulator on an item that is already visible, the
| Method | Returns | Event Fired | Description |
|--------|---------|-------------| ------------|
| `.set( [array] value)` | `ClayItem` | `change` | Checks the checkboxes that corresponds to the provided list of values. |
-| `.get()` | `Array.` | | Gets an array of strings representing the list of the values of the checked items. **NOTE:** each item in the array will be separated by a zero when sent to the watch. See [`Clay.getSettings()`](#methods) |
+| `.get()` | `Array.` | | Gets an array of booleans representing the list the checked items. **NOTE:** each item in the array will be converted to an `int` when sent to the watch. See [`Clay.getSettings()`](#methods) |
| `.disable()` | `ClayItem` | `disabled` | Prevents this item from being edited by the user. |
| `.enable()` | `ClayItem` | `enabled` | Allows this item to be edited by the user. |
| `.hide()` | `ClayItem` | `hide` | Hides the item |
@@ -780,7 +798,7 @@ Clay is built to allow developers to add their own basic interactivity to the co
## Handling The 'showConfiguration' and 'webviewclosed' Events Manually
-Clay will by default, automatically handle the 'showConfiguration' and 'webviewclosed' events. This allows the `appKey` of each config item to be automatically delivered to the `InboxReceivedHandler` on the watch side. If you wish to override this behavior and handle the events yourself, pass an object as the 3rd parameter of the Clay constructor with `autoHandleEvents` set to `false`.
+Clay will by default, automatically handle the 'showConfiguration' and 'webviewclosed' events. This allows the `messageKey` of each config item to be automatically delivered to the `InboxReceivedHandler` on the watch side. If you wish to override this behavior and handle the events yourself, pass an object as the 3rd parameter of the Clay constructor with `autoHandleEvents` set to `false`.
Example:
@@ -898,7 +916,7 @@ cssColor(Settings.option('BACKGROUND_COLOR') || 0xff0000);
| `Clay( [array] config, [function] customFn=null, [object] options={autoHandleEvents: true})` `config` - an Array representing your config `customFn` - function to be run in the context of the generated page `options.autoHandleEvents` - set to `false` to prevent Clay from automatically handling the "showConfiguration" and "webviewclosed" events | `Clay` - a new instance of Clay. |
| `.registerComponent( [ClayComponent] component )` Registers a custom component. | `void`. |
| `.generateUrl()` | `string` - The URL to open with `Pebble.openURL()` to use the Clay-generated config page. |
-| `.getSettings( [object] response, [boolean] convert=true)` `response` - the response object provided to the "webviewclosed" event `convert` - Pass `false` to not convert the settings to be compatible with `Pebble.sendAppMessage()` | `Object` - object of keys and values for each config page item with an `appKey`, where the key is the `appKey` and the value is the chosen value of that item. This method will do some conversions depending on the type of the setting. Arrays containing strings will have zeros inserted before each item. eg `['one', 'two']` becomes `['one', 0, 'two', 0]`. Booleans will be converted to numbers. eg `true` becomes `1` and `false` becomes `0`. Pass `false` as the second parameter to disable this behavior |
+| `.getSettings( [object] response, [boolean] convert=true)` `response` - the response object provided to the "webviewclosed" event `convert` - Pass `false` to not convert the settings to be compatible with `Pebble.sendAppMessage()` | `Object` - object of keys and values for each config page item with an `messageKey`, where the key is the `messageKey` and the value is the chosen value of that item. This method will do some conversions depending on the type of the setting. Arrays containing strings will have zeros inserted before each item. eg `['one', 'two']` becomes `['one', 0, 'two', 0]`. Booleans will be converted to numbers. eg `true` becomes `1` and `false` becomes `0`. Pass `false` as the second parameter to disable this behavior |
---
@@ -936,20 +954,20 @@ module.exports = function(minified) {
function toggleBackground() {
if (this.get()) {
- clayConfig.getItemByAppKey('background').enable();
+ clayConfig.getItemByMessageKey('background').enable();
} else {
- clayConfig.getItemByAppKey('background').disable();
+ clayConfig.getItemByMessageKey('background').disable();
}
}
clayConfig.on(clayConfig.EVENTS.AFTER_BUILD, function() {
- var coolStuffToggle = clayConfig.getItemByAppKey('cool_stuff');
+ var coolStuffToggle = clayConfig.getItemByMessageKey('cool_stuff');
toggleBackground.call(coolStuffToggle);
coolStuffToggle.on('change', toggleBackground);
// Hide the color picker for aplite
if (!clayConfig.meta.activeWatchInfo || clayConfig.meta.activeWatchInfo.platform === 'aplite') {
- clayConfig.getItemByAppKey('background').hide();
+ clayConfig.getItemByMessageKey('background').hide();
}
// Set the value of an item based on the userData
@@ -992,10 +1010,10 @@ This is the main way of talking to your generated config page. An instance of th
| Method | Returns |
|--------|---------|
| `.getAllItems()` | `Array.` - an array of all config items. |
-| `.getItemByAppKey( [string] appKey )` | `ConfigItem\|undefined` - a single `ConfigItem` that has the provided `appKey`, otherwise `undefined`. |
+| `.getItemByMessageKey( [string] messageKey )` | `ConfigItem\|undefined` - a single `ConfigItem` that has the provided `messageKey`, otherwise `undefined`. |
| `.getItemById( [string] id )` | `ConfigItem\|undefined` - a single `ConfigItem` that has the provided `id`, otherwise `undefined`. |
| `.getItemsByType( [string] type )` | `Array.` - an array of config items that match the provided `type`. |
-| `.serialize()` | `Object` - an object representing all items with an `appKey` where the key is the `appKey` and the value is an object with the `value` property set to the result of running `.get()` on the Clay item. If the Clay item has a `precision` property set, it is included in the object |
+| `.serialize()` | `Object` - an object representing all items with an `messageKey` where the key is the `messageKey` and the value is an object with the `value` property set to the result of running `.get()` on the Clay item. If the Clay item has a `precision` property set, it is included in the object |
| `.build()` Builds the config page. Will dispatch the `BEFORE_BUILD` event prior to building the page, then the `AFTER_BUILD` event once it is complete. If the config page has already been built, then the `ClayConfig.destroy()` method will be executed prior to building the page again. | `ClayConfig` |
| `.destroy()` Destroys the config page. Will dispatch the `BEFORE_DESTROY` event prior to destroying the page, then the `AFTER_DESTROY` event once it is complete. This method wipes the config page completely, including all existing items. You will need to make sure that you re-attach your event handlers for any items that are replaced | `ClayConfig` |
| `.on( [string] events, [function] handler )` Register an event to the provided handler. The handler will be called with this instance of `ClayConfig` as the context. If you wish to register multiple events to the same handler, then separate the events with a space | `ClayConfig` |
@@ -1012,7 +1030,7 @@ This is the main way of talking to your generated config page. An instance of th
| Property | Type | Description |
|----------|------|-------------|
| `.id` | String | The ID of the item if provided in the config. |
-| `.appKey` | String | The ID of the item if provided in the config. |
+| `.messageKey` | String | The ID of the item if provided in the config. |
| `.config` | Object | Reference to the config passed to the constructer. |
| `$element` | $Minified | A Minified list representing the root HTML element of the config item. |
| `$manipulatorTarget` | $Minified | A Minified list representing the HTML element with **data-manipulator-target** set. This is generally pointing to the main `` element and will be used for binding events. |
@@ -1140,4 +1158,4 @@ While developing components and other functionality for Clay, it is much easier
Most of the magic happens in the `src/scripts/lib` directory. `config-page.js` initializes a new instance of `ClayConfig` and calls the injected custom function (`window.customFn`) with the `ClayConfig` as its context. This allows developers to add extra functionality to the config page, such as setting values of items dynamically or registering small custom components.
-Once the `ClayConfig` is initialized, we run the `.build()` method. This iterates over the config and injects each item into the page. Each item is an instance of `ClayItem`. It also indexes the items to later be retrieved with `.getAllItems()`, `.getItemByAppKey()`, `.getItemById()`, `.getItemsByType()`.
+Once the `ClayConfig` is initialized, we run the `.build()` method. This iterates over the config and injects each item into the page. Each item is an instance of `ClayItem`. It also indexes the items to later be retrieved with `.getAllItems()`, `.getItemByMessageKey()`, `.getItemById()`, `.getItemsByType()`.
diff --git a/dev/config-2.js b/dev/config-2.js
index 37d60e4..e06c08a 100644
--- a/dev/config-2.js
+++ b/dev/config-2.js
@@ -21,7 +21,7 @@ module.exports = [
},
{
"type": "input",
- "appKey": "email",
+ "messageKey": "email",
"defaultValue": "",
"label": "Input Field",
"description": "This is a new field",
diff --git a/dev/config.js b/dev/config.js
index 96e3033..c98ef72 100644
--- a/dev/config.js
+++ b/dev/config.js
@@ -38,7 +38,7 @@ module.exports = [
},
{
"type": "input",
- "appKey": "email",
+ "messageKey": "email",
"defaultValue": "",
"label": "Input Field",
"description": "This is a description for the input component. " +
@@ -63,7 +63,7 @@ module.exports = [
},
{
"type": "slider",
- "appKey": "slider",
+ "messageKey": "slider",
"defaultValue": 15,
"label": "Slider",
"description": "This is the description for the slider",
@@ -73,20 +73,32 @@ module.exports = [
},
{
"type": "toggle",
- "appKey": "cool_stuff",
+ "messageKey": "cool_stuff",
+ "label": "This is a Toggle",
+ "defaultValue": false
+ },
+ {
+ "type": "toggle",
+ "messageKey": "arrayToggle[0]",
+ "label": "This is a Toggle",
+ "defaultValue": false
+ },
+ {
+ "type": "toggle",
+ "messageKey": "arrayToggle[1]",
"label": "This is a Toggle",
"defaultValue": false
},
{
"type": "color",
- "appKey": "colorTest",
+ "messageKey": "colorTest",
"defaultValue": "FF0000",
"label": "Standard Color Picker",
"sunlight": false
},
{
"type": "color",
- "appKey": "sunnyColorTest",
+ "messageKey": "sunnyColorTest",
"defaultValue": "00FF00",
"label": "Sunny Color Picker",
"sunlight": true
@@ -110,7 +122,7 @@ module.exports = [
},
{
"type": "radiogroup",
- "appKey": "radiogroup-test",
+ "messageKey": "radiogroup-test",
"label": "Radio Group",
"options": [
{ "label": "Test thing", "value": "one" },
@@ -120,18 +132,14 @@ module.exports = [
},
{
"type": "checkboxgroup",
- "appKey": "checkboxgroup-test",
- "defaultValue": ["quote' \"test", "two"],
+ "messageKey": "checkboxgroup-test",
+ "defaultValue": [0, 1, 0],
"label": "Checkbox Group",
- "options": [
- { "label": "First thing", "value": "three" },
- { "label": "Another thing", "value": "two" },
- { "label": "Final thing", "value": "three" }
- ]
+ "options": [ "First thing", "Another thing", "Final thing" ]
},
{
"type": "select",
- "appKey": "flavor",
+ "messageKey": "flavor",
"defaultValue": "grape",
"label": "Select Menu",
"options": [
diff --git a/dev/custom-fn.js b/dev/custom-fn.js
index d6202a4..f8d25dc 100644
--- a/dev/custom-fn.js
+++ b/dev/custom-fn.js
@@ -10,9 +10,9 @@ module.exports = function() {
*/
function toggleBackground() {
if (this.get()) {
- Clay.getItemByAppKey('colorTest').enable();
+ Clay.getItemByMessageKey('colorTest').enable();
} else {
- Clay.getItemByAppKey('colorTest').disable();
+ Clay.getItemByMessageKey('colorTest').disable();
}
}
@@ -25,7 +25,7 @@ module.exports = function() {
}
Clay.on(Clay.EVENTS.AFTER_BUILD, function() {
- var coolStuffToggle = Clay.getItemByAppKey('cool_stuff');
+ var coolStuffToggle = Clay.getItemByMessageKey('cool_stuff');
toggleBackground.call(coolStuffToggle);
coolStuffToggle.on('change', toggleBackground);
diff --git a/gulpfile.js b/gulpfile.js
index 80cec06..1130469 100755
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -15,6 +15,7 @@ var uglify = require('gulp-uglify');
var sassify = require('sassify');
var autoprefixify = require('./src/scripts/vendor/autoprefixify');
var insert = require('gulp-insert');
+var clayPackage = require('./package.json');
var sassIncludePaths = [].concat(
require('bourbon').includePaths,
@@ -37,7 +38,7 @@ var autoprefixerOptions = {
var stringifyOptions = ['.html', '.tpl'];
var versionMessage = '/* Clay - https://github.com/pebble/clay - Version: ' +
- require('./package.json').version +
+ clayPackage.version +
' - Build Date: ' + new Date().toISOString() + ' */\n';
gulp.task('clean-js', function() {
@@ -84,13 +85,14 @@ gulp.task('inlineHtml', ['js', 'sass'], function() {
gulp.task('clay', ['inlineHtml'], function() {
return browserify('index.js', {
debug: false,
- standalone: 'clay'
+ standalone: clayPackage.name
})
.transform('deamdify')
.transform(stringify(stringifyOptions))
.transform(sassify, sassifyOptions)
.transform(autoprefixify, autoprefixerOptions)
- .require(require.resolve('./index'), {expose: 'pebble-clay'})
+ .require(require.resolve('./index'), {expose: clayPackage.name})
+ .exclude('message_keys')
.bundle()
.pipe(source('index.js'))
.pipe(buffer())
diff --git a/index.js b/index.js
index 7838ae2..d223733 100755
--- a/index.js
+++ b/index.js
@@ -5,6 +5,7 @@ var toSource = require('tosource');
var standardComponents = require('./src/scripts/components');
var deepcopy = require('deepcopy/build/deepcopy.min');
var version = require('./package.json').version;
+var messageKeys = require('message_keys');
/**
* @param {Array} config - the Clay config
@@ -82,23 +83,50 @@ function Clay(config, customFn, options) {
}
/**
+ * If this function returns true then the callback will be executed
+ * @callback _scanConfig_testFn
+ * @param {Clay~ConfigItem} item
+ */
+
+ /**
+ * @callback _scanConfig_callback
+ * @param {Clay~ConfigItem} item
+ */
+
+ /**
+ * Scan over the config and run the callback if the testFn resolves to true
* @private
* @param {Clay~ConfigItem|Array} item
+ * @param {_scanConfig_testFn} testFn
+ * @param {_scanConfig_callback} callback
* @return {void}
*/
- function _registerStandardComponents(item) {
+ function _scanConfig(item, testFn, callback) {
if (Array.isArray(item)) {
item.forEach(function(item) {
- _registerStandardComponents(item);
+ _scanConfig(item, testFn, callback);
});
} else if (item.type === 'section') {
- _registerStandardComponents(item.items);
- } else if (standardComponents[item.type]) {
- self.registerComponent(standardComponents[item.type]);
+ _scanConfig(item.items, testFn, callback);
+ } else if (testFn(item)) {
+ callback(item);
}
}
- _registerStandardComponents(self.config);
+ // register standard components
+ _scanConfig(self.config, function(item) {
+ return standardComponents[item.type];
+ }, function(item) {
+ self.registerComponent(standardComponents[item.type]);
+ });
+
+ // validate config against teh use of appKeys
+ _scanConfig(self.config, function(item) {
+ return item.appKey;
+ }, function() {
+ throw new Error('appKeys are no longer supported. ' +
+ 'Please follow the migration guide to upgrade your project');
+ });
}
/**
@@ -182,7 +210,9 @@ Clay.prototype.getSettings = function(response, convert) {
localStorage.setItem('clay-settings', JSON.stringify(settingsStorage));
- return convert === false ? settings : Clay.prepareSettingsForAppMessage(settings);
+ return convert === false ?
+ settings :
+ Clay.prepareSettingsForAppMessage(settings, messageKeys);
};
/**
@@ -214,7 +244,7 @@ Clay.encodeDataUri = function(input, prefix) {
* Eg: 1.4567 with a precision set to 3 will become 1456
* @param {number|string|boolean|Array|Object} val
* @param {number|string|boolean|Array} val.value
- * @param {number} [val.precision=0]
+ * @param {number|undefined} [val.precision=0]
* @returns {number|string|Array}
*/
Clay.prepareForAppMessage = function(val) {
@@ -235,12 +265,8 @@ Clay.prepareForAppMessage = function(val) {
if (Array.isArray(val)) {
result = [];
- val.forEach(function(item) {
- var itemConverted = Clay.prepareForAppMessage(item);
- result.push(itemConverted);
- if (typeof itemConverted === 'string') {
- result.push(0);
- }
+ val.forEach(function(item, index) {
+ result[index] = Clay.prepareForAppMessage(item);
});
} else if (typeof val === 'object' && val) {
if (typeof val.value === 'number') {
@@ -266,16 +292,56 @@ Clay.prepareForAppMessage = function(val) {
/**
* Converts a Clay settings dict into one that is compatible with
- * Pebble.sendAppMessage();
+ * Pebble.sendAppMessage(); It also uses the provided messageKeys to correctly
+ * assign arrays into individual keys
* @see {prepareForAppMessage}
* @param {Object} settings
* @returns {{}}
*/
Clay.prepareSettingsForAppMessage = function(settings) {
- var result = {};
+
+ // flatten settings
+ var flatSettings = {};
Object.keys(settings).forEach(function(key) {
- result[key] = Clay.prepareForAppMessage(settings[key]);
+ var val = settings[key];
+ var matches = key.match(/(.+?)(?:\[(\d*)\])?$/);
+
+ if (!matches[2]) {
+ flatSettings[key] = val;
+ return;
+ }
+
+ var position = parseInt(matches[2], 10);
+ key = matches[1];
+
+ if (typeof flatSettings[key] === 'undefined') {
+ flatSettings[key] = [];
+ }
+
+ flatSettings[key][position] = val;
});
+
+ var result = {};
+ Object.keys(flatSettings).forEach(function(key) {
+ var messageKey = messageKeys[key];
+ var settingArr = Clay.prepareForAppMessage(flatSettings[key]);
+ settingArr = Array.isArray(settingArr) ? settingArr : [settingArr];
+
+ settingArr.forEach(function(setting, index) {
+ result[messageKey + index] = setting;
+ });
+ });
+
+ // validate the settings
+ Object.keys(result).forEach(function(key) {
+ if (Array.isArray(result[key])) {
+ throw new Error('Clay does not support 2 dimensional arrays for item ' +
+ 'values. Make sure you are not attempting to use array ' +
+ 'syntax (eg: "myMessageKey[2]") in the messageKey for ' +
+ 'components that return an array, such as a checkboxgroup');
+ }
+ });
+
return result;
};
diff --git a/src/scripts/components/checkboxgroup.js b/src/scripts/components/checkboxgroup.js
index b894745..8c999c4 100644
--- a/src/scripts/components/checkboxgroup.js
+++ b/src/scripts/components/checkboxgroup.js
@@ -8,7 +8,6 @@ module.exports = {
defaults: {
label: '',
options: [],
- description: '',
- attributes: {}
+ description: ''
}
};
diff --git a/src/scripts/lib/clay-config.js b/src/scripts/lib/clay-config.js
index 99a17e3..3de7b55 100644
--- a/src/scripts/lib/clay-config.js
+++ b/src/scripts/lib/clay-config.js
@@ -5,7 +5,7 @@
* @typedef {Object} Clay~ConfigItem
* @property {string} type
* @property {string|boolean|number} defaultValue
- * @property {string} [appKey]
+ * @property {string} [messageKey]
* @property {string} [id]
* @property {string} [label]
* @property {Object} [attributes]
@@ -36,7 +36,7 @@ function ClayConfig(settings, config, $rootContainer, meta) {
var _settings = _.copyObj(settings);
var _items;
var _itemsById;
- var _itemsByAppKey;
+ var _itemsByMessageKey;
var _isBuilt;
/**
@@ -47,7 +47,7 @@ function ClayConfig(settings, config, $rootContainer, meta) {
function _initializeItems() {
_items = [];
_itemsById = {};
- _itemsByAppKey = {};
+ _itemsByMessageKey = {};
_isBuilt = false;
}
@@ -77,15 +77,15 @@ function ClayConfig(settings, config, $rootContainer, meta) {
_itemsById[_item.id] = clayItem;
}
- if (_item.appKey) {
- _itemsByAppKey[_item.appKey] = clayItem;
+ if (_item.messageKey) {
+ _itemsByMessageKey[_item.messageKey] = clayItem;
}
_items.push(clayItem);
// set the value of the item via the manipulator to ensure consistency
- var value = typeof _settings[_item.appKey] !== 'undefined' ?
- _settings[_item.appKey] :
+ var value = typeof _settings[_item.messageKey] !== 'undefined' ?
+ _settings[_item.messageKey] :
(_item.defaultValue || '');
clayItem.set(value);
@@ -154,12 +154,12 @@ function ClayConfig(settings, config, $rootContainer, meta) {
};
/**
- * @param {string} appKey
+ * @param {string} messageKey
* @returns {ClayItem}
*/
- self.getItemByAppKey = function(appKey) {
- _checkBuilt('getItemByAppKey');
- return _itemsByAppKey[appKey];
+ self.getItemByMessageKey = function(messageKey) {
+ _checkBuilt('getItemByMessageKey');
+ return _itemsByMessageKey[messageKey];
};
/**
@@ -190,13 +190,13 @@ function ClayConfig(settings, config, $rootContainer, meta) {
_settings = {};
- _.eachObj(_itemsByAppKey, function(appKey, item) {
- _settings[appKey] = {
+ _.eachObj(_itemsByMessageKey, function(messageKey, item) {
+ _settings[messageKey] = {
value: item.get()
};
if (item.precision) {
- _settings[appKey].precision = item.precision;
+ _settings[messageKey].precision = item.precision;
}
});
return _settings;
diff --git a/src/scripts/lib/clay-item.js b/src/scripts/lib/clay-item.js
index c3ad27f..8eac8ec 100644
--- a/src/scripts/lib/clay-item.js
+++ b/src/scripts/lib/clay-item.js
@@ -29,7 +29,7 @@ function ClayItem(config) {
self.id = config.id || null;
/** @type {string|null} */
- self.appKey = config.appKey || null;
+ self.messageKey = config.messageKey || null;
/** @type {Object} */
self.config = config;
diff --git a/src/scripts/lib/manipulators.js b/src/scripts/lib/manipulators.js
index 25fd76b..baf69e7 100755
--- a/src/scripts/lib/manipulators.js
+++ b/src/scripts/lib/manipulators.js
@@ -133,24 +133,27 @@ module.exports = {
checkboxgroup: {
get: function() {
var result = [];
- this.$element.select('input:checked').each(function(item) {
- result.push(item.value);
+ this.$element.select('input').each(function(item) {
+ result.push(!!item.checked);
});
return result;
},
set: function(values) {
var self = this;
- values = values || [];
+ values = Array.isArray(values) ? values : [];
+
+ while (values.length < this.get().length) {
+ values.push(false);
+ }
if (_.equals(this.get(), values)) { return this; }
- self.$element.select('input').set('checked', false);
+ self.$element.select('input')
+ .set('checked', false)
+ .each(function(item, index) {
+ item.checked = !!values[index];
+ });
- values.map(function(value) {
- self.$element
- .select('input[value="' + value.toString(10).replace('"', '\\"') + '"]')
- .set('checked', true);
- });
return self.trigger('change');
},
disable: disable,
diff --git a/src/templates/components/checkboxgroup.tpl b/src/templates/components/checkboxgroup.tpl
index 6f7c15c..153c58d 100755
--- a/src/templates/components/checkboxgroup.tpl
+++ b/src/templates/components/checkboxgroup.tpl
@@ -3,13 +3,8 @@