mirror of
https://github.com/pebble-dev/clay.git
synced 2026-09-01 06:46:49 -04:00
Compare commits
148
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae5cc21abb | ||
|
|
df3965ccac | ||
|
|
8d53645a3a | ||
|
|
e328fa8cfa | ||
|
|
ff4f41e63f | ||
|
|
12e335915b | ||
|
|
d12c9a00db | ||
|
|
27a5b77b8a | ||
|
|
1aff0378f5 | ||
|
|
ffd0ba64ec | ||
|
|
74d6d0d21d | ||
|
|
6e6440c9fb | ||
|
|
62f0337198 | ||
|
|
eb89496733 | ||
|
|
5d6e070e01 | ||
|
|
ae31f9737f | ||
|
|
6e42537aa1 | ||
|
|
2b14076171 | ||
|
|
55ac8681af | ||
|
|
b5d2615307 | ||
|
|
6742ec84cf | ||
|
|
61d735662e | ||
|
|
050b89f82f | ||
|
|
1b179a603a | ||
|
|
4a6382aa7f | ||
|
|
f0c44c5719 | ||
|
|
2c2076bf69 | ||
|
|
5a67fec99e | ||
|
|
7cadd5fba4 | ||
|
|
6b59844084 | ||
|
|
b3963179d1 | ||
|
|
6a63c40e08 | ||
|
|
f91c525a30 | ||
|
|
19779a627f | ||
|
|
b19ad435df | ||
|
|
5627f789a3 | ||
|
|
1c9a812009 | ||
|
|
939398774d | ||
|
|
361dc99ea5 | ||
|
|
9f2a89a41e | ||
|
|
26df41b25e | ||
|
|
dd6fe54834 | ||
|
|
3cffa3a275 | ||
|
|
a0335be710 | ||
|
|
cabee3ae0b | ||
|
|
940c5e2451 | ||
|
|
870436ddd8 | ||
|
|
c3c8de77fc | ||
|
|
87807f180c | ||
|
|
9e344d527b | ||
|
|
2f9e36bdb2 | ||
|
|
5038e80bf2 | ||
|
|
7a2dbb276f | ||
|
|
3300e98ce2 | ||
|
|
693b795c2b | ||
|
|
ef8ab37eb4 | ||
|
|
118df1d952 | ||
|
|
8d4ff52b0f | ||
|
|
613c41fd6e | ||
|
|
ecac278f63 | ||
|
|
742890ee31 | ||
|
|
f08f11fb6f | ||
|
|
859e2f2767 | ||
|
|
19ad09eb66 | ||
|
|
39056a2868 | ||
|
|
8a2d560482 | ||
|
|
f62f960433 | ||
|
|
8fb67ce08d | ||
|
|
65b952d7f8 | ||
|
|
6c2a335b7c | ||
|
|
f717661eea | ||
|
|
35bdeb7519 | ||
|
|
93b927db63 | ||
|
|
9afdd2adb9 | ||
|
|
5fe87a04de | ||
|
|
7e2b75dd58 | ||
|
|
32e192e938 | ||
|
|
1af2b39db1 | ||
|
|
782eaf82f3 | ||
|
|
41d38e04a7 | ||
|
|
1611599a25 | ||
|
|
842cbc9e84 | ||
|
|
238d0e6a6c | ||
|
|
c5af3baaeb | ||
|
|
2030aa1af7 | ||
|
|
4445cb98cf | ||
|
|
12c420d861 | ||
|
|
dc33aa0b5d | ||
|
|
f200af909b | ||
|
|
6f3873ad6b | ||
|
|
70bb2e9d07 | ||
|
|
a9aaf0b940 | ||
|
|
80eb1506d8 | ||
|
|
68723d36f7 | ||
|
|
c937ee735e | ||
|
|
b553094742 | ||
|
|
f31ffed558 | ||
|
|
3f71f97b8b | ||
|
|
1a6308f621 | ||
|
|
7045c59a72 | ||
|
|
4ffb1018f7 | ||
|
|
e78c09f5be | ||
|
|
d8e73f2674 | ||
|
|
0358e820e6 | ||
|
|
1d821f61c3 | ||
|
|
e2ffa42595 | ||
|
|
3a2dcb187e | ||
|
|
08e3a21429 | ||
|
|
42a613caf4 | ||
|
|
4ff38eb0ba | ||
|
|
b466c48487 | ||
|
|
517d4f84ff | ||
|
|
e04a6c9f87 | ||
|
|
1f0867985d | ||
|
|
d0e5988024 | ||
|
|
d007d48afc | ||
|
|
07a847457f | ||
|
|
9347c9f3d2 | ||
|
|
4ee5a7361c | ||
|
|
dad8b2a025 | ||
|
|
d52f6d4aa2 | ||
|
|
a94eae9a16 | ||
|
|
317f6fd4e8 | ||
|
|
b19b07c47d | ||
|
|
0a2ddab597 | ||
|
|
005ac939b1 | ||
|
|
12b0ae9bd6 | ||
|
|
6f92c0d267 | ||
|
|
756cdc0d9e | ||
|
|
783bc84779 | ||
|
|
e621bbf0bd | ||
|
|
43c5c3cc9f | ||
|
|
d8738e06b2 | ||
|
|
67449ec34e | ||
|
|
5c63b00eda | ||
|
|
79eef99731 | ||
|
|
e5496c62d6 | ||
|
|
b1a6f184ba | ||
|
|
878fea0340 | ||
|
|
1142d1adee | ||
|
|
0873f5eba2 | ||
|
|
0faa585d5f | ||
|
|
3b7c4cda7c | ||
|
|
3839a47f5c | ||
|
|
85503d2846 | ||
|
|
d6ae6faef4 | ||
|
|
2c2f003e15 | ||
|
|
1af5828b18 |
@@ -1,4 +1,6 @@
|
|||||||
coverage/
|
coverage/
|
||||||
dist/
|
dist/
|
||||||
tmp/
|
tmp/
|
||||||
|
build/
|
||||||
src/scripts/vendor/
|
src/scripts/vendor/
|
||||||
|
src/js/index.js
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"new-cap": [0],
|
"new-cap": [0],
|
||||||
"max-params": [2, 4],
|
"max-params": [2, 5],
|
||||||
"require-jsdoc": [2, {
|
"require-jsdoc": [2, {
|
||||||
"require": {
|
"require": {
|
||||||
"FunctionDeclaration": true,
|
"FunctionDeclaration": true,
|
||||||
|
|||||||
+8
-23
@@ -1,31 +1,16 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
# Coverage directory used by tools like istanbul
|
||||||
coverage
|
/coverage
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# node-waf configuration
|
# node-waf configuration
|
||||||
.lock-wscript
|
.lock-wscript
|
||||||
|
/.lock-waf_darwin_build
|
||||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directory
|
# Dependency directory
|
||||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||||
node_modules
|
/node_modules
|
||||||
|
/npm-debug.log
|
||||||
|
|
||||||
tmp
|
/tmp/
|
||||||
|
/src/js/index.js
|
||||||
dist
|
/dist.zip
|
||||||
|
/build/
|
||||||
|
|||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# We ignore everything except for what is actually used by the pebble tool.
|
||||||
|
/**/*
|
||||||
|
!/dist.zip
|
||||||
|
!/src/**/*
|
||||||
|
!/index.js
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
cmake_minimum_required (VERSION 3.2)
|
||||||
|
project (test-project)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "-std=c11 -mthumb -ffunction-sections -g -fno-diagnostics-show-caret -D_REENT_SMALL=1 -Wall -Wextra -Werror -Wpointer-arith -Wno-unused-parameter -Wno-missing-field-initializers -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=unused-parameter -Wno-error=unused-but-set-variable -Wno-packed-bitfield-compat -mcpu=cortex-m3 -Os -Werror=return-type")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE src "src/c/*")
|
||||||
|
file(GLOB_RECURSE include "include/*")
|
||||||
|
file(GLOB_RECURSE build "build/basalt/*.c")
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
${src}
|
||||||
|
${build}
|
||||||
|
${include}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(INCLUDES
|
||||||
|
~/Library/Application\ Support/Pebble\ SDK/SDKs/current/sdk-core/pebble/basalt/include
|
||||||
|
./node_modules/pebble-events/dist/include
|
||||||
|
./node_modules/@smallstoneapps/linked-list/dist/include
|
||||||
|
./node_modules/@keegan-stoneware/simple-dict/dist/include
|
||||||
|
./node_modules/@keegan-stoneware/simple-app-message/dist/include
|
||||||
|
./build/include
|
||||||
|
./build
|
||||||
|
./include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions(-DPEBBLE)
|
||||||
|
add_definitions(-DPBL_COLOR)
|
||||||
|
|
||||||
|
include_directories(${INCLUDES})
|
||||||
|
|
||||||
|
add_executable(app ${SOURCES})
|
||||||
|
|
||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Thanks for checking out Clay. We accept pull requests from anybody, however we ask
|
||||||
|
that you follow some conventions when contributing to Clay.
|
||||||
|
|
||||||
|
We have set up a
|
||||||
|
[piece-of-cake](https://github.com/pebble/clay/issues?q=is%3Aopen+is%3Aissue+label%3Apiece-of-cake)
|
||||||
|
label in GitHub issues for bugs/features that are easy to implement. These issues are
|
||||||
|
a great place to start for people that are new to the codebase.
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [Node](https://nodejs.org) (4.0 and later)
|
||||||
|
- [Google Chrome](www.google.com/chrome) (To run tests)
|
||||||
|
- [Pebble SDK](https://developer.pebble.com/sdk/) (optional)
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### 1. Fork, then clone the repo:
|
||||||
|
|
||||||
|
`$ git clone git@github.com:your-username/clay.git`
|
||||||
|
|
||||||
|
### 2. Create a new branch with the following naming convention.
|
||||||
|
|
||||||
|
`#<issue-id>/description-of-change`
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
`#90/support-message-keys`
|
||||||
|
|
||||||
|
If there is currently no GitHub issue open for your proposed contribution,
|
||||||
|
then please make one. This allows us to assign milestones and triage priority.
|
||||||
|
|
||||||
|
### 3. Install the project dependencies.
|
||||||
|
|
||||||
|
`$ npm install`
|
||||||
|
|
||||||
|
### 4. Develop your feature
|
||||||
|
|
||||||
|
Commit as often as you like. Your branch will be squashed when the pull request is merged.
|
||||||
|
|
||||||
|
### 5. Run the tests
|
||||||
|
|
||||||
|
`$ npm run test`
|
||||||
|
|
||||||
|
### 6. Run linting
|
||||||
|
|
||||||
|
`$ npm run lint`
|
||||||
|
|
||||||
|
### 7. Submit your pull request
|
||||||
|
|
||||||
|
In the main comment of the pull request, you must state the GitHub issue that your PR
|
||||||
|
resolves. You do this by using the syntax `Resolves #<issue-id>`. An example of this
|
||||||
|
can be found [here](https://github.com/pebble/clay/pull/81)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Project Structure and Development
|
||||||
|
|
||||||
|
|
||||||
|
## Important Files
|
||||||
|
|
||||||
|
There are two main entry points for Clay - `index.js` and `src/scripts/config-page.js`.
|
||||||
|
|
||||||
|
### index.js
|
||||||
|
|
||||||
|
This is the main entry point for the code that will run in the Pebble app's `src/js/app.js`.
|
||||||
|
It is responsible for serializing the provided config into a data URI that will be opened
|
||||||
|
using `Pebble.openURL()`. It also persists data to `localStorage`.
|
||||||
|
|
||||||
|
### src/scripts/config-page.js
|
||||||
|
|
||||||
|
This is the main entry point for the code that runs on the generated config page,
|
||||||
|
and is responsible for passing the injected config and other components to the
|
||||||
|
`ClayConfig` class.
|
||||||
|
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Use the following command to build Clay during development.
|
||||||
|
|
||||||
|
`$ npm run dev`
|
||||||
|
|
||||||
|
This command packages up `src/scripts/config-page.js` and `dev/dev.js` into the `tmp/` directory
|
||||||
|
so `dev/dev.html` can include them as script tags. This will also watch for changes
|
||||||
|
in the project.
|
||||||
|
|
||||||
|
While developing components and other functionality for Clay, it is much easier to
|
||||||
|
work with the files in the `dev/` directory than on a phone or emulator. Below is
|
||||||
|
an explanation of the files and their purpose.
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|----------|-----|
|
||||||
|
| `dev.html` | Open this page in a browser after running `$ npm run dev`. |
|
||||||
|
| `dev.js` | Injects the components and dependencies into the window the same way `index.js` would. |
|
||||||
|
| `config.js` | Clay config to use as a sandbox for testing components. |
|
||||||
|
| `custom-fn.js` | Clay custom function to be injected by `dev.js`. |
|
||||||
|
| `emulator.html` | Copy of the HTML page that is used to make the Clay compatible with the Pebble SDK emulator. |
|
||||||
|
| `uri-test.html` | Used to stress test URI creation for older browser versions. |
|
||||||
|
|
||||||
|
|
||||||
|
## Testing your change inside of a test project.
|
||||||
|
|
||||||
|
Once your change is ready to be tested in the emulator or on the watch, follow these steps:
|
||||||
|
|
||||||
|
1. From your Clay fork run `$ npm run pebble-build`. This builds Clay to work with the Pebble SDK
|
||||||
|
2. From your test Pebble project, run `$ pebble package install /path/to/your/clay/fork`.
|
||||||
|
This will modify your `package.json` to point directly to your local copy of Clay.
|
||||||
|
3. Run the usual`$ pebble build && pebble install --emulator basalt && pebble emu-app-config` to test your modifications
|
||||||
|
4. If you make modifications to your fork of Clay, you must repeat the above steps.
|
||||||
|
|
||||||
|
|
||||||
|
## Functionality
|
||||||
|
|
||||||
|
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()`,
|
||||||
|
`.getItemByMessageKey()`, `.getItemById()`, `.getItemsByType()`.
|
||||||
|
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Clay enforces 100% test coverage. Chances are, that if you are submitting a pull request,
|
||||||
|
there should be tests for the proposed code changes. Have a look at the other tests in
|
||||||
|
the repository to see some examples of how you should be writing your tests.
|
||||||
|
|
||||||
|
There are two commands to use while writing your tests.
|
||||||
|
|
||||||
|
- `$ npm run test` - Runs all tests once.
|
||||||
|
- `$ npm run test-debug` - Watches for file changes and re-runs all tests when it
|
||||||
|
detects a change. This also leaves an in instance of Google Chrome running.
|
||||||
|
If you click the "Debug" button in the top right of the page, a new tab will open.
|
||||||
|
You can then use the Google Chrome developer tools to debug your tests as they run.
|
||||||
|
Refreshing the page will re-run the tests.
|
||||||
|
|
||||||
|
## Style Guide
|
||||||
|
|
||||||
|
Our style guide is very similar to the
|
||||||
|
[Airbnb style guide.](https://github.com/airbnb/javascript/tree/master/es5) with the
|
||||||
|
following exceptions:
|
||||||
|
|
||||||
|
- When saving a reference to `this` use `self`.
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function() {
|
||||||
|
var _this = this;
|
||||||
|
return function() {
|
||||||
|
console.log(_this);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad
|
||||||
|
function() {
|
||||||
|
var that = this;
|
||||||
|
return function() {
|
||||||
|
console.log(that);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
function() {
|
||||||
|
var self = this;
|
||||||
|
return function() {
|
||||||
|
console.log(self);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Don't double name your functions. It makes refactoring more difficult.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
var log = function log(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
// good
|
||||||
|
var log = function(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The style guide is enforced using ESLint. Run `$ npm run lint` to ensure you are
|
||||||
|
adhering to the style guide.
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Chat to us in the `#clay` channel on slack. http://slack.pbldev.io/
|
||||||
|
- Visit the [Pebble Forums](https://forums.pebble.com/)
|
||||||
|
- Tweet at [@pebbledev](https://twitter.com/pebbledev)
|
||||||
|
- More options at https://developer.pebble.com/community/online/
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Ignore build generated files
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
dist.zip
|
||||||
|
|
||||||
|
# Ignore waf lock file
|
||||||
|
.lock-waf*
|
||||||
|
|
||||||
|
# Ignore installed node modules
|
||||||
|
node_modules/
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
cmake_minimum_required (VERSION 3.2)
|
||||||
|
project (test-project)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "-std=c11 -mthumb -ffunction-sections -g -fno-diagnostics-show-caret -D_REENT_SMALL=1 -Wall -Wextra -Werror -Wpointer-arith -Wno-unused-parameter -Wno-missing-field-initializers -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=unused-parameter -Wno-error=unused-but-set-variable -Wno-packed-bitfield-compat -mcpu=cortex-m3 -Os -Werror=return-type")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE src "src/*")
|
||||||
|
file(GLOB_RECURSE build "build/basalt/*.c")
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
${src}
|
||||||
|
${build}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(INCLUDES
|
||||||
|
~/Library/Application\ Support/Pebble\ SDK/SDKs/current/sdk-core/pebble/basalt/include
|
||||||
|
./node_modules/pebble-events/dist/include
|
||||||
|
./node_modules/@keegan-stoneware/simple-dict/dist/include
|
||||||
|
./node_modules/pebble-clay/dist/include
|
||||||
|
./build/include
|
||||||
|
./build
|
||||||
|
./include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions(-DPEBBLE)
|
||||||
|
add_definitions(-DPBL_COLOR)
|
||||||
|
|
||||||
|
include_directories(${INCLUDES})
|
||||||
|
|
||||||
|
add_executable(app ${SOURCES})
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "clay-test",
|
||||||
|
"author": "MakeAwesomeHappen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"keywords": [
|
||||||
|
"pebble-app"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "./scripts/build.sh"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pebble-clay": "../"
|
||||||
|
},
|
||||||
|
"pebble": {
|
||||||
|
"displayName": "clay-test",
|
||||||
|
"uuid": "3fff53d2-1b59-48d7-8187-0c2f3674c32d",
|
||||||
|
"sdkVersion": "3",
|
||||||
|
"enableMultiJS": true,
|
||||||
|
"targetPlatforms": [
|
||||||
|
"aplite",
|
||||||
|
"basalt",
|
||||||
|
"chalk",
|
||||||
|
"diorite",
|
||||||
|
"emery"
|
||||||
|
],
|
||||||
|
"watchapp": {
|
||||||
|
"watchface": false
|
||||||
|
},
|
||||||
|
"messageKeys": [],
|
||||||
|
"resources": {
|
||||||
|
"media": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
test_app_folder_name="clay-test"
|
||||||
|
if [ "$(basename $(pwd))" != "$test_app_folder_name" ]; then
|
||||||
|
echo "Must be executed from $test_app_folder_name folder"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
pebble clean && cd .. && npm run pebble-build || { cd $test_app_folder_name && exit; } && cd $test_app_folder_name && pebble build
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#include <inttypes.h>
|
||||||
|
#include <pebble.h>
|
||||||
|
#include <pebble-clay/clay.h>
|
||||||
|
|
||||||
|
#define CLAY_INBOX_SIZE (64)
|
||||||
|
#define CHAR_SIZE (10)
|
||||||
|
#define ARRAY_SIZE (3)
|
||||||
|
#define INT_DEFAULT (0xff0000)
|
||||||
|
#define CHAR_DEFAULT ("default")
|
||||||
|
#define BOOL_DEFAULT (false)
|
||||||
|
|
||||||
|
static Window *s_window;
|
||||||
|
static int s_background_color = INT_DEFAULT;
|
||||||
|
static char s_label_char_value[CHAR_SIZE] = CHAR_DEFAULT;
|
||||||
|
static bool s_label_bool_value = BOOL_DEFAULT;
|
||||||
|
static bool s_label_array_value_raw[ARRAY_SIZE] = {true, false, true};
|
||||||
|
static char s_label_array_value[ARRAY_SIZE + 1] = {'0', '0', '0'};
|
||||||
|
static TextLayer *s_label_char;
|
||||||
|
static TextLayer *s_label_bool;
|
||||||
|
static TextLayer *s_label_array;
|
||||||
|
|
||||||
|
static void prv_update_display(Layer *layer, GContext *ctx) {
|
||||||
|
graphics_context_set_fill_color(ctx, GColorFromHEX(s_background_color));
|
||||||
|
graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone);
|
||||||
|
|
||||||
|
text_layer_set_text(s_label_char, s_label_char_value);
|
||||||
|
text_layer_set_text(s_label_bool, s_label_bool_value ? "true" : "false");
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE; ++i) {
|
||||||
|
if (s_label_array_value_raw[i]) {
|
||||||
|
s_label_array_value[i] = '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_layer_set_text(s_label_array, s_label_array_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_clay_updated_handler(void *context) {
|
||||||
|
clay_get_int("test_int", &s_background_color);
|
||||||
|
clay_get_bool("test_bool", &s_label_bool_value);
|
||||||
|
clay_get_string("test_char", s_label_char_value, CHAR_SIZE);
|
||||||
|
|
||||||
|
layer_mark_dirty(window_get_root_layer(s_window));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_window_unload(Window *window) {
|
||||||
|
layer_destroy(text_layer_get_layer(s_label_char));
|
||||||
|
layer_destroy(text_layer_get_layer(s_label_bool));
|
||||||
|
layer_destroy(text_layer_get_layer(s_label_array));
|
||||||
|
window_destroy(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||||
|
s_background_color = 0x00ff00;
|
||||||
|
clay_set_int("test_int", &s_background_color);
|
||||||
|
|
||||||
|
s_label_bool_value = true;
|
||||||
|
clay_set_bool("test_bool", &s_label_bool_value);
|
||||||
|
|
||||||
|
strcpy(s_label_char_value, "internal");
|
||||||
|
clay_set_string("test_char", s_label_char_value);
|
||||||
|
|
||||||
|
layer_mark_dirty(window_get_root_layer(s_window));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||||
|
s_background_color = INT_DEFAULT;
|
||||||
|
clay_set_int("test_int", &s_background_color);
|
||||||
|
|
||||||
|
s_label_bool_value = BOOL_DEFAULT;
|
||||||
|
clay_set_bool("test_bool", &s_label_bool_value);
|
||||||
|
|
||||||
|
strcpy(s_label_char_value, CHAR_DEFAULT);
|
||||||
|
clay_set_string("test_char", s_label_char_value);
|
||||||
|
|
||||||
|
layer_mark_dirty(window_get_root_layer(s_window));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void prv_click_config_provider(void *context) {
|
||||||
|
window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler);
|
||||||
|
window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_window_load(Window *window) {
|
||||||
|
Layer *window_layer = window_get_root_layer(s_window);
|
||||||
|
|
||||||
|
layer_set_update_proc(window_layer, prv_update_display);
|
||||||
|
|
||||||
|
GRect bounds = layer_get_bounds(window_layer);
|
||||||
|
|
||||||
|
s_label_char = text_layer_create(GRect(0, bounds.size.h / 4, bounds.size.w, 30));
|
||||||
|
text_layer_set_text_alignment(s_label_char, GTextAlignmentCenter);
|
||||||
|
text_layer_set_background_color(s_label_char, GColorClear);
|
||||||
|
layer_add_child(window_layer, text_layer_get_layer(s_label_char));
|
||||||
|
|
||||||
|
s_label_bool = text_layer_create(GRect(0, bounds.size.h / 2, bounds.size.w, 30));
|
||||||
|
text_layer_set_text_alignment(s_label_bool, GTextAlignmentCenter);
|
||||||
|
text_layer_set_background_color(s_label_bool, GColorClear);
|
||||||
|
layer_add_child(window_layer, text_layer_get_layer(s_label_bool));
|
||||||
|
|
||||||
|
s_label_array = text_layer_create(GRect(0, 3 * (bounds.size.h / 4), bounds.size.w, 30));
|
||||||
|
text_layer_set_text_alignment(s_label_array, GTextAlignmentCenter);
|
||||||
|
text_layer_set_background_color(s_label_array, GColorClear);
|
||||||
|
layer_add_child(window_layer, text_layer_get_layer(s_label_array));
|
||||||
|
|
||||||
|
prv_clay_updated_handler(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_init(void) {
|
||||||
|
|
||||||
|
const ClayCallbacks clay_callbacks = {
|
||||||
|
.settings_updated = prv_clay_updated_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
clay_register_callbacks(&clay_callbacks, NULL);
|
||||||
|
clay_init(CLAY_INBOX_SIZE, true);
|
||||||
|
|
||||||
|
s_window = window_create();
|
||||||
|
window_set_window_handlers(s_window, (WindowHandlers) {
|
||||||
|
.load = prv_window_load,
|
||||||
|
.unload = prv_window_unload,
|
||||||
|
});
|
||||||
|
|
||||||
|
window_set_click_config_provider(s_window, prv_click_config_provider);
|
||||||
|
|
||||||
|
window_stack_push(s_window, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
prv_init();
|
||||||
|
app_event_loop();
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
var Clay = require('pebble-clay');
|
||||||
|
var clay = new Clay({
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
type: 'section',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'int',
|
||||||
|
type: 'color',
|
||||||
|
messageKey: 'test_int'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'char',
|
||||||
|
type: 'input',
|
||||||
|
messageKey: 'test_char'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'bool',
|
||||||
|
type: 'toggle',
|
||||||
|
messageKey: 'test_bool'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'array',
|
||||||
|
type: 'checkboxgroup',
|
||||||
|
messageKey: 'test_array',
|
||||||
|
options: ['one', 'two', 'three']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'submit',
|
||||||
|
defaultValue: 'submit'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
closedCallback: function(response) {
|
||||||
|
console.log('closedcallback: arguments' + JSON.stringify(arguments));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#
|
||||||
|
# This file is the default set of rules to compile a Pebble application.
|
||||||
|
#
|
||||||
|
# Feel free to customize this to your needs.
|
||||||
|
#
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
import waflib
|
||||||
|
|
||||||
|
top = '.'
|
||||||
|
out = 'build'
|
||||||
|
|
||||||
|
|
||||||
|
def options(ctx):
|
||||||
|
ctx.load('pebble_sdk')
|
||||||
|
|
||||||
|
|
||||||
|
def distclean(ctx):
|
||||||
|
node_modules_dir = ctx.path.find_node('node_modules')
|
||||||
|
if node_modules_dir:
|
||||||
|
shutil.rmtree(node_modules_dir.abspath())
|
||||||
|
waflib.Scripting.distclean(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def configure(ctx):
|
||||||
|
"""
|
||||||
|
This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures
|
||||||
|
a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your
|
||||||
|
change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first.
|
||||||
|
Universal configuration: add your change prior to calling ctx.load('pebble_sdk').
|
||||||
|
"""
|
||||||
|
ctx.load('pebble_sdk')
|
||||||
|
|
||||||
|
|
||||||
|
def build(ctx):
|
||||||
|
ctx.load('pebble_sdk')
|
||||||
|
|
||||||
|
build_worker = os.path.exists('worker_src')
|
||||||
|
binaries = []
|
||||||
|
|
||||||
|
cached_env = ctx.env
|
||||||
|
for platform in ctx.env.TARGET_PLATFORMS:
|
||||||
|
ctx.env = ctx.all_envs[platform]
|
||||||
|
ctx.set_group(ctx.env.PLATFORM_NAME)
|
||||||
|
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
|
||||||
|
ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), target=app_elf)
|
||||||
|
|
||||||
|
if build_worker:
|
||||||
|
worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
|
||||||
|
binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})
|
||||||
|
ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'), target=worker_elf)
|
||||||
|
else:
|
||||||
|
binaries.append({'platform': platform, 'app_elf': app_elf})
|
||||||
|
ctx.env = cached_env
|
||||||
|
|
||||||
|
ctx.set_group('bundle')
|
||||||
|
ctx.pbl_bundle(binaries=binaries,
|
||||||
|
js=ctx.path.ant_glob(['src/js/**/*.js', 'src/js/**/*.json']),
|
||||||
|
js_entry_file='src/js/app.js')
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
'use strict';
|
||||||
|
/* eslint-disable quotes */
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"id": "main-heading",
|
||||||
|
"defaultValue": "Clay Test Page",
|
||||||
|
"size": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"defaultValue": "This is config 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'thing',
|
||||||
|
defaultValue: {
|
||||||
|
en_US: 'foo',
|
||||||
|
fr_FR: 'bar'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"defaultValue": "This is a section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "input",
|
||||||
|
"messageKey": "email",
|
||||||
|
"defaultValue": "",
|
||||||
|
"label": "Input Field",
|
||||||
|
"description": "This is a new field",
|
||||||
|
"attributes": {
|
||||||
|
"placeholder": "Placeholder set with attributes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "submit",
|
||||||
|
"defaultValue": "Save"
|
||||||
|
}
|
||||||
|
];
|
||||||
+70
-15
@@ -13,6 +13,17 @@ module.exports = [
|
|||||||
"defaultValue": "Some arbitrary text explaining how this all works. " +
|
"defaultValue": "Some arbitrary text explaining how this all works. " +
|
||||||
"It's cool if this wraps across multiple lines"
|
"It's cool if this wraps across multiple lines"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"capabilities": ["RECT", "MICROPHONE"],
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"defaultValue": "This section is only visible for rectangular devices that" +
|
||||||
|
" have a microphone"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "section",
|
"type": "section",
|
||||||
"items": [
|
"items": [
|
||||||
@@ -20,33 +31,74 @@ module.exports = [
|
|||||||
"type": "heading",
|
"type": "heading",
|
||||||
"defaultValue": "This is a section"
|
"defaultValue": "This is a section"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"defaultValue": "This is only visible for aplite",
|
||||||
|
"capabilities": ["APLITE"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "input",
|
"type": "input",
|
||||||
"appKey": "email",
|
"messageKey": "email",
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"label": "Input Field",
|
"label": "Input Field",
|
||||||
"description": "This is a description for the input component. " +
|
"description": "{{i18n.foo}} This is a description for the input component. " +
|
||||||
"You can add <strong>html</strong> in here too.",
|
"You can add <strong>html</strong> in here too.",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"placeholder": "Placeholder set with attributes"
|
"placeholder": "Placeholder set with attributes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "input",
|
||||||
|
"label": "Time Field",
|
||||||
|
"attributes": {
|
||||||
|
"type": "time"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "input",
|
||||||
|
"label": "Date Field",
|
||||||
|
"attributes": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "slider",
|
||||||
|
"messageKey": "slider",
|
||||||
|
"defaultValue": 15,
|
||||||
|
"label": "Slider",
|
||||||
|
"description": "This is the description for the slider",
|
||||||
|
"min": 10,
|
||||||
|
"max": 20,
|
||||||
|
"step": 0.25
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "toggle",
|
"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",
|
"label": "This is a Toggle",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "color",
|
"type": "color",
|
||||||
"appKey": "colorTest",
|
"messageKey": "colorTest",
|
||||||
"defaultValue": "FF0000",
|
"defaultValue": "FF0000",
|
||||||
"label": "Standard Color Picker",
|
"label": "Standard Color Picker",
|
||||||
"sunlight": false
|
"sunlight": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "color",
|
"type": "color",
|
||||||
"appKey": "sunnyColorTest",
|
"messageKey": "sunnyColorTest",
|
||||||
"defaultValue": "00FF00",
|
"defaultValue": "00FF00",
|
||||||
"label": "Sunny Color Picker",
|
"label": "Sunny Color Picker",
|
||||||
"sunlight": true
|
"sunlight": true
|
||||||
@@ -70,7 +122,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "radiogroup",
|
"type": "radiogroup",
|
||||||
"appKey": "radiogroup-test",
|
"messageKey": "radiogroup-test",
|
||||||
"label": "Radio Group",
|
"label": "Radio Group",
|
||||||
"options": [
|
"options": [
|
||||||
{ "label": "Test thing", "value": "one" },
|
{ "label": "Test thing", "value": "one" },
|
||||||
@@ -80,25 +132,28 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "checkboxgroup",
|
"type": "checkboxgroup",
|
||||||
"appKey": "checkboxgroup-test",
|
"messageKey": "checkboxgroup-test",
|
||||||
"defaultValue": ["quote' \"test", "two"],
|
"defaultValue": [0, 1, 0],
|
||||||
"label": "Checkbox Group",
|
"label": "Checkbox Group",
|
||||||
"options": [
|
"options": [ "First thing", "Another thing", "Final thing" ]
|
||||||
{ "label": "First thing", "value": "three" },
|
|
||||||
{ "label": "Another thing", "value": "two" },
|
|
||||||
{ "label": "Final thing", "value": "three" }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"appKey": "flavor",
|
"messageKey": "flavor",
|
||||||
"defaultValue": "grape",
|
"defaultValue": "grape",
|
||||||
"label": "Select Menu",
|
"label": "Select Menu",
|
||||||
"options": [
|
"options": [
|
||||||
{ "label": "", "value": "" },
|
{ "label": "", "value": "" },
|
||||||
{ "label": "Berry", "value": "berry" },
|
{ "label": "Berry", "value": "berry" },
|
||||||
{ "label": "This Option is Selected", "value": "grape" },
|
{ "label": "This Option is Selected", "value": "grape" },
|
||||||
{ "label": "Banana", "value": "banana" }
|
{ "label": "Banana", "value": "banana" },
|
||||||
|
{
|
||||||
|
"label": "This is an optgroup",
|
||||||
|
"value": [
|
||||||
|
{ "label": "Peach", "value": "peach" },
|
||||||
|
{ "label": "Mango", "value": "mango" }
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"required": "required"
|
"required": "required"
|
||||||
|
|||||||
+15
-3
@@ -10,15 +10,27 @@ module.exports = function() {
|
|||||||
*/
|
*/
|
||||||
function toggleBackground() {
|
function toggleBackground() {
|
||||||
if (this.get()) {
|
if (this.get()) {
|
||||||
Clay.getItemByAppKey('colorTest').enable();
|
Clay.getItemByMessageKey('colorTest').enable();
|
||||||
} else {
|
} else {
|
||||||
Clay.getItemByAppKey('colorTest').disable();
|
Clay.getItemByMessageKey('colorTest').disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function handleButtonClick() {
|
||||||
|
Clay.config = Clay.meta.userData.config2;
|
||||||
|
Clay.build();
|
||||||
|
}
|
||||||
|
|
||||||
Clay.on(Clay.EVENTS.AFTER_BUILD, function() {
|
Clay.on(Clay.EVENTS.AFTER_BUILD, function() {
|
||||||
var coolStuffToggle = Clay.getItemByAppKey('cool_stuff');
|
var coolStuffToggle = Clay.getItemByMessageKey('cool_stuff');
|
||||||
toggleBackground.call(coolStuffToggle);
|
toggleBackground.call(coolStuffToggle);
|
||||||
coolStuffToggle.on('change', toggleBackground);
|
coolStuffToggle.on('change', toggleBackground);
|
||||||
|
|
||||||
|
Clay.getItemById('testButton').on('click', handleButtonClick);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('userData: ', Clay.meta.userData);
|
||||||
};
|
};
|
||||||
|
|||||||
+17
@@ -5,6 +5,23 @@ window.clayConfig = require('./config.js');
|
|||||||
window.claySettings = {};
|
window.claySettings = {};
|
||||||
window.customFn = require('./custom-fn.js');
|
window.customFn = require('./custom-fn.js');
|
||||||
window.clayComponents = require('../src/scripts/components');
|
window.clayComponents = require('../src/scripts/components');
|
||||||
|
window.clayMeta = require('../test/fixture').meta({
|
||||||
|
activeWatchInfo: {
|
||||||
|
platform: 'basalt',
|
||||||
|
model: 'qemu_platform_basalt',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
patch: 2,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userData: {
|
||||||
|
foo: 'bar',
|
||||||
|
config2: require('./config-2')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var platform = window.navigator.userAgent.match(/Android/) ? 'android' : 'ios';
|
var platform = window.navigator.userAgent.match(/Android/) ? 'android' : 'ios';
|
||||||
document.documentElement.classList.add('platform-' + platform);
|
document.documentElement.classList.add('platform-' + platform);
|
||||||
|
|||||||
+6
-1
@@ -20,9 +20,14 @@
|
|||||||
|
|
||||||
var returnTo = getQueryParam('return_to');
|
var returnTo = getQueryParam('return_to');
|
||||||
var data = decodeURIComponent(location.hash.substring(1));
|
var data = decodeURIComponent(location.hash.substring(1));
|
||||||
if (data) {
|
|
||||||
|
// Check if we dealing with a base64 encoded URI
|
||||||
|
if (data && data.charAt(0) !== '<') {
|
||||||
data = window.atob(data).replace('$$RETURN_TO$$', returnTo);
|
data = window.atob(data).replace('$$RETURN_TO$$', returnTo);
|
||||||
window.location.href = 'data:text/html;base64,' + encodeURIComponent(window.btoa(data));
|
window.location.href = 'data:text/html;base64,' + encodeURIComponent(window.btoa(data));
|
||||||
|
} else if (data) {
|
||||||
|
data = data.replace('$$RETURN_TO$$', returnTo);
|
||||||
|
window.location.href = 'data:text/html;charset=utf-8,' + encodeURIComponent(data);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+11
-6
@@ -15,6 +15,7 @@ var uglify = require('gulp-uglify');
|
|||||||
var sassify = require('sassify');
|
var sassify = require('sassify');
|
||||||
var autoprefixify = require('./src/scripts/vendor/autoprefixify');
|
var autoprefixify = require('./src/scripts/vendor/autoprefixify');
|
||||||
var insert = require('gulp-insert');
|
var insert = require('gulp-insert');
|
||||||
|
var clayPackage = require('./package.json');
|
||||||
|
|
||||||
var sassIncludePaths = [].concat(
|
var sassIncludePaths = [].concat(
|
||||||
require('bourbon').includePaths,
|
require('bourbon').includePaths,
|
||||||
@@ -37,7 +38,7 @@ var autoprefixerOptions = {
|
|||||||
|
|
||||||
var stringifyOptions = ['.html', '.tpl'];
|
var stringifyOptions = ['.html', '.tpl'];
|
||||||
var versionMessage = '/* Clay - https://github.com/pebble/clay - Version: ' +
|
var versionMessage = '/* Clay - https://github.com/pebble/clay - Version: ' +
|
||||||
require('./package.json').version +
|
clayPackage.version +
|
||||||
' - Build Date: ' + new Date().toISOString() + ' */\n';
|
' - Build Date: ' + new Date().toISOString() + ' */\n';
|
||||||
|
|
||||||
gulp.task('clean-js', function() {
|
gulp.task('clean-js', function() {
|
||||||
@@ -84,19 +85,22 @@ gulp.task('inlineHtml', ['js', 'sass'], function() {
|
|||||||
gulp.task('clay', ['inlineHtml'], function() {
|
gulp.task('clay', ['inlineHtml'], function() {
|
||||||
return browserify('index.js', {
|
return browserify('index.js', {
|
||||||
debug: false,
|
debug: false,
|
||||||
standalone: 'clay'
|
standalone: clayPackage.name
|
||||||
})
|
})
|
||||||
.transform('deamdify')
|
.transform('deamdify')
|
||||||
.transform(stringify(stringifyOptions))
|
.transform(stringify(stringifyOptions))
|
||||||
.transform(sassify, sassifyOptions)
|
.transform(sassify, sassifyOptions)
|
||||||
.transform(autoprefixify, autoprefixerOptions)
|
.transform(autoprefixify, autoprefixerOptions)
|
||||||
.require(require.resolve('./index'), {expose: 'pebble-clay'})
|
.require(require.resolve('./index'), {expose: clayPackage.name})
|
||||||
|
.exclude('message_keys')
|
||||||
.bundle()
|
.bundle()
|
||||||
.pipe(source('clay.js'))
|
.pipe(source('index.js'))
|
||||||
.pipe(buffer())
|
.pipe(buffer())
|
||||||
.pipe(uglify())
|
.pipe(uglify({
|
||||||
|
preserveComments: 'license'
|
||||||
|
}))
|
||||||
.pipe(insert.prepend(versionMessage))
|
.pipe(insert.prepend(versionMessage))
|
||||||
.pipe(gulp.dest('./dist/'));
|
.pipe(gulp.dest('./src/js'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('dev-js', ['js', 'sass'], function() {
|
gulp.task('dev-js', ['js', 'sass'], function() {
|
||||||
@@ -105,6 +109,7 @@ gulp.task('dev-js', ['js', 'sass'], function() {
|
|||||||
.transform('deamdify')
|
.transform('deamdify')
|
||||||
.transform(sassify, sassifyOptions)
|
.transform(sassify, sassifyOptions)
|
||||||
.transform(autoprefixify, autoprefixerOptions)
|
.transform(autoprefixify, autoprefixerOptions)
|
||||||
|
.ignore('message_keys')
|
||||||
.bundle()
|
.bundle()
|
||||||
.pipe(source('dev.js'))
|
.pipe(source('dev.js'))
|
||||||
.pipe(gulp.dest('./tmp/'));
|
.pipe(gulp.dest('./tmp/'));
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <@keegan-stoneware/simple-app-message/simple-app-message.h>
|
||||||
|
|
||||||
|
typedef void (*ClayUpdatedCallback)(void *context);
|
||||||
|
|
||||||
|
typedef struct ClayCallbacks {
|
||||||
|
ClayUpdatedCallback settings_updated;
|
||||||
|
} ClayCallbacks;
|
||||||
|
|
||||||
|
void clay_init(uint32_t inbox_size, bool open_app_message);
|
||||||
|
|
||||||
|
void clay_register_callbacks(const ClayCallbacks *callbacks, void *context);
|
||||||
|
|
||||||
|
bool clay_remove(const char *key);
|
||||||
|
|
||||||
|
bool clay_set_bool(const char *key, bool *value);
|
||||||
|
|
||||||
|
bool clay_set_data(const char *key, const void *value, size_t size);
|
||||||
|
|
||||||
|
bool clay_set_int(const char *key, int *value);
|
||||||
|
|
||||||
|
bool clay_set_string(const char *key, const char *value);
|
||||||
|
|
||||||
|
bool clay_get_bool(const char *key, bool *value_out);
|
||||||
|
|
||||||
|
bool clay_get_data(const char *key, void *value_out, size_t size);
|
||||||
|
|
||||||
|
bool clay_get_int(const char *key, int *value_out);
|
||||||
|
|
||||||
|
bool clay_get_string(const char *key, char *value_out, size_t size);
|
||||||
@@ -3,64 +3,40 @@
|
|||||||
var configPageHtml = require('./tmp/config-page.html');
|
var configPageHtml = require('./tmp/config-page.html');
|
||||||
var toSource = require('tosource');
|
var toSource = require('tosource');
|
||||||
var standardComponents = require('./src/scripts/components');
|
var standardComponents = require('./src/scripts/components');
|
||||||
|
var deepcopy = require('deepcopy/build/deepcopy.min');
|
||||||
|
var version = require('./package.json').version;
|
||||||
|
var simpleAppMessage = require('@keegan-stoneware/simple-app-message');
|
||||||
var utils = require('./src/scripts/lib/utils');
|
var utils = require('./src/scripts/lib/utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array} config - the Clay config
|
* @param {Object} options - Clay options
|
||||||
* @param {function} [customFn] - Custom code to run from the config page. Will run
|
* @param {Array} [options.config] - The config
|
||||||
* with the ClayConfig instance as context
|
* @param {function} [options.openCallback]
|
||||||
* @param {Object} [options] - Additional options to pass to Clay
|
* @param {function} [options.closedCallback]
|
||||||
* @param {boolean} [options.autoHandleEvents] - If false, Clay will not
|
* @param {Object} [options.locales]
|
||||||
* automatically handle the 'showConfiguration' and 'webviewclosed' events
|
* @param {function} [options.customFunction] - Custom code to run from the config
|
||||||
|
* page. Will run with the ClayConfig instance as context
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function Clay(config, customFn, options) {
|
function Clay(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!Array.isArray(config)) {
|
if (!options) {
|
||||||
throw new Error('config must be an Array');
|
utils.throw('You must define options');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customFn && typeof customFn !== 'function') {
|
options = deepcopy(options);
|
||||||
throw new Error('customFn must be a function or "null"');
|
|
||||||
}
|
|
||||||
|
|
||||||
options = options || {};
|
self.config = options.config;
|
||||||
|
self.customFn = options.customFn || function() {};
|
||||||
self.config = config;
|
|
||||||
self.customFn = customFn || function() {};
|
|
||||||
self.components = {};
|
self.components = {};
|
||||||
self.meta = {
|
self.meta = {
|
||||||
activeWatchInfo: null,
|
activeWatchInfo: null,
|
||||||
accountToken: '',
|
accountToken: '',
|
||||||
watchToken: ''
|
watchToken: '',
|
||||||
|
userData: {}
|
||||||
};
|
};
|
||||||
|
self.version = version;
|
||||||
// Let Clay handle all the magic
|
|
||||||
if (options.autoHandleEvents !== false && typeof Pebble !== 'undefined') {
|
|
||||||
|
|
||||||
Pebble.addEventListener('showConfiguration', function() {
|
|
||||||
_populateMeta();
|
|
||||||
Pebble.openURL(self.generateUrl());
|
|
||||||
});
|
|
||||||
|
|
||||||
Pebble.addEventListener('webviewclosed', function(e) {
|
|
||||||
|
|
||||||
if (!e || !e.response) { return; }
|
|
||||||
|
|
||||||
// Send settings to Pebble watchapp
|
|
||||||
Pebble.sendAppMessage(self.getSettings(e.response), function() {
|
|
||||||
console.log('Sent config data to Pebble');
|
|
||||||
}, function(error) {
|
|
||||||
console.log('Failed to send config data!');
|
|
||||||
console.log(JSON.stringify(error));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else if (typeof Pebble !== 'undefined') {
|
|
||||||
Pebble.addEventListener('ready', function() {
|
|
||||||
_populateMeta();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the meta with data from the Pebble object. Make sure to run this inside
|
* Populate the meta with data from the Pebble object. Make sure to run this inside
|
||||||
@@ -71,28 +47,82 @@ function Clay(config, customFn, options) {
|
|||||||
self.meta = {
|
self.meta = {
|
||||||
activeWatchInfo: Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo(),
|
activeWatchInfo: Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo(),
|
||||||
accountToken: Pebble.getAccountToken(),
|
accountToken: Pebble.getAccountToken(),
|
||||||
watchToken: Pebble.getWatchToken()
|
watchToken: Pebble.getWatchToken(),
|
||||||
|
userData: deepcopy(options.userData || {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let Clay handle all the magic
|
||||||
|
Pebble.addEventListener('showConfiguration', function() {
|
||||||
|
_populateMeta();
|
||||||
|
Pebble.openURL(self.generateUrl());
|
||||||
|
});
|
||||||
|
|
||||||
|
Pebble.addEventListener('webviewclosed', function(e) {
|
||||||
|
|
||||||
|
if (!e || !e.response) { return; }
|
||||||
|
|
||||||
|
var settings = self.getSettings(e.response, false);
|
||||||
|
|
||||||
|
if (options.closedCallback) {
|
||||||
|
options.closedCallback(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleAppMessage.send('CLAY', settings, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
utils.log('Failed to send config data!');
|
||||||
|
utils.log(response.error);
|
||||||
|
} else {
|
||||||
|
utils.log('Sent config data to Pebble');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 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
|
* @private
|
||||||
* @param {Clay~ConfigItem|Array} item
|
* @param {Clay~ConfigItem|Array} item
|
||||||
|
* @param {_scanConfig_testFn} testFn
|
||||||
|
* @param {_scanConfig_callback} callback
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function _registerStandardComponents(item) {
|
function _scanConfig(item, testFn, callback) {
|
||||||
if (Array.isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
item.forEach(function(item) {
|
item.forEach(function(item) {
|
||||||
_registerStandardComponents(item);
|
_scanConfig(item, testFn, callback);
|
||||||
});
|
});
|
||||||
} else if (item.type === 'section') {
|
} else if (item.type === 'section') {
|
||||||
_registerStandardComponents(item.items);
|
_scanConfig(item.items, testFn, callback);
|
||||||
} else if (standardComponents[item.type]) {
|
} else if (testFn(item)) {
|
||||||
self.registerComponent(standardComponents[item.type]);
|
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');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,72 +186,160 @@ Clay.prototype.generateUrl = function() {
|
|||||||
Clay.prototype.getSettings = function(response, convert) {
|
Clay.prototype.getSettings = function(response, convert) {
|
||||||
// Decode and parse config data as JSON
|
// Decode and parse config data as JSON
|
||||||
var settings = {};
|
var settings = {};
|
||||||
|
response = response.match(/^\{/) ? response : decodeURIComponent(response);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
settings = JSON.parse(decodeURIComponent(response));
|
settings = JSON.parse(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('The provided response was not valid JSON');
|
utils.error('The provided response was not valid JSON');
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('clay-settings', JSON.stringify(settings));
|
// @todo do something with the persist flag here
|
||||||
|
// flatten the settings for localStorage
|
||||||
|
var settingsStorage = {};
|
||||||
|
Object.keys(settings).forEach(function(key) {
|
||||||
|
if (typeof settings[key] === 'object' && settings[key]) {
|
||||||
|
settingsStorage[key] = settings[key].value;
|
||||||
|
} else {
|
||||||
|
settingsStorage[key] = settings[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return convert === false ? settings : utils.prepareSettingsForAppMessage(settings);
|
localStorage.setItem('clay-settings', JSON.stringify(settingsStorage));
|
||||||
|
|
||||||
|
return convert === false ? settingsStorage : Clay.prepareSettingsForAppMessage(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {string} [prefix]
|
* @param {string} [prefix='data:text/html;charset=utf-8,']
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
Clay.encodeDataUri = function(input, prefix) {
|
Clay.encodeDataUri = function(input, prefix) {
|
||||||
prefix = typeof prefix !== 'undefined' ? prefix : 'data:text/html;base64,';
|
prefix = typeof prefix !== 'undefined' ? prefix : 'data:text/html;charset=utf-8,';
|
||||||
|
return prefix + encodeURIComponent(input);
|
||||||
|
};
|
||||||
|
|
||||||
if (window.btoa) {
|
/**
|
||||||
return prefix + encodeURIComponent(window.btoa(input));
|
* Converts the val into a type compatible with Pebble.sendAppMessage().
|
||||||
|
* - Strings will be returned without modification
|
||||||
|
* - Numbers will be returned without modification
|
||||||
|
* - Booleans will be converted to a 0 or 1
|
||||||
|
* - Arrays that contain strings will be returned without modification
|
||||||
|
* eg: ['one', 'two'] becomes ['one', 'two']
|
||||||
|
* - Arrays that contain numbers will be returned without modification
|
||||||
|
* eg: [1, 2] becomes [1, 2]
|
||||||
|
* - Arrays that contain booleans will be converted to a 0 or 1
|
||||||
|
* eg: [true, false] becomes [1, 0]
|
||||||
|
* - Arrays must be single dimensional
|
||||||
|
* - Objects that have a "value" property will apply the above rules to the type of
|
||||||
|
* value. If the value is a number or an array of numbers and the optional
|
||||||
|
* property: "precision" is provided, then the number will be multiplied by 10 to
|
||||||
|
* the power of precision (value * 10 ^ precision) and then floored.
|
||||||
|
* 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|undefined} [val.precision=0]
|
||||||
|
* @returns {number|string|Array}
|
||||||
|
*/
|
||||||
|
Clay.prepareForAppMessage = function(val) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* moves the decimal place of a number by precision then drop any remaining decimal
|
||||||
|
* places.
|
||||||
|
* @param {number} number
|
||||||
|
* @param {number} precision - number of decimal places to move
|
||||||
|
* @returns {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _normalizeToPrecision(number, precision) {
|
||||||
|
return Math.floor(number * Math.pow(10, precision || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// iOS doesn't have a window so we need to polyfil window.btoa
|
var result;
|
||||||
// extracted from https://github.com/inexorabletash/polyfill
|
|
||||||
var B64_ALPHABET =
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
||||||
input = String(input);
|
|
||||||
var position = 0;
|
|
||||||
var out = [];
|
|
||||||
var o1;
|
|
||||||
var o2;
|
|
||||||
var o3;
|
|
||||||
var e1;
|
|
||||||
var e2;
|
|
||||||
var e3;
|
|
||||||
var e4;
|
|
||||||
|
|
||||||
if (/[^\x00-\xFF]/.test(input)) { throw Error('InvalidCharacterError'); }
|
if (Array.isArray(val)) {
|
||||||
|
result = [];
|
||||||
|
val.forEach(function(item, index) {
|
||||||
|
result[index] = Clay.prepareForAppMessage(item);
|
||||||
|
});
|
||||||
|
} else if (typeof val === 'object' && val) {
|
||||||
|
if (typeof val.value === 'number') {
|
||||||
|
result = _normalizeToPrecision(val.value, val.precision);
|
||||||
|
} else if (Array.isArray(val.value)) {
|
||||||
|
result = val.value.map(function(item) {
|
||||||
|
if (typeof item === 'number') {
|
||||||
|
return _normalizeToPrecision(item, val.precision);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = Clay.prepareForAppMessage(val.value);
|
||||||
|
}
|
||||||
|
} else if (typeof val === 'boolean') {
|
||||||
|
result = val ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
result = val;
|
||||||
|
}
|
||||||
|
|
||||||
while (position < input.length) {
|
return result;
|
||||||
o1 = input.charCodeAt(position++);
|
};
|
||||||
o2 = input.charCodeAt(position++);
|
|
||||||
o3 = input.charCodeAt(position++);
|
|
||||||
|
|
||||||
// 111111 112222 222233 333333
|
/**
|
||||||
e1 = o1 >> 2;
|
* Converts a Clay settings dict into one that is compatible with
|
||||||
e2 = ((o1 & 0x3) << 4) | (o2 >> 4);
|
* Pebble.sendAppMessage(); It also uses the provided messageKeys to correctly
|
||||||
e3 = ((o2 & 0xf) << 2) | (o3 >> 6);
|
* assign arrays into individual keys
|
||||||
e4 = o3 & 0x3f;
|
* @see {prepareForAppMessage}
|
||||||
|
* @param {Object} settings
|
||||||
|
* @returns {{}}
|
||||||
|
*/
|
||||||
|
Clay.prepareSettingsForAppMessage = function(settings) {
|
||||||
|
|
||||||
if (position === input.length + 2) {
|
// flatten settings
|
||||||
e3 = 64;
|
var flatSettings = {};
|
||||||
e4 = 64;
|
Object.keys(settings).forEach(function(key) {
|
||||||
} else if (position === input.length + 1) {
|
var val = settings[key];
|
||||||
e4 = 64;
|
var matches = key.match(/(.+?)(?:\[(\d*)\])?$/);
|
||||||
|
|
||||||
|
if (!matches[2]) {
|
||||||
|
flatSettings[key] = val;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push(B64_ALPHABET.charAt(e1),
|
var position = parseInt(matches[2], 10);
|
||||||
B64_ALPHABET.charAt(e2),
|
key = matches[1];
|
||||||
B64_ALPHABET.charAt(e3),
|
|
||||||
B64_ALPHABET.charAt(e4));
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + encodeURIComponent(out.join(''));
|
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;
|
||||||
|
// });
|
||||||
|
|
||||||
|
result[key] = Clay.prepareForAppMessage(flatSettings[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Clay;
|
module.exports = Clay;
|
||||||
|
|||||||
+30
-9
@@ -1,32 +1,50 @@
|
|||||||
{
|
{
|
||||||
"name": "pebble-clay",
|
"name": "pebble-clay",
|
||||||
"version": "0.1.3",
|
"version": "1.0.2",
|
||||||
"description": "Pebble Config Framework",
|
"description": "Pebble Config Framework",
|
||||||
"main": "dist/clay.js",
|
|
||||||
"directories": {
|
|
||||||
"example": "example"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test-travis": "./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --single-run --browsers chromeTravisCI && ./node_modules/.bin/eslint ./",
|
"test-travis": "./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --single-run --browsers chromeTravisCI && ./node_modules/.bin/eslint ./",
|
||||||
"test-debug": "(export DEBUG=true && ./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --no-single-run)",
|
"test-debug": "(export DEBUG=true && ./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --no-single-run)",
|
||||||
"test": "./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --single-run",
|
"test": "./node_modules/.bin/gulp && ./node_modules/.bin/karma start ./test/karma.conf.js --single-run",
|
||||||
"lint": "./node_modules/.bin/eslint ./",
|
"lint": "./node_modules/.bin/eslint ./",
|
||||||
"build": "gulp",
|
"build": "gulp",
|
||||||
"dev": "gulp dev"
|
"dev": "gulp dev",
|
||||||
|
"pebble-clean": "rm -rf tmp src/js/index.js && pebble clean",
|
||||||
|
"pebble-publish": "npm run pebble-clean && npm run build && pebble build && pebble package publish && npm run pebble-clean",
|
||||||
|
"pebble-build": "npm run build && pebble build"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pebble/clay.git"
|
"url": "git+https://github.com/pebble/clay.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Pebble",
|
"pebble",
|
||||||
"Config"
|
"config",
|
||||||
|
"configuration",
|
||||||
|
"pebble-package"
|
||||||
],
|
],
|
||||||
"author": "Pebble Technology",
|
"author": "Pebble Technology",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/pebble/clay/issues"
|
"url": "https://github.com/pebble/clay/issues"
|
||||||
},
|
},
|
||||||
|
"pebble": {
|
||||||
|
"projectType": "package",
|
||||||
|
"sdkVersion": "3",
|
||||||
|
"targetPlatforms": [
|
||||||
|
"aplite",
|
||||||
|
"basalt",
|
||||||
|
"chalk",
|
||||||
|
"diorite",
|
||||||
|
"emery"
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"media": []
|
||||||
|
},
|
||||||
|
"capabilities": [
|
||||||
|
"configurable"
|
||||||
|
]
|
||||||
|
},
|
||||||
"homepage": "https://github.com/pebble/clay#readme",
|
"homepage": "https://github.com/pebble/clay#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.3.1",
|
"autoprefixer": "^6.3.1",
|
||||||
@@ -35,6 +53,7 @@
|
|||||||
"browserify-istanbul": "^0.2.1",
|
"browserify-istanbul": "^0.2.1",
|
||||||
"chai": "^3.4.1",
|
"chai": "^3.4.1",
|
||||||
"deamdify": "^0.2.0",
|
"deamdify": "^0.2.0",
|
||||||
|
"deepcopy": "^0.6.1",
|
||||||
"del": "^2.0.2",
|
"del": "^2.0.2",
|
||||||
"eslint": "^1.5.1",
|
"eslint": "^1.5.1",
|
||||||
"eslint-config-pebble": "^1.2.0",
|
"eslint-config-pebble": "^1.2.0",
|
||||||
@@ -68,5 +87,7 @@
|
|||||||
"vinyl-source-stream": "^1.1.0",
|
"vinyl-source-stream": "^1.1.0",
|
||||||
"watchify": "^3.7.0"
|
"watchify": "^3.7.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"@keegan-stoneware/simple-app-message": "^1.1.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "clay.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#define SIMPLE_APP_MESSAGE_NAMESPACE ("CLAY")
|
||||||
|
|
||||||
|
static ClayCallbacks s_callbacks;
|
||||||
|
|
||||||
|
static uint32_t prv_hash_key(const char *key) {
|
||||||
|
return hash((uint8_t *)key, (uint32_t)strlen(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool prv_read(const char *key, void *value_out, size_t size) {
|
||||||
|
uint32_t persist_key = prv_hash_key(key);
|
||||||
|
int bytes_read = persist_read_data(persist_key, value_out, size);
|
||||||
|
return bytes_read >= 0 && (size_t)bytes_read == size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool prv_write(const char *key, const void *data, size_t size) {
|
||||||
|
uint32_t persist_key = prv_hash_key(key);
|
||||||
|
int bytes_written = persist_write_data(persist_key, data, size);
|
||||||
|
return bytes_written >= 0 && (size_t)bytes_written == size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool prv_simple_dict_foreach_callback(
|
||||||
|
const char *key, SimpleDictDataType type, const void *data, size_t size, void *context) {
|
||||||
|
return prv_write(key, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prv_simple_app_message_received_callback(const SimpleDict *message, void *context) {
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
simple_dict_foreach(message, prv_simple_dict_foreach_callback, NULL);
|
||||||
|
s_callbacks.settings_updated(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- PUBLIC API -----
|
||||||
|
|
||||||
|
void clay_init(uint32_t inbox_size, bool open_app_message) {
|
||||||
|
const bool request_inbox_size_success = simple_app_message_request_inbox_size(inbox_size);
|
||||||
|
if (!request_inbox_size_success) {
|
||||||
|
APP_LOG(APP_LOG_LEVEL_ERROR, "CLAY: Failed to request inbox size of %d", (int)inbox_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!open_app_message) return;
|
||||||
|
|
||||||
|
const AppMessageResult open_state = simple_app_message_open();
|
||||||
|
if (open_state != APP_MSG_OK) {
|
||||||
|
APP_LOG(APP_LOG_LEVEL_ERROR, "CLAY: Failed to open appmessage inbox. Code: %d", open_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clay_register_callbacks(const ClayCallbacks *callbacks, void *context) {
|
||||||
|
s_callbacks = *callbacks;
|
||||||
|
|
||||||
|
const SimpleAppMessageCallbacks simple_app_message_callbacks = {
|
||||||
|
.message_received = prv_simple_app_message_received_callback,
|
||||||
|
};
|
||||||
|
const bool register_success = simple_app_message_register_callbacks(
|
||||||
|
SIMPLE_APP_MESSAGE_NAMESPACE, &simple_app_message_callbacks, context);
|
||||||
|
|
||||||
|
if (!register_success) {
|
||||||
|
APP_LOG(APP_LOG_LEVEL_ERROR, "CLAY: Failed to register callbacks for namespace %s",
|
||||||
|
SIMPLE_APP_MESSAGE_NAMESPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_get_int(const char *key, int *value_out) {
|
||||||
|
return prv_read(key, value_out, sizeof(*value_out));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_get_bool(const char *key, bool *value_out) {
|
||||||
|
return prv_read(key, value_out, sizeof(*value_out));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_get_string(const char *key, char *value_out, size_t size) {
|
||||||
|
return prv_read(key, value_out, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_get_data(const char *key, void *value_out, size_t size) {
|
||||||
|
return prv_read(key, value_out, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_set_int(const char *key, int *value) {
|
||||||
|
return prv_write(key, value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_set_bool(const char *key, bool *value) {
|
||||||
|
return prv_write(key, value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_set_data(const char *key, const void *value, size_t size) {
|
||||||
|
return prv_write(key, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clay_set_string(const char *key, const char *value) {
|
||||||
|
return prv_write(key, value, strlen(value));
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Based on DJB2 Hash
|
||||||
|
uint32_t hash(const uint8_t *bytes, const uint32_t length) {
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t c;
|
||||||
|
const uint8_t *last_byte = bytes + length;
|
||||||
|
while (bytes != last_byte) {
|
||||||
|
c = *bytes;
|
||||||
|
hash = ((hash << 5) + hash) + c;
|
||||||
|
bytes++;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t hash(const uint8_t *bytes, const uint32_t length);
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 135 KiB |
@@ -8,7 +8,6 @@ module.exports = {
|
|||||||
defaults: {
|
defaults: {
|
||||||
label: '',
|
label: '',
|
||||||
options: [],
|
options: [],
|
||||||
description: '',
|
description: ''
|
||||||
attributes: {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+224
-92
@@ -9,117 +9,226 @@ module.exports = {
|
|||||||
label: '',
|
label: '',
|
||||||
description: ''
|
description: ''
|
||||||
},
|
},
|
||||||
initialize: function(minified) {
|
initialize: function(minified, clay) {
|
||||||
var HTML = minified.HTML;
|
var HTML = minified.HTML;
|
||||||
var self = this;
|
var self = this;
|
||||||
var useSunlight = self.config.sunlight !== false;
|
|
||||||
var sunlightColorMap = {
|
|
||||||
'000000': '000000',
|
|
||||||
'000055': '001e41',
|
|
||||||
'0000aa': '004387',
|
|
||||||
'0000ff': '0068ca',
|
|
||||||
'005500': '2b4a2c',
|
|
||||||
'005555': '27514f',
|
|
||||||
'0055aa': '16638d',
|
|
||||||
'0055ff': '007dce',
|
|
||||||
'00aa00': '5e9860',
|
|
||||||
'00aa55': '5c9b72',
|
|
||||||
'00aaaa': '57a5a2',
|
|
||||||
'00aaff': '4cb4db',
|
|
||||||
'00ff00': '8ee391',
|
|
||||||
'00ff55': '8ee69e',
|
|
||||||
'00ffaa': '8aebc0',
|
|
||||||
'00ffff': '84f5f1',
|
|
||||||
'550000': '4a161b',
|
|
||||||
'550055': '482748',
|
|
||||||
'5500aa': '40488a',
|
|
||||||
'5500ff': '2f6bcc',
|
|
||||||
'555500': '564e36',
|
|
||||||
'555555': '545454',
|
|
||||||
'5555aa': '4f6790',
|
|
||||||
'5555ff': '4180d0',
|
|
||||||
'55aa00': '759a64',
|
|
||||||
'55aa55': '759d76',
|
|
||||||
'55aaaa': '71a6a4',
|
|
||||||
'55aaff': '69b5dd',
|
|
||||||
'55ff00': '9ee594',
|
|
||||||
'55ff55': '9de7a0',
|
|
||||||
'55ffaa': '9becc2',
|
|
||||||
'55ffff': '95f6f2',
|
|
||||||
'aa0000': '99353f',
|
|
||||||
'aa0055': '983e5a',
|
|
||||||
'aa00aa': '955694',
|
|
||||||
'aa00ff': '8f74d2',
|
|
||||||
'aa5500': '9d5b4d',
|
|
||||||
'aa5555': '9d6064',
|
|
||||||
'aa55aa': '9a7099',
|
|
||||||
'aa55ff': '9587d5',
|
|
||||||
'aaaa00': 'afa072',
|
|
||||||
'aaaa55': 'aea382',
|
|
||||||
'aaaaaa': 'ababab',
|
|
||||||
'ffffff': 'ffffff',
|
|
||||||
'aaaaff': 'a7bae2',
|
|
||||||
'aaff00': 'c9e89d',
|
|
||||||
'aaff55': 'c9eaa7',
|
|
||||||
'aaffaa': 'c7f0c8',
|
|
||||||
'aaffff': 'c3f9f7',
|
|
||||||
'ff0000': 'e35462',
|
|
||||||
'ff0055': 'e25874',
|
|
||||||
'ff00aa': 'e16aa3',
|
|
||||||
'ff00ff': 'de83dc',
|
|
||||||
'ff5500': 'e66e6b',
|
|
||||||
'ff5555': 'e6727c',
|
|
||||||
'ff55aa': 'e37fa7',
|
|
||||||
'ff55ff': 'e194df',
|
|
||||||
'ffaa00': 'f1aa86',
|
|
||||||
'ffaa55': 'f1ad93',
|
|
||||||
'ffaaaa': 'efb5b8',
|
|
||||||
'ffaaff': 'ecc3eb',
|
|
||||||
'ffff00': 'ffeeab',
|
|
||||||
'ffff55': 'fff1b5',
|
|
||||||
'ffffaa': 'fff6d3'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|boolean|number} color
|
* @param {string|boolean|number} color
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function cssColor(color) {
|
function cssColor(color) {
|
||||||
if (color === false) { return 'transparent'; }
|
|
||||||
if (typeof color === 'number') {
|
if (typeof color === 'number') {
|
||||||
color = color.toString(16);
|
color = color.toString(16);
|
||||||
|
} else if (!color) {
|
||||||
|
return 'transparent';
|
||||||
}
|
}
|
||||||
while (color.length < 6) {
|
|
||||||
color = '0' + color;
|
color = padColorString(color);
|
||||||
}
|
|
||||||
|
|
||||||
return '#' + (useSunlight ? sunlightColorMap[color] : color);
|
return '#' + (useSunlight ? sunlightColorMap[color] : color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} color
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function padColorString(color) {
|
||||||
|
color = color.toLowerCase();
|
||||||
|
|
||||||
|
while (color.length < 6) {
|
||||||
|
color = '0' + color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number|string} value
|
||||||
|
* @returns {string|*}
|
||||||
|
*/
|
||||||
|
function normalizeColor(value) {
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'number': return padColorString(value.toString(16));
|
||||||
|
case 'string': return value.replace(/^#|^0x/, '');
|
||||||
|
default: return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<Array>} layout
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function flattenLayout(layout) {
|
||||||
|
return layout.reduce(function(a, b) {
|
||||||
|
return a.concat(b);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert HEX color to LAB.
|
||||||
|
* Adapted from: https://github.com/antimatter15/rgb-lab
|
||||||
|
* @param {string} hex
|
||||||
|
* @returns {Array} - [l, a, b]
|
||||||
|
*/
|
||||||
|
function hex2lab(hex) {
|
||||||
|
hex = hex.replace(/^#|^0x/, '');
|
||||||
|
|
||||||
|
var r = parseInt(hex.slice(0, 2), 16) / 255;
|
||||||
|
var g = parseInt(hex.slice(2, 4), 16) / 255;
|
||||||
|
var b = parseInt(hex.slice(4), 16) / 255;
|
||||||
|
|
||||||
|
r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
||||||
|
g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
||||||
|
b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
||||||
|
|
||||||
|
var x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
|
||||||
|
var y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000;
|
||||||
|
var z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
|
||||||
|
|
||||||
|
x = (x > 0.008856) ? Math.pow(x, 1 / 3) : (7.787 * x) + 16 / 116;
|
||||||
|
y = (y > 0.008856) ? Math.pow(y, 1 / 3) : (7.787 * y) + 16 / 116;
|
||||||
|
z = (z > 0.008856) ? Math.pow(z, 1 / 3) : (7.787 * z) + 16 / 116;
|
||||||
|
|
||||||
|
return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the perceptual color distance between two LAB colors
|
||||||
|
* @param {Array} labA
|
||||||
|
* @param {Array} labB
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function deltaE(labA, labB) {
|
||||||
|
var deltaL = labA[0] - labB[0];
|
||||||
|
var deltaA = labA[1] - labB[1];
|
||||||
|
var deltaB = labA[2] - labB[2];
|
||||||
|
|
||||||
|
return Math.sqrt(Math.pow(deltaL, 2) +
|
||||||
|
Math.pow(deltaA, 2) +
|
||||||
|
Math.pow(deltaB, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layout based on the connected watch
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function autoLayout() {
|
||||||
|
if (!clay.meta.activeWatchInfo ||
|
||||||
|
clay.meta.activeWatchInfo.firmware.major === 2 ||
|
||||||
|
['aplite', 'diorite'].indexOf(clay.meta.activeWatchInfo.platform) > -1 &&
|
||||||
|
!self.config.allowGray) {
|
||||||
|
return standardLayouts.BLACK_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['aplite', 'diorite'].indexOf(clay.meta.activeWatchInfo.platform) > -1 &&
|
||||||
|
self.config.allowGray) {
|
||||||
|
return standardLayouts.GRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return standardLayouts.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number|string} color
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
self.roundColorToLayout = function(color) {
|
||||||
|
var itemValue = normalizeColor(color);
|
||||||
|
|
||||||
|
// if the color is not in the layout we will need find the closest match
|
||||||
|
if (colorList.indexOf(itemValue) === -1) {
|
||||||
|
var itemValueLAB = hex2lab(itemValue);
|
||||||
|
var differenceArr = colorList.map(function(color) {
|
||||||
|
var colorLAB = hex2lab(normalizeColor(color));
|
||||||
|
return deltaE(itemValueLAB, colorLAB);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the lowest number from the differenceArray
|
||||||
|
var lowest = Math.min.apply(Math, differenceArr);
|
||||||
|
|
||||||
|
// Get the index for that lowest number
|
||||||
|
var index = differenceArr.indexOf(lowest);
|
||||||
|
itemValue = colorList[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(itemValue, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
var useSunlight = self.config.sunlight !== false;
|
||||||
|
var sunlightColorMap = {
|
||||||
|
'000000': '000000', '000055': '001e41', '0000aa': '004387', '0000ff': '0068ca',
|
||||||
|
'005500': '2b4a2c', '005555': '27514f', '0055aa': '16638d', '0055ff': '007dce',
|
||||||
|
'00aa00': '5e9860', '00aa55': '5c9b72', '00aaaa': '57a5a2', '00aaff': '4cb4db',
|
||||||
|
'00ff00': '8ee391', '00ff55': '8ee69e', '00ffaa': '8aebc0', '00ffff': '84f5f1',
|
||||||
|
'550000': '4a161b', '550055': '482748', '5500aa': '40488a', '5500ff': '2f6bcc',
|
||||||
|
'555500': '564e36', '555555': '545454', '5555aa': '4f6790', '5555ff': '4180d0',
|
||||||
|
'55aa00': '759a64', '55aa55': '759d76', '55aaaa': '71a6a4', '55aaff': '69b5dd',
|
||||||
|
'55ff00': '9ee594', '55ff55': '9de7a0', '55ffaa': '9becc2', '55ffff': '95f6f2',
|
||||||
|
'aa0000': '99353f', 'aa0055': '983e5a', 'aa00aa': '955694', 'aa00ff': '8f74d2',
|
||||||
|
'aa5500': '9d5b4d', 'aa5555': '9d6064', 'aa55aa': '9a7099', 'aa55ff': '9587d5',
|
||||||
|
'aaaa00': 'afa072', 'aaaa55': 'aea382', 'aaaaaa': 'ababab', 'ffffff': 'ffffff',
|
||||||
|
'aaaaff': 'a7bae2', 'aaff00': 'c9e89d', 'aaff55': 'c9eaa7', 'aaffaa': 'c7f0c8',
|
||||||
|
'aaffff': 'c3f9f7', 'ff0000': 'e35462', 'ff0055': 'e25874', 'ff00aa': 'e16aa3',
|
||||||
|
'ff00ff': 'de83dc', 'ff5500': 'e66e6b', 'ff5555': 'e6727c', 'ff55aa': 'e37fa7',
|
||||||
|
'ff55ff': 'e194df', 'ffaa00': 'f1aa86', 'ffaa55': 'f1ad93', 'ffaaaa': 'efb5b8',
|
||||||
|
'ffaaff': 'ecc3eb', 'ffff00': 'ffeeab', 'ffff55': 'fff1b5', 'ffffaa': 'fff6d3'
|
||||||
|
};
|
||||||
|
|
||||||
/* eslint-disable comma-spacing, no-multi-spaces, max-len,
|
/* eslint-disable comma-spacing, no-multi-spaces, max-len,
|
||||||
standard/array-bracket-even-spacing */
|
standard/array-bracket-even-spacing */
|
||||||
var layout = self.config.layout || [
|
var standardLayouts = {
|
||||||
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
COLOR: [
|
||||||
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
||||||
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
||||||
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
||||||
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
||||||
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
||||||
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
||||||
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
||||||
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
||||||
];
|
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
||||||
|
],
|
||||||
|
GRAY: [
|
||||||
|
['000000', 'aaaaaa', 'ffffff']
|
||||||
|
],
|
||||||
|
BLACK_WHITE: [
|
||||||
|
['000000', 'ffffff']
|
||||||
|
]
|
||||||
|
};
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
|
var layout = self.config.layout || autoLayout();
|
||||||
|
|
||||||
|
if (typeof layout === 'string') {
|
||||||
|
layout = standardLayouts[layout];
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure layout is a 2D array
|
||||||
|
if (!Array.isArray(layout[0])) {
|
||||||
|
layout = [layout];
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorList = flattenLayout(layout).map(function(item) {
|
||||||
|
return normalizeColor(item);
|
||||||
|
}).filter(function(item) {
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
var grid = '';
|
var grid = '';
|
||||||
var itemWidth = 100 / layout[0].length;
|
var rows = layout.length;
|
||||||
var itemHeight = 100 / layout.length;
|
var cols = 0;
|
||||||
|
layout.forEach(function(row) {
|
||||||
|
cols = row.length > cols ? row.length : cols;
|
||||||
|
});
|
||||||
|
var itemWidth = 100 / cols;
|
||||||
|
var itemHeight = 100 / rows;
|
||||||
var $elem = self.$element;
|
var $elem = self.$element;
|
||||||
|
|
||||||
for (var i = 0; i < layout.length; i++) {
|
for (var i = 0; i < rows; i++) {
|
||||||
for (var j = 0; j < layout[i].length; j++) {
|
for (var j = 0; j < cols; j++) {
|
||||||
|
|
||||||
var color = layout[i][j];
|
var color = normalizeColor(layout[i][j]);
|
||||||
var selectable = (color ? ' selectable' : '');
|
var selectable = (color ? ' selectable' : '');
|
||||||
|
|
||||||
var roundedTL = (i === 0 && j === 0) || i === 0 && !layout[i][j - 1] ||
|
var roundedTL = (i === 0 && j === 0) || i === 0 && !layout[i][j - 1] ||
|
||||||
@@ -146,7 +255,7 @@ module.exports = {
|
|||||||
grid += '<i ' +
|
grid += '<i ' +
|
||||||
'class="color-box ' + selectable + roundedTL + roundedTR + roundedBL +
|
'class="color-box ' + selectable + roundedTL + roundedTR + roundedBL +
|
||||||
roundedBR + '" ' +
|
roundedBR + '" ' +
|
||||||
'data-value="' + parseInt(color, 16) + '" ' +
|
(color ? 'data-value="' + parseInt(color, 16) + '" ' : '') +
|
||||||
'style="' +
|
'style="' +
|
||||||
'width:' + itemWidth + '%; ' +
|
'width:' + itemWidth + '%; ' +
|
||||||
'height:' + itemHeight + '%; ' +
|
'height:' + itemHeight + '%; ' +
|
||||||
@@ -155,13 +264,34 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$elem.select('.color-box-container').add(HTML(grid));
|
// on very small layouts the boxes end up huge. The following adds extra padding
|
||||||
|
// to prevent them from being so big.
|
||||||
|
var extraPadding = 0;
|
||||||
|
if (cols === 3) {
|
||||||
|
extraPadding = 5;
|
||||||
|
}
|
||||||
|
if (cols === 2) {
|
||||||
|
extraPadding = 8;
|
||||||
|
}
|
||||||
|
var vPadding = (extraPadding * itemWidth / itemHeight) + '%';
|
||||||
|
var hPadding = extraPadding + '%';
|
||||||
|
$elem.select('.color-box-container')
|
||||||
|
.add(HTML(grid))
|
||||||
|
.set('$paddingTop', vPadding)
|
||||||
|
.set('$paddingRight', hPadding)
|
||||||
|
.set('$paddingBottom', vPadding)
|
||||||
|
.set('$paddingLeft', hPadding);
|
||||||
|
|
||||||
|
$elem.select('.color-box-wrap').set(
|
||||||
|
'$paddingBottom',
|
||||||
|
(itemWidth / itemHeight * 100) + '%'
|
||||||
|
);
|
||||||
|
|
||||||
var $valueDisplay = $elem.select('.value');
|
var $valueDisplay = $elem.select('.value');
|
||||||
var $picker = $elem.select('.picker-wrap');
|
var $picker = $elem.select('.picker-wrap');
|
||||||
var disabled = self.$manipulatorTarget.get('disabled');
|
var disabled = self.$manipulatorTarget.get('disabled');
|
||||||
|
|
||||||
$elem.select('label').on('click', function(ev) {
|
$elem.select('label').on('click', function() {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
$picker.set('show'); // toggle visibility
|
$picker.set('show'); // toggle visibility
|
||||||
}
|
}
|
||||||
@@ -191,5 +321,7 @@ module.exports = {
|
|||||||
disabled = false;
|
disabled = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// expose layout for testing
|
||||||
|
self._layout = layout;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ module.exports = {
|
|||||||
toggle: require('./toggle'),
|
toggle: require('./toggle'),
|
||||||
radiogroup: require('./radiogroup'),
|
radiogroup: require('./radiogroup'),
|
||||||
checkboxgroup: require('./checkboxgroup'),
|
checkboxgroup: require('./checkboxgroup'),
|
||||||
button: require('./button')
|
button: require('./button'),
|
||||||
|
slider: require('./slider')
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'slider',
|
||||||
|
template: require('../../templates/components/slider.tpl'),
|
||||||
|
style: require('../../styles/clay/components/slider.scss'),
|
||||||
|
manipulator: 'slider',
|
||||||
|
defaults: {
|
||||||
|
label: '',
|
||||||
|
description: '',
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
attributes: {}
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var $value = self.$element.select('.value');
|
||||||
|
var $valuePad = self.$element.select('.value-pad');
|
||||||
|
var $slider = self.$manipulatorTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value display
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function setValueDisplay() {
|
||||||
|
var value = self.get().toFixed(self.precision);
|
||||||
|
$value.set('value', value);
|
||||||
|
$valuePad.set('innerHTML', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var step = $slider.get('step');
|
||||||
|
step = step.toString(10).split('.')[1];
|
||||||
|
self.precision = step ? step.length : 0;
|
||||||
|
|
||||||
|
self.on('change', setValueDisplay);
|
||||||
|
$slider.on('|input', setValueDisplay);
|
||||||
|
setValueDisplay();
|
||||||
|
|
||||||
|
$value.on('|input', function() {
|
||||||
|
$valuePad.set('innerHTML', this.get('value'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$value.on('|change', function() {
|
||||||
|
self.set(this.get('value'));
|
||||||
|
setValueDisplay();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -28,7 +28,7 @@ var clayConfig = new ClayConfig(settings, config, $mainForm, clayMeta);
|
|||||||
$mainForm.on('submit', function() {
|
$mainForm.on('submit', function() {
|
||||||
// Set the return URL depending on the runtime environment
|
// Set the return URL depending on the runtime environment
|
||||||
location.href = returnTo +
|
location.href = returnTo +
|
||||||
encodeURIComponent(JSON.stringify(clayConfig.getSettings()));
|
encodeURIComponent(JSON.stringify(clayConfig.serialize()));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run the custom function in the context of the ClayConfig
|
// Run the custom function in the context of the ClayConfig
|
||||||
|
|||||||
+101
-40
@@ -5,12 +5,13 @@
|
|||||||
* @typedef {Object} Clay~ConfigItem
|
* @typedef {Object} Clay~ConfigItem
|
||||||
* @property {string} type
|
* @property {string} type
|
||||||
* @property {string|boolean|number} defaultValue
|
* @property {string|boolean|number} defaultValue
|
||||||
* @property {string} [appKey]
|
* @property {string} [messageKey]
|
||||||
* @property {string} [id]
|
* @property {string} [id]
|
||||||
* @property {string} [label]
|
* @property {string} [label]
|
||||||
* @property {Object} [attributes]
|
* @property {Object} [attributes]
|
||||||
* @property {Array} [options]
|
* @property {Array} [options]
|
||||||
* @property {Array} [items]
|
* @property {Array} [items]
|
||||||
|
* @property {Array} [capabilities]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var HTML = require('../vendor/minified').HTML;
|
var HTML = require('../vendor/minified').HTML;
|
||||||
@@ -33,10 +34,22 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var _settings = _.copyObj(settings);
|
var _settings = _.copyObj(settings);
|
||||||
var _items = [];
|
var _items;
|
||||||
var _itemsById = {};
|
var _itemsById;
|
||||||
var _itemsByAppKey = {};
|
var _itemsByMessageKey;
|
||||||
var _isBuilt = false;
|
var _isBuilt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the item arrays and objects
|
||||||
|
* @private
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function _initializeItems() {
|
||||||
|
_items = [];
|
||||||
|
_itemsById = {};
|
||||||
|
_itemsByMessageKey = {};
|
||||||
|
_isBuilt = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add item(s) to the config
|
* Add item(s) to the config
|
||||||
@@ -49,34 +62,36 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
item.forEach(function(item) {
|
item.forEach(function(item) {
|
||||||
_addItems(item, $container);
|
_addItems(item, $container);
|
||||||
});
|
});
|
||||||
} else if (item.type === 'section') {
|
} else if (utils.includesCapability(meta.activeWatchInfo, item.capabilities)) {
|
||||||
var $wrapper = HTML('<div class="section">');
|
if (item.type === 'section') {
|
||||||
$container.add($wrapper);
|
var $wrapper = HTML('<div class="section">');
|
||||||
_addItems(item.items, $wrapper);
|
$container.add($wrapper);
|
||||||
} else {
|
_addItems(item.items, $wrapper);
|
||||||
var _item = _.copyObj(item);
|
} else {
|
||||||
_item.clayId = _items.length;
|
var _item = _.copyObj(item);
|
||||||
|
_item.clayId = _items.length;
|
||||||
|
|
||||||
var clayItem = new ClayItem(_item).initialize(self);
|
var clayItem = new ClayItem(_item).initialize(self);
|
||||||
|
|
||||||
if (_item.id) {
|
if (_item.id) {
|
||||||
_itemsById[_item.id] = clayItem;
|
_itemsById[_item.id] = 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.messageKey] !== 'undefined' ?
|
||||||
|
_settings[_item.messageKey] :
|
||||||
|
(_item.defaultValue || '');
|
||||||
|
|
||||||
|
clayItem.set(value);
|
||||||
|
|
||||||
|
$container.add(clayItem.$element);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_item.appKey) {
|
|
||||||
_itemsByAppKey[_item.appKey] = 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] :
|
|
||||||
(_item.defaultValue || '');
|
|
||||||
|
|
||||||
clayItem.set(value);
|
|
||||||
|
|
||||||
$container.add(clayItem.$element);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +112,7 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.meta = meta;
|
self.meta = meta;
|
||||||
|
self.$rootContainer = $rootContainer;
|
||||||
|
|
||||||
self.EVENTS = {
|
self.EVENTS = {
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +127,21 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
* value set
|
* value set
|
||||||
* @const
|
* @const
|
||||||
*/
|
*/
|
||||||
AFTER_BUILD: 'AFTER_BUILD'
|
AFTER_BUILD: 'AFTER_BUILD',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if .build() is executed after the page has already been built and
|
||||||
|
* before the existing content is destroyed
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
BEFORE_DESTROY: 'BEFORE_DESTROY',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if .build() is executed after the page has already been built and after
|
||||||
|
* the existing content is destroyed
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
AFTER_DESTROY: 'AFTER_DESTROY'
|
||||||
};
|
};
|
||||||
utils.updateProperties(self.EVENTS, {writable: false});
|
utils.updateProperties(self.EVENTS, {writable: false});
|
||||||
|
|
||||||
@@ -124,12 +154,12 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} appKey
|
* @param {string} messageKey
|
||||||
* @returns {ClayItem}
|
* @returns {ClayItem}
|
||||||
*/
|
*/
|
||||||
self.getItemByAppKey = function(appKey) {
|
self.getItemByMessageKey = function(messageKey) {
|
||||||
_checkBuilt('getItemByAppKey');
|
_checkBuilt('getItemByMessageKey');
|
||||||
return _itemsByAppKey[appKey];
|
return _itemsByMessageKey[messageKey];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,10 +185,19 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
/**
|
/**
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
self.getSettings = function() {
|
self.serialize = function() {
|
||||||
_checkBuilt('getSettings');
|
_checkBuilt('serialize');
|
||||||
_.eachObj(_itemsByAppKey, function(appKey, item) {
|
|
||||||
_settings[appKey] = item.get();
|
_settings = {};
|
||||||
|
|
||||||
|
_.eachObj(_itemsByMessageKey, function(messageKey, item) {
|
||||||
|
_settings[messageKey] = {
|
||||||
|
value: item.get()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (item.precision) {
|
||||||
|
_settings[messageKey].precision = item.precision;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return _settings;
|
return _settings;
|
||||||
};
|
};
|
||||||
@@ -166,18 +205,40 @@ function ClayConfig(settings, config, $rootContainer, meta) {
|
|||||||
// @todo maybe don't do this and force the static method
|
// @todo maybe don't do this and force the static method
|
||||||
self.registerComponent = ClayConfig.registerComponent;
|
self.registerComponent = ClayConfig.registerComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empties the root container
|
||||||
|
* @returns {ClayConfig}
|
||||||
|
*/
|
||||||
|
self.destroy = function() {
|
||||||
|
var el = $rootContainer[0];
|
||||||
|
self.trigger(self.EVENTS.BEFORE_DESTROY);
|
||||||
|
while (el.firstChild) {
|
||||||
|
el.removeChild(el.firstChild);
|
||||||
|
}
|
||||||
|
_initializeItems();
|
||||||
|
self.trigger(self.EVENTS.AFTER_DESTROY);
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the config page. This must be run before any of the get methods can be run
|
* Build the config page. This must be run before any of the get methods can be run
|
||||||
|
* If you call this method after the page has already been built, teh page will be
|
||||||
|
* destroyed and built again.
|
||||||
* @returns {ClayConfig}
|
* @returns {ClayConfig}
|
||||||
*/
|
*/
|
||||||
self.build = function() {
|
self.build = function() {
|
||||||
|
if (_isBuilt) {
|
||||||
|
self.destroy();
|
||||||
|
}
|
||||||
self.trigger(self.EVENTS.BEFORE_BUILD);
|
self.trigger(self.EVENTS.BEFORE_BUILD);
|
||||||
_addItems(config, $rootContainer);
|
_addItems(self.config, $rootContainer);
|
||||||
_isBuilt = true;
|
_isBuilt = true;
|
||||||
self.trigger(self.EVENTS.AFTER_BUILD);
|
self.trigger(self.EVENTS.AFTER_BUILD);
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_initializeItems();
|
||||||
|
|
||||||
// attach event methods
|
// attach event methods
|
||||||
ClayEvents.call(self, $rootContainer);
|
ClayEvents.call(self, $rootContainer);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function ClayItem(config) {
|
|||||||
self.id = config.id || null;
|
self.id = config.id || null;
|
||||||
|
|
||||||
/** @type {string|null} */
|
/** @type {string|null} */
|
||||||
self.appKey = config.appKey || null;
|
self.messageKey = config.messageKey || null;
|
||||||
|
|
||||||
/** @type {Object} */
|
/** @type {Object} */
|
||||||
self.config = config;
|
self.config = config;
|
||||||
@@ -59,7 +59,7 @@ function ClayItem(config) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// attach event methods
|
// attach event methods
|
||||||
ClayEvents.call(self, self.$element);
|
ClayEvents.call(self, self.$manipulatorTarget);
|
||||||
|
|
||||||
// attach the manipulator methods to the clayItem
|
// attach the manipulator methods to the clayItem
|
||||||
_.eachObj(_component.manipulator, function(methodName, method) {
|
_.eachObj(_component.manipulator, function(methodName, method) {
|
||||||
|
|||||||
@@ -85,6 +85,21 @@ module.exports = {
|
|||||||
hide: hide,
|
hide: hide,
|
||||||
show: show
|
show: show
|
||||||
},
|
},
|
||||||
|
slider: {
|
||||||
|
get: function() {
|
||||||
|
return parseFloat(this.$manipulatorTarget.get('value'));
|
||||||
|
},
|
||||||
|
set: function(value) {
|
||||||
|
var initVal = this.get();
|
||||||
|
this.$manipulatorTarget.set('value', value);
|
||||||
|
if (this.get() === initVal) { return this; }
|
||||||
|
return this.trigger('change');
|
||||||
|
},
|
||||||
|
disable: disable,
|
||||||
|
enable: enable,
|
||||||
|
hide: hide,
|
||||||
|
show: show
|
||||||
|
},
|
||||||
checked: {
|
checked: {
|
||||||
get: function() {
|
get: function() {
|
||||||
return this.$manipulatorTarget.get('checked');
|
return this.$manipulatorTarget.get('checked');
|
||||||
@@ -118,24 +133,27 @@ module.exports = {
|
|||||||
checkboxgroup: {
|
checkboxgroup: {
|
||||||
get: function() {
|
get: function() {
|
||||||
var result = [];
|
var result = [];
|
||||||
this.$element.select('input:checked').each(function(item) {
|
this.$element.select('input').each(function(item) {
|
||||||
result.push(item.value);
|
result.push(!!item.checked);
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
set: function(values) {
|
set: function(values) {
|
||||||
var self = this;
|
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; }
|
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');
|
return self.trigger('change');
|
||||||
},
|
},
|
||||||
disable: disable,
|
disable: disable,
|
||||||
@@ -145,16 +163,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
get: function() {
|
get: function() {
|
||||||
return parseInt(this.$manipulatorTarget.get('value'), 16);
|
return parseInt(this.$manipulatorTarget.get('value'), 10) || 0;
|
||||||
},
|
},
|
||||||
set: function(value) {
|
set: function(value) {
|
||||||
switch (typeof value) {
|
value = this.roundColorToLayout(value || 0);
|
||||||
case 'number': value = value.toString(16); break;
|
|
||||||
case 'string': value = value.replace(/^#|^0x/, ''); break;
|
|
||||||
}
|
|
||||||
value = value || '000000';
|
|
||||||
|
|
||||||
if (this.get() === parseInt(value, 16)) { return this; }
|
if (this.get() === value) { return this; }
|
||||||
this.$manipulatorTarget.set('value', value);
|
this.$manipulatorTarget.set('value', value);
|
||||||
return this.trigger('change');
|
return this.trigger('change');
|
||||||
},
|
},
|
||||||
|
|||||||
+117
-44
@@ -18,53 +18,126 @@ module.exports.updateProperties = function(obj, descriptor) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
module.exports.capabilityMap = {
|
||||||
* Converts the val into a type compatible with Pebble.sendAppMessage().
|
PLATFORM_APLITE: {
|
||||||
* - Strings will be returned without modification
|
platforms: ['aplite'],
|
||||||
* - Numbers will be returned without modification
|
minFwMajor: 0,
|
||||||
* - Booleans will be converted to a 0 or 1
|
minFwMinor: 0
|
||||||
* - Arrays that contain strings will be split with a zero.
|
},
|
||||||
* eg: ['one', 'two'] becomes ['one', 0, 'two', 0]
|
PLATFORM_BASALT: {
|
||||||
* - Arrays that contain numbers will be returned without modification
|
platforms: ['basalt'],
|
||||||
* eg: [1, 2] becomes [1, 2]
|
minFwMajor: 0,
|
||||||
* - Arrays that contain booleans will be converted to a 0 or 1
|
minFwMinor: 0
|
||||||
* eg: [true, false] becomes [1, 0]
|
},
|
||||||
* - Arrays must be single dimensional
|
PLATFORM_CHALK: {
|
||||||
* @param {number|string|boolean|Array} val
|
platforms: ['chalk'],
|
||||||
* @returns {number|string|Array}
|
minFwMajor: 0,
|
||||||
*/
|
minFwMinor: 0
|
||||||
module.exports.prepareForAppMessage = function(val) {
|
},
|
||||||
var result;
|
PLATFORM_DIORITE: {
|
||||||
|
platforms: ['diorite'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
PLATFORM_EMERY: {
|
||||||
|
platforms: ['emery'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
BW: {
|
||||||
|
platforms: ['aplite', 'diorite'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
COLOR: {
|
||||||
|
platforms: ['basalt', 'chalk', 'emery'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
MICROPHONE: {
|
||||||
|
platforms: ['basalt', 'chalk', 'diorite', 'emery'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
SMARTSTRAP: {
|
||||||
|
platforms: ['basalt', 'chalk', 'diorite', 'emery'],
|
||||||
|
minFwMajor: 3,
|
||||||
|
minFwMinor: 4
|
||||||
|
},
|
||||||
|
SMARTSTRAP_POWER: {
|
||||||
|
platforms: ['basalt', 'chalk', 'emery'],
|
||||||
|
minFwMajor: 3,
|
||||||
|
minFwMinor: 4
|
||||||
|
},
|
||||||
|
HEALTH: {
|
||||||
|
platforms: ['basalt', 'chalk', 'diorite', 'emery'],
|
||||||
|
minFwMajor: 3,
|
||||||
|
minFwMinor: 10
|
||||||
|
},
|
||||||
|
RECT: {
|
||||||
|
platforms: ['aplite', 'basalt', 'diorite', 'emery'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
ROUND: {
|
||||||
|
platforms: ['chalk'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
DISPLAY_144x168: {
|
||||||
|
platforms: ['aplite', 'basalt', 'diorite'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
DISPLAY_180x180_ROUND: {
|
||||||
|
platforms: ['chalk'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
},
|
||||||
|
DISPLAY_200x228: {
|
||||||
|
platforms: ['emery'],
|
||||||
|
minFwMajor: 0,
|
||||||
|
minFwMinor: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (typeof val === 'boolean') {
|
/**
|
||||||
result = val ? 1 : 0;
|
* Checks if all of the provided capabilities are compatible with the watch
|
||||||
} else if (Array.isArray(val)) {
|
* @param {Object} activeWatchInfo
|
||||||
result = [];
|
* @param {Array<string>} [capabilities]
|
||||||
val.forEach(function(item) {
|
* @return {boolean}
|
||||||
var itemConverted = module.exports.prepareForAppMessage(item);
|
*/
|
||||||
result.push(itemConverted);
|
module.exports.includesCapability = function(activeWatchInfo, capabilities) {
|
||||||
if (typeof itemConverted === 'string') {
|
var notRegex = /^NOT_/;
|
||||||
result.push(0);
|
var result = [];
|
||||||
}
|
|
||||||
});
|
if (!capabilities || !capabilities.length) {
|
||||||
} else {
|
return true;
|
||||||
result = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
for (var i = capabilities.length - 1; i >= 0; i--) {
|
||||||
|
var capability = capabilities[i];
|
||||||
|
var mapping = module.exports.capabilityMap[capability.replace(notRegex, '')];
|
||||||
|
|
||||||
|
if (!mapping ||
|
||||||
|
mapping.platforms.indexOf(activeWatchInfo.platform) === -1 ||
|
||||||
|
mapping.minFwMajor > activeWatchInfo.firmware.major ||
|
||||||
|
mapping.minFwMajor === activeWatchInfo.firmware.major &&
|
||||||
|
mapping.minFwMinor > activeWatchInfo.firmware.minor
|
||||||
|
) {
|
||||||
|
result.push(!!capability.match(notRegex));
|
||||||
|
} else {
|
||||||
|
result.push(!capability.match(notRegex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.indexOf(false) === -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
module.exports.log = function(message) {
|
||||||
* Converts a Clay settings dict into one that is compatible with
|
console.log('Clay: ' + JSON.stringify(message));
|
||||||
* Pebble.sendAppMessage();
|
};
|
||||||
* @see {prepareForAppMessage}
|
|
||||||
* @param {Object} settings
|
module.exports.throw = function(error) {
|
||||||
* @returns {{}}
|
throw new Error('Clay: '+ error)
|
||||||
*/
|
|
||||||
module.exports.prepareSettingsForAppMessage = function(settings) {
|
|
||||||
var result = {};
|
|
||||||
Object.keys(settings).forEach(function(key) {
|
|
||||||
result[key] = module.exports.prepareForAppMessage(settings[key]);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ h6 {
|
|||||||
@include font-size(0.8);
|
@include font-size(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -107,6 +113,7 @@ label {
|
|||||||
|
|
||||||
.tap-highlight {
|
.tap-highlight {
|
||||||
@include tap-highlight();
|
@include tap-highlight();
|
||||||
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component {
|
.component {
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ $item-spacing-v: $base-height / 2;
|
|||||||
$item-spacing-h: 0.75rem;
|
$item-spacing-h: 0.75rem;
|
||||||
$small-component-width: $base-height * $golden;
|
$small-component-width: $base-height * $golden;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---- Colors ------
|
// ---- Colors ------
|
||||||
$color-orange: #ff4700;
|
$color-orange: #ff4700;
|
||||||
$color-orange-dark: #993d19;
|
$color-orange-dark: #993d19;
|
||||||
@@ -37,4 +35,5 @@ $color-gray-11: #f2f2f2;
|
|||||||
$button-padding: 0.6rem;
|
$button-padding: 0.6rem;
|
||||||
$button-padding-ios: 0.5rem;
|
$button-padding-ios: 0.5rem;
|
||||||
|
|
||||||
$box-shadow-small-components: $color-gray-1 0 0.1rem 0.1rem;
|
$box-shadow-small-components: 0 0.1rem 0.1rem $color-gray-1;
|
||||||
|
$box-shadow-large-components: 0 rem(3px) rem(15px) rgba(0,0,0,0.4);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
border-radius: $base-height / 2;
|
border-radius: $base-height / 2;
|
||||||
box-shadow: $box-shadow-small-components;
|
box-shadow: $box-shadow-small-components;
|
||||||
display: block;
|
display: block;
|
||||||
|
background: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-wrap {
|
.picker-wrap {
|
||||||
@@ -20,17 +21,25 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 1rem;
|
padding: $item-spacing-v $item-spacing-h /2;
|
||||||
background: rgba(0,0,0,0.7);
|
background: rgba(0, 0, 0, 0.65);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 70ms ease-in 175ms;
|
transition: opacity 100ms ease-in 175ms;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.picker {
|
.picker {
|
||||||
padding: 1rem;
|
padding: $item-spacing-v $item-spacing-h;
|
||||||
background: $color-gray-11;
|
background: $color-gray-4;
|
||||||
|
box-shadow: $box-shadow-large-components;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 26rem;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
@@ -46,7 +55,6 @@
|
|||||||
height: 0;
|
height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 0 100% 0; // overridden with inline style
|
padding: 0 0 100% 0; // overridden with inline style
|
||||||
margin: 0.6em 0 0;
|
|
||||||
|
|
||||||
.color-box-container {
|
.color-box-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -77,7 +85,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
transform: scale(1.15);
|
transform: scale(1.1);
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
box-shadow: #111 0 0 0.24rem;
|
box-shadow: #111 0 0 0.24rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
appearance: none;
|
||||||
|
min-height: $item-spacing-v + ($base-height + 0rem) ;
|
||||||
|
|
||||||
@include placeholder {
|
@include placeholder {
|
||||||
color: $color-gray-8;
|
color: $color-gray-8;
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: #000;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
margin:0;
|
margin:0;
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
@import "clay";
|
||||||
|
|
||||||
|
.component-slider {
|
||||||
|
|
||||||
|
.section & {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: $item-spacing-v;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 1rem;
|
||||||
|
display: block;
|
||||||
|
padding-right: $item-spacing-h;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-wrap {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value,
|
||||||
|
.value-pad {
|
||||||
|
display: block;
|
||||||
|
background: $color-gray-2;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
padding: $item-spacing-v / 2 $item-spacing-h / 2;
|
||||||
|
border: none;
|
||||||
|
vertical-align: baseline;
|
||||||
|
color: $color-white;
|
||||||
|
text-align: right;
|
||||||
|
margin: 0;
|
||||||
|
min-width: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-pad {
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: ' ';
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
max-width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left:0;
|
||||||
|
top:0
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap {
|
||||||
|
padding: 0 $item-spacing-h $item-spacing-v;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
$track-height: rem(3px);
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
min-width: 100%;
|
||||||
|
height: $base-height;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: $track-height;
|
||||||
|
background: $color-gray-6;
|
||||||
|
width: 100%;
|
||||||
|
top: $base-height / 2 - $track-height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
appearance: none;
|
||||||
|
position: relative;
|
||||||
|
height: $base-height;
|
||||||
|
margin: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-runnable-track {
|
||||||
|
border: none;
|
||||||
|
height: $base-height;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
appearance: none;
|
||||||
|
position: relative;
|
||||||
|
height: $base-height;
|
||||||
|
width: $base-height;
|
||||||
|
background-color: $color-orange;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1000px;
|
||||||
|
top: $base-height / 2 - $track-height / 2;
|
||||||
|
height: $track-height;
|
||||||
|
width: 1001px;
|
||||||
|
background: $color-orange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,14 +3,8 @@
|
|||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
{{each options}}
|
{{each options}}
|
||||||
<label class="tap-highlight">
|
<label class="tap-highlight">
|
||||||
<span class="label">{{{this.label}}}</span>
|
<span class="label">{{{this}}}</span>
|
||||||
<input
|
<input type="checkbox" value="1" name="clay-{{clayId}}" />
|
||||||
type="checkbox"
|
|
||||||
value="{{this.value}}"
|
|
||||||
name="clay-{{clayId}}"
|
|
||||||
data-manipulator-target
|
|
||||||
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
|
|
||||||
/>
|
|
||||||
<i></i>
|
<i></i>
|
||||||
</label>
|
</label>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
type="radio"
|
type="radio"
|
||||||
value="{{this.value}}"
|
value="{{this.value}}"
|
||||||
name="clay-{{clayId}}"
|
name="clay-{{clayId}}"
|
||||||
data-manipulator-target
|
|
||||||
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
|
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
|
||||||
/>
|
/>
|
||||||
<i></i>
|
<i></i>
|
||||||
|
|||||||
@@ -4,7 +4,15 @@
|
|||||||
<span class="value"></span>
|
<span class="value"></span>
|
||||||
<select data-manipulator-target {{each key: attributes}}{{key}}="{{this}}"{{/each}}>
|
<select data-manipulator-target {{each key: attributes}}{{key}}="{{this}}"{{/each}}>
|
||||||
{{each options}}
|
{{each options}}
|
||||||
<option value="{{this.value}}" class="item-select-option">{{this.label}}</option>
|
{{if Array.isArray(this.value)}}
|
||||||
|
<optgroup label="{{this.label}}">
|
||||||
|
{{each this.value}}
|
||||||
|
<option value="{{this.value}}" class="item-select-option">{{this.label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</optgroup>
|
||||||
|
{{else}}
|
||||||
|
<option value="{{this.value}}" class="item-select-option">{{this.label}}</option>
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<div class="component component-slider">
|
||||||
|
<label class="tap-highlight">
|
||||||
|
<span class="label-container">
|
||||||
|
<span class="label">{{{label}}}</span>
|
||||||
|
<span class="value-wrap">
|
||||||
|
<span class="value-pad"></span>
|
||||||
|
<input type="text" class="value" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="input">
|
||||||
|
<input
|
||||||
|
data-manipulator-target
|
||||||
|
class="slider"
|
||||||
|
type="range"
|
||||||
|
min="{{min}}"
|
||||||
|
max="{{max}}"
|
||||||
|
step="{{step}}"
|
||||||
|
{{each key: attributes}}{{key}}="{{this}}"{{/each}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{{if description}}
|
||||||
|
<div class="description">{{{description}}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
+72
-7
@@ -11,12 +11,13 @@ var componentRegistry = require('../src/scripts/lib/component-registry');
|
|||||||
var idCounter = 0;
|
var idCounter = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {Object} [extra] - add/replace keys in the meta
|
||||||
* @returns {{accountToken: string, watchToken: string, activeWatchInfo: {platform:
|
* @returns {{accountToken: string, watchToken: string, activeWatchInfo: {platform:
|
||||||
* string, model: string, language: string, firmware: {major: number, minor:
|
* string, model: string, language: string, firmware: {major: number, minor:
|
||||||
* number, patch: number, suffix: string}}}}
|
* number, patch: number, suffix: string}}, userData: {}}}
|
||||||
*/
|
*/
|
||||||
module.exports.meta = function() {
|
module.exports.meta = function(extra) {
|
||||||
return {
|
var result = {
|
||||||
accountToken: '0123456789abcdef0123456789abcdef',
|
accountToken: '0123456789abcdef0123456789abcdef',
|
||||||
watchToken: '0123456789abcdef0123456789abcdef',
|
watchToken: '0123456789abcdef0123456789abcdef',
|
||||||
activeWatchInfo: {
|
activeWatchInfo: {
|
||||||
@@ -29,8 +30,15 @@ module.exports.meta = function() {
|
|||||||
patch: 2,
|
patch: 2,
|
||||||
suffix: ''
|
suffix: ''
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
userData: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_.eachObj(extra || {}, function(key, val) {
|
||||||
|
result[key] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +53,7 @@ module.exports.configItem = function(config, autoRegister) {
|
|||||||
|
|
||||||
var result = _.extend({}, {
|
var result = _.extend({}, {
|
||||||
label: config.type + '-label',
|
label: config.type + '-label',
|
||||||
appKey: 'appKey-' + idCounter,
|
messageKey: 'messageKey-' + idCounter,
|
||||||
id: 'id-' + idCounter
|
id: 'id-' + idCounter
|
||||||
}, config);
|
}, config);
|
||||||
|
|
||||||
@@ -87,14 +95,15 @@ module.exports.config = function(types, autoRegister) {
|
|||||||
* @param {boolean} [build=true] - run the build method on the result
|
* @param {boolean} [build=true] - run the build method on the result
|
||||||
* @param {boolean} [autoRegister=true]
|
* @param {boolean} [autoRegister=true]
|
||||||
* @param {Object} [settings] - settings to pass to constructor
|
* @param {Object} [settings] - settings to pass to constructor
|
||||||
|
* @param {Object} [meta] - override the meta
|
||||||
* @returns {ClayConfig}
|
* @returns {ClayConfig}
|
||||||
*/
|
*/
|
||||||
module.exports.clayConfig = function(types, build, autoRegister, settings) {
|
module.exports.clayConfig = function(types, build, autoRegister, settings, meta) {
|
||||||
var clayConfig = new ClayConfig(
|
var clayConfig = new ClayConfig(
|
||||||
settings || {},
|
settings || {},
|
||||||
module.exports.config(types, autoRegister),
|
module.exports.config(types, autoRegister),
|
||||||
$(HTML('<div>')),
|
$(HTML('<div>')),
|
||||||
module.exports.meta()
|
module.exports.meta(meta)
|
||||||
);
|
);
|
||||||
return build === false ? clayConfig : clayConfig.build();
|
return build === false ? clayConfig : clayConfig.build();
|
||||||
};
|
};
|
||||||
@@ -106,6 +115,8 @@ module.exports.clayConfig = function(types, build, autoRegister, settings) {
|
|||||||
* @param {Object} [options] - Additional options to pass to Clay
|
* @param {Object} [options] - Additional options to pass to Clay
|
||||||
* @param {boolean} [options.autoHandleEvents] - If false, Clay will not
|
* @param {boolean} [options.autoHandleEvents] - If false, Clay will not
|
||||||
* automatically handle the 'showConfiguration' and 'webviewclosed' events
|
* 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]
|
* @param {boolean} [destroyLocalStorage=true]
|
||||||
* @return {Clay}
|
* @return {Clay}
|
||||||
*/
|
*/
|
||||||
@@ -115,3 +126,57 @@ module.exports.clay = function(config, customFn, options, destroyLocalStorage) {
|
|||||||
}
|
}
|
||||||
return new Clay(config, customFn, options);
|
return new Clay(config, customFn, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} keys
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
module.exports.messageKeys = function(keys) {
|
||||||
|
var counter = 10000;
|
||||||
|
var result = {};
|
||||||
|
|
||||||
|
keys.forEach(function(key) {
|
||||||
|
var matches = key.match(/(.+?)(?:\[(\d*)\])?$/);
|
||||||
|
var parsedKey = matches[1];
|
||||||
|
var length = parseInt(matches[2] || 1, 10);
|
||||||
|
|
||||||
|
result[parsedKey] = counter;
|
||||||
|
|
||||||
|
counter += length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} obj
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
module.exports.messageKeysObjToArray = function(obj) {
|
||||||
|
return Object.keys(obj).map(function(key) {
|
||||||
|
return Array.isArray(obj[key]) ?
|
||||||
|
key + '[' + obj[key].length + ']' :
|
||||||
|
key;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} expected
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
module.exports.messageKeysExpected = function(expected) {
|
||||||
|
var result = {};
|
||||||
|
var messageKeys = module.exports.messageKeysObjToArray(expected);
|
||||||
|
messageKeys = module.exports.messageKeys(messageKeys);
|
||||||
|
|
||||||
|
Object.keys(expected).forEach(function(key) {
|
||||||
|
var expectedVal = Array.isArray(expected[key]) ? expected[key] : [expected[key]];
|
||||||
|
expectedVal.forEach(function(val, index) {
|
||||||
|
if (typeof val !== 'undefined') {
|
||||||
|
result[messageKeys[key] + index] = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|||||||
+12
-1
@@ -12,6 +12,12 @@ module.exports = function(config) {
|
|||||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||||
frameworks: ['browserify', 'source-map-support', 'mocha'],
|
frameworks: ['browserify', 'source-map-support', 'mocha'],
|
||||||
|
|
||||||
|
client: {
|
||||||
|
mocha: {
|
||||||
|
timeout: 10000 // 10 sec because Travis struggles sometimes.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
browserify: {
|
browserify: {
|
||||||
debug: true,
|
debug: true,
|
||||||
transform: [
|
transform: [
|
||||||
@@ -47,7 +53,12 @@ module.exports = function(config) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
configure: function(bundle) {
|
||||||
|
bundle.on('prebundle', function() {
|
||||||
|
bundle.ignore('message_keys');
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// list of files / patterns to load in the browser
|
// list of files / patterns to load in the browser
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max-params": [0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,48 @@
|
|||||||
var assert = require('chai').assert;
|
var assert = require('chai').assert;
|
||||||
var fixture = require('../../fixture');
|
var fixture = require('../../fixture');
|
||||||
|
|
||||||
|
var sunlightColorMap = {
|
||||||
|
'000000': '000000', '000055': '001e41', '0000aa': '004387', '0000ff': '0068ca',
|
||||||
|
'005500': '2b4a2c', '005555': '27514f', '0055aa': '16638d', '0055ff': '007dce',
|
||||||
|
'00aa00': '5e9860', '00aa55': '5c9b72', '00aaaa': '57a5a2', '00aaff': '4cb4db',
|
||||||
|
'00ff00': '8ee391', '00ff55': '8ee69e', '00ffaa': '8aebc0', '00ffff': '84f5f1',
|
||||||
|
'550000': '4a161b', '550055': '482748', '5500aa': '40488a', '5500ff': '2f6bcc',
|
||||||
|
'555500': '564e36', '555555': '545454', '5555aa': '4f6790', '5555ff': '4180d0',
|
||||||
|
'55aa00': '759a64', '55aa55': '759d76', '55aaaa': '71a6a4', '55aaff': '69b5dd',
|
||||||
|
'55ff00': '9ee594', '55ff55': '9de7a0', '55ffaa': '9becc2', '55ffff': '95f6f2',
|
||||||
|
'aa0000': '99353f', 'aa0055': '983e5a', 'aa00aa': '955694', 'aa00ff': '8f74d2',
|
||||||
|
'aa5500': '9d5b4d', 'aa5555': '9d6064', 'aa55aa': '9a7099', 'aa55ff': '9587d5',
|
||||||
|
'aaaa00': 'afa072', 'aaaa55': 'aea382', 'aaaaaa': 'ababab', 'ffffff': 'ffffff',
|
||||||
|
'aaaaff': 'a7bae2', 'aaff00': 'c9e89d', 'aaff55': 'c9eaa7', 'aaffaa': 'c7f0c8',
|
||||||
|
'aaffff': 'c3f9f7', 'ff0000': 'e35462', 'ff0055': 'e25874', 'ff00aa': 'e16aa3',
|
||||||
|
'ff00ff': 'de83dc', 'ff5500': 'e66e6b', 'ff5555': 'e6727c', 'ff55aa': 'e37fa7',
|
||||||
|
'ff55ff': 'e194df', 'ffaa00': 'f1aa86', 'ffaa55': 'f1ad93', 'ffaaaa': 'efb5b8',
|
||||||
|
'ffaaff': 'ecc3eb', 'ffff00': 'ffeeab', 'ffff55': 'fff1b5', 'ffffaa': 'fff6d3'
|
||||||
|
};
|
||||||
|
|
||||||
|
/* eslint-disable comma-spacing, no-multi-spaces, max-len,
|
||||||
|
standard/array-bracket-even-spacing */
|
||||||
|
var standardLayouts = {
|
||||||
|
COLOR: [
|
||||||
|
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
||||||
|
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
||||||
|
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
||||||
|
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
||||||
|
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
||||||
|
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
||||||
|
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
||||||
|
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
||||||
|
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
||||||
|
],
|
||||||
|
GRAY: [
|
||||||
|
['000000', 'aaaaaa', 'ffffff']
|
||||||
|
],
|
||||||
|
BLACK_WHITE: [
|
||||||
|
['000000', 'ffffff']
|
||||||
|
]
|
||||||
|
};
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ClayItem} colorItem
|
* @param {ClayItem} colorItem
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
@@ -11,6 +53,182 @@ function openPicker(colorItem) {
|
|||||||
colorItem.$element.select('label')[0].click();
|
colorItem.$element.select('label')[0].click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} hex
|
||||||
|
* @returns {string} - eg: "rgb(1, 2, 3)"
|
||||||
|
*/
|
||||||
|
function hexToRgb(hex) {
|
||||||
|
var result = /^(?:#|0x)?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result ?
|
||||||
|
'rgb(' +
|
||||||
|
parseInt(result[1], 16) + ', ' +
|
||||||
|
parseInt(result[2], 16) + ', ' +
|
||||||
|
parseInt(result[3], 16) + ')' :
|
||||||
|
hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number|string} value
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function normalizeColor(value) {
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'number': value = value.toString(16); break;
|
||||||
|
case 'string': value = value.replace(/^#|^0x/, ''); break;
|
||||||
|
}
|
||||||
|
return value || '000000';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<Array>} layout
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function flattenLayout(layout) {
|
||||||
|
return layout.reduce(function(a, b) {
|
||||||
|
return a.concat(b);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array|string} [layout=color]
|
||||||
|
* @param {Array} [expectedColors]
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function testCustomLayout(layout, expectedColors) {
|
||||||
|
|
||||||
|
describe('layout: ' + JSON.stringify(layout || 'default'), function() {
|
||||||
|
|
||||||
|
describe('Normal', function() {
|
||||||
|
testColors(false, layout, expectedColors);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Sunlight', function() {
|
||||||
|
testColors(true, layout, expectedColors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} platform
|
||||||
|
* @param {Array} layout
|
||||||
|
* @param {boolean} allowGray
|
||||||
|
* @param {string} desc
|
||||||
|
* @param {Object} activeWatchInfo
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function testAutoLayout(platform, layout, allowGray, desc, activeWatchInfo) {
|
||||||
|
it('chooses the best layout for the ' + (desc || platform) +
|
||||||
|
' platform when allowGray is ' + allowGray,
|
||||||
|
function() {
|
||||||
|
var clayConfig = fixture.clayConfig(
|
||||||
|
[{type: 'color', allowGray: allowGray}],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
activeWatchInfo: activeWatchInfo
|
||||||
|
}
|
||||||
|
);
|
||||||
|
var colorItem = clayConfig.getItemsByType('color')[0];
|
||||||
|
assert.deepEqual(colorItem._layout, layout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number|string} input
|
||||||
|
* @param {number} expected
|
||||||
|
* @param {array|string} layout
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function testClosestToLayout(input, expected, layout) {
|
||||||
|
it('rounds ' + input + ' to ' + expected.toString(16) +
|
||||||
|
' in layout: ' + JSON.stringify(layout),
|
||||||
|
function() {
|
||||||
|
var clayConfig;
|
||||||
|
var colorItem;
|
||||||
|
|
||||||
|
clayConfig = fixture.clayConfig([{
|
||||||
|
type: 'color',
|
||||||
|
sunlight: false,
|
||||||
|
layout: layout
|
||||||
|
}]);
|
||||||
|
colorItem = clayConfig.getItemsByType('color')[0];
|
||||||
|
colorItem.set(input);
|
||||||
|
assert.strictEqual(colorItem.get(), expected);
|
||||||
|
|
||||||
|
clayConfig = fixture.clayConfig([{
|
||||||
|
type: 'color',
|
||||||
|
sunlight: false,
|
||||||
|
layout: layout
|
||||||
|
}]);
|
||||||
|
colorItem = clayConfig.getItemsByType('color')[0];
|
||||||
|
colorItem.set(input);
|
||||||
|
assert.strictEqual(colorItem.get(), expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} sunlight
|
||||||
|
* @param {Array|string} [layout=color]
|
||||||
|
* @param {Array} [expectedColors]
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function testColors(sunlight, layout, expectedColors) {
|
||||||
|
var clayConfig;
|
||||||
|
var colorItem;
|
||||||
|
var $colorBoxes;
|
||||||
|
var colors;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
clayConfig = fixture.clayConfig([
|
||||||
|
{
|
||||||
|
type: 'color',
|
||||||
|
layout: layout,
|
||||||
|
sunlight: sunlight
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
colorItem = clayConfig.getItemsByType('color')[0];
|
||||||
|
|
||||||
|
layout = layout || 'COLOR';
|
||||||
|
if (typeof layout === 'string') {
|
||||||
|
layout = standardLayouts[layout];
|
||||||
|
}
|
||||||
|
|
||||||
|
colors = expectedColors || flattenLayout(layout);
|
||||||
|
$colorBoxes = colorItem.$element.select('.color-box');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Has the correct items in the layout', function() {
|
||||||
|
colors.forEach(function(color, index) {
|
||||||
|
if (color === false) {
|
||||||
|
assert.strictEqual($colorBoxes[index].dataset.value, undefined);
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(
|
||||||
|
parseInt($colorBoxes[index].dataset.value, 10),
|
||||||
|
parseInt(color || 0, 16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the color correctly', function() {
|
||||||
|
colors.forEach(function(color, index) {
|
||||||
|
if (color === false) {
|
||||||
|
assert.strictEqual(
|
||||||
|
$colorBoxes[index].style.backgroundColor,
|
||||||
|
'transparent'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var normalizedColor = normalizeColor(color);
|
||||||
|
assert.strictEqual(
|
||||||
|
$colorBoxes[index].style.backgroundColor,
|
||||||
|
hexToRgb(sunlight ? sunlightColorMap[normalizedColor] : normalizedColor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe('component - color', function() {
|
describe('component - color', function() {
|
||||||
it('shows the sunlit colors in the value display', function() {
|
it('shows the sunlit colors in the value display', function() {
|
||||||
var clayConfig = fixture.clayConfig([
|
var clayConfig = fixture.clayConfig([
|
||||||
@@ -105,4 +323,180 @@ describe('component - color', function() {
|
|||||||
$picker[0].click();
|
$picker[0].click();
|
||||||
assert.strictEqual($picker.get('classList').contains('show'), false);
|
assert.strictEqual($picker.get('classList').contains('show'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('color rounding', function() {
|
||||||
|
var layout = 'GRAY';
|
||||||
|
testClosestToLayout('000000', 0x000000, layout);
|
||||||
|
testClosestToLayout('005500', 0x000000, layout);
|
||||||
|
testClosestToLayout('55aa55', 0xaaaaaa, layout);
|
||||||
|
testClosestToLayout('ffaaff', 0xaaaaaa, layout);
|
||||||
|
testClosestToLayout('ffffaa', 0xffffff, layout);
|
||||||
|
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||||
|
|
||||||
|
layout = 'BLACK_WHITE';
|
||||||
|
testClosestToLayout('000000', 0x000000, layout);
|
||||||
|
testClosestToLayout('005500', 0x000000, layout);
|
||||||
|
testClosestToLayout('55aa55', 0xffffff, layout);
|
||||||
|
testClosestToLayout('ffaaff', 0xffffff, layout);
|
||||||
|
testClosestToLayout('ffffaa', 0xffffff, layout);
|
||||||
|
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||||
|
|
||||||
|
layout = 'COLOR';
|
||||||
|
testClosestToLayout('000000', 0x000000, layout);
|
||||||
|
testClosestToLayout('005500', 0x005500, layout);
|
||||||
|
testClosestToLayout('55bb55', 0x55aa55, layout);
|
||||||
|
testClosestToLayout('ff99ff', 0xffaaff, layout);
|
||||||
|
testClosestToLayout('ffffee', 0xffffff, layout);
|
||||||
|
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||||
|
|
||||||
|
layout = ['ff0000', 'ff5500', 'ff00ff', '00ff00', '555555'];
|
||||||
|
testClosestToLayout('000000', 0x555555, layout);
|
||||||
|
testClosestToLayout('ff0000', 0xff0000, layout);
|
||||||
|
testClosestToLayout('ff1111', 0xff0000, layout);
|
||||||
|
testClosestToLayout('00aa00', 0x00ff00, layout);
|
||||||
|
testClosestToLayout('ff55ff', 0xff00ff, layout);
|
||||||
|
testClosestToLayout('aaaaaa', 0x555555, layout);
|
||||||
|
testClosestToLayout('ffffff', 0x555555, layout);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('layouts', function() {
|
||||||
|
|
||||||
|
// allow gray does nothing for 2.x
|
||||||
|
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, true, 'aplite (2.x)',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, true, 'aplite (2.x)', {
|
||||||
|
platform: 'aplite',
|
||||||
|
model: 'qemu_platform_aplite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 2,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (2.x)',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (2.x)', {
|
||||||
|
platform: 'aplite',
|
||||||
|
model: 'qemu_platform_aplite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 2,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (3.x)', {
|
||||||
|
platform: 'aplite',
|
||||||
|
model: 'qemu_platform_aplite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('aplite', standardLayouts.GRAY, true, 'aplite (3.x)', {
|
||||||
|
platform: 'aplite',
|
||||||
|
model: 'qemu_platform_aplite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('basalt', standardLayouts.COLOR, true, '', {
|
||||||
|
platform: 'basalt',
|
||||||
|
model: 'qemu_platform_basalt',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('chalk', standardLayouts.COLOR, true, '', {
|
||||||
|
platform: 'chalk',
|
||||||
|
model: 'qemu_platform_chalk',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('diorite', standardLayouts.BLACK_WHITE, false, '', {
|
||||||
|
platform: 'diorite',
|
||||||
|
model: 'qemu_platform_diorite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 4,
|
||||||
|
minor: 0,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testAutoLayout('diorite', standardLayouts.GRAY, true, '', {
|
||||||
|
platform: 'diorite',
|
||||||
|
model: 'qemu_platform_diorite',
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: 4,
|
||||||
|
minor: 0,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testCustomLayout();
|
||||||
|
testCustomLayout('COLOR');
|
||||||
|
testCustomLayout('GRAY');
|
||||||
|
testCustomLayout('BLACK_WHITE');
|
||||||
|
testCustomLayout(['aaffaa', '55ff55', '00ff00']);
|
||||||
|
testCustomLayout([false, '55ff55', '00ff00']);
|
||||||
|
testCustomLayout([['aaffaa', '55ff55', '00ff00']]);
|
||||||
|
testCustomLayout(
|
||||||
|
[['AAFFAA', '#55ff55', '0x00ff00', 0xff5500, null]],
|
||||||
|
['aaffaa', '55ff55', '00ff00', 'ff5500', false]
|
||||||
|
);
|
||||||
|
testCustomLayout([
|
||||||
|
['aaffaa', '55ff55', '00ff00'],
|
||||||
|
['005555', 'ffffff', '000000']]
|
||||||
|
);
|
||||||
|
testCustomLayout([
|
||||||
|
['aaffaa', '55ff55', '00ff00'],
|
||||||
|
[false, 'ffffff', false]]
|
||||||
|
);
|
||||||
|
testCustomLayout(
|
||||||
|
[
|
||||||
|
['aaffaa', '55ff55', '00ff00'],
|
||||||
|
[false, 'ffffff']
|
||||||
|
],
|
||||||
|
['aaffaa', '55ff55', '00ff00', false, 'ffffff', false]
|
||||||
|
);
|
||||||
|
testCustomLayout(
|
||||||
|
[
|
||||||
|
['aaffaa', '55ff55', false],
|
||||||
|
[false, 'ffffff']
|
||||||
|
],
|
||||||
|
['aaffaa', '55ff55', false, false, 'ffffff', false]
|
||||||
|
);
|
||||||
|
testCustomLayout(
|
||||||
|
[
|
||||||
|
['aaffaa', '55ff55'],
|
||||||
|
[false, 'ffffff', '000000']
|
||||||
|
],
|
||||||
|
['aaffaa', '55ff55', false, false, 'ffffff', '000000']
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ var HTML = require('../../../src/scripts/vendor/minified').HTML;
|
|||||||
var components = require('../../../src/scripts/components');
|
var components = require('../../../src/scripts/components');
|
||||||
var manipulators = require('../../../src/scripts/lib/manipulators');
|
var manipulators = require('../../../src/scripts/lib/manipulators');
|
||||||
var fixture = require('../../fixture');
|
var fixture = require('../../fixture');
|
||||||
|
var sinon = require('sinon');
|
||||||
|
|
||||||
var componentSchema = Joi.object().keys({
|
var componentSchema = Joi.object().keys({
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
@@ -39,6 +40,20 @@ describe('components', function() {
|
|||||||
it('is able to be passed to ClayConfig', function() {
|
it('is able to be passed to ClayConfig', function() {
|
||||||
fixture.clayConfig([component.name]);
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,4 +21,26 @@ describe('component - select', function() {
|
|||||||
selectItem.set('value-2');
|
selectItem.set('value-2');
|
||||||
assert.strictEqual($valueDisplay.get('innerHTML'), 'label 2');
|
assert.strictEqual($valueDisplay.get('innerHTML'), 'label 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the value display to the correct value on change when using optgroups',
|
||||||
|
function() {
|
||||||
|
var clayConfig = fixture.clayConfig([
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'value-1',
|
||||||
|
options: [
|
||||||
|
{ label: 'label 1', value: 'value-1' },
|
||||||
|
{ label: 'group', value: [
|
||||||
|
{ label: 'label 2', value: 'value-2' },
|
||||||
|
{ label: 'label 3', value: 'value-3' }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
var selectItem = clayConfig.getItemsByType('select')[0];
|
||||||
|
var $valueDisplay = selectItem.$element.select('.value');
|
||||||
|
assert.strictEqual($valueDisplay.get('innerHTML'), 'label 1');
|
||||||
|
selectItem.set('value-2');
|
||||||
|
assert.strictEqual($valueDisplay.get('innerHTML'), 'label 2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assert = require('chai').assert;
|
||||||
|
var fixture = require('../../fixture');
|
||||||
|
|
||||||
|
describe('component - slider', function() {
|
||||||
|
it('sets the value display to the correct value on set', function() {
|
||||||
|
var clayConfig = fixture.clayConfig(['slider']);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
var $valueDisplay = sliderItem.$element.select('.value');
|
||||||
|
var $valueDisplayPad = sliderItem.$element.select('.value-pad');
|
||||||
|
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '50');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '50');
|
||||||
|
sliderItem.set(75);
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '75');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '75');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the value display to the correct value on slider change', function() {
|
||||||
|
var clayConfig = fixture.clayConfig(['slider']);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
var $valueDisplay = sliderItem.$element.select('.value');
|
||||||
|
var $valueDisplayPad = sliderItem.$element.select('.value-pad');
|
||||||
|
var $slider = sliderItem.$element.select('.slider');
|
||||||
|
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '50');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '50');
|
||||||
|
$slider.set('value', 75).trigger('change');
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '75');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '75');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the value-pad to the same value as what the user is typing', function() {
|
||||||
|
var clayConfig = fixture.clayConfig(['slider']);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
var $valueDisplay = sliderItem.$element.select('.value');
|
||||||
|
var $valueDisplayPad = sliderItem.$element.select('.value-pad');
|
||||||
|
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '50');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '50');
|
||||||
|
$valueDisplay.set('value', 75).trigger('input');
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '75');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '75');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the slider to the correct value on user input', function() {
|
||||||
|
var clayConfig = fixture.clayConfig(['slider']);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
var $valueDisplay = sliderItem.$element.select('.value');
|
||||||
|
var $slider = sliderItem.$element.select('.slider');
|
||||||
|
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '50');
|
||||||
|
assert.strictEqual($slider.get('value'), '50');
|
||||||
|
$valueDisplay.set('value', 75).trigger('change');
|
||||||
|
assert.strictEqual($slider.get('value'), '75');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the precision correctly', function() {
|
||||||
|
var clayConfig = fixture.clayConfig([{
|
||||||
|
type: 'slider',
|
||||||
|
step: 0.25
|
||||||
|
}]);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
|
||||||
|
assert.strictEqual(sliderItem.precision, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('always displays decimal when precision is less than 1', function() {
|
||||||
|
var clayConfig = fixture.clayConfig([{
|
||||||
|
type: 'slider',
|
||||||
|
step: 0.05
|
||||||
|
}]);
|
||||||
|
var sliderItem = clayConfig.getItemsByType('slider')[0];
|
||||||
|
var $valueDisplay = sliderItem.$element.select('.value');
|
||||||
|
var $valueDisplayPad = sliderItem.$element.select('.value-pad');
|
||||||
|
var $slider = sliderItem.$element.select('.slider');
|
||||||
|
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '50.00');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '50.00');
|
||||||
|
assert.strictEqual($slider.get('value'), '50');
|
||||||
|
sliderItem.set(75);
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '75.00');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '75.00');
|
||||||
|
assert.strictEqual($slider.get('value'), '75');
|
||||||
|
sliderItem.set(33.25);
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '33.25');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '33.25');
|
||||||
|
assert.strictEqual($slider.get('value'), '33.25');
|
||||||
|
sliderItem.set(17.1);
|
||||||
|
assert.strictEqual($valueDisplay.get('value'), '17.10');
|
||||||
|
assert.strictEqual($valueDisplayPad.get('innerHTML'), '17.10');
|
||||||
|
assert.strictEqual($slider.get('value'), '17.1');
|
||||||
|
});
|
||||||
|
});
|
||||||
+281
-89
@@ -7,34 +7,40 @@ var standardComponents = require('../../src/scripts/components');
|
|||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var toSource = require('tosource');
|
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}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function stubPebble() {
|
function stubPebble() {
|
||||||
|
var meta = fixture.meta();
|
||||||
|
|
||||||
global.Pebble = {
|
global.Pebble = {
|
||||||
addEventListener: sinon.stub(),
|
addEventListener: sinon.stub(),
|
||||||
openURL: sinon.stub(),
|
openURL: sinon.stub(),
|
||||||
sendAppMessage: sinon.stub(),
|
sendAppMessage: sinon.stub(),
|
||||||
getActiveWatchInfo: sinon.stub().returns(activeWatchInfo),
|
getActiveWatchInfo: sinon.stub().returns(meta.activeWatchInfo),
|
||||||
getAccountToken: sinon.stub().returns(accountToken),
|
getAccountToken: sinon.stub().returns(meta.accountToken),
|
||||||
getWatchToken: sinon.stub().returns(watchToken)
|
getWatchToken: sinon.stub().returns(meta.watchToken)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} keys
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
function stubMessageKeys(keys) {
|
||||||
|
var messageKeys = require('message_keys');
|
||||||
|
Object.keys(messageKeys).forEach(function(key) {
|
||||||
|
delete messageKeys[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
var newKeys = fixture.messageKeys(keys);
|
||||||
|
Object.keys(newKeys).forEach(function(key) {
|
||||||
|
messageKeys[key] = newKeys[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return messageKeys;
|
||||||
|
}
|
||||||
|
|
||||||
describe('Clay', function() {
|
describe('Clay', function() {
|
||||||
describe('Clay constructor', function() {
|
describe('Clay constructor', function() {
|
||||||
it('throws if the config is not an array', function() {
|
it('throws if the config is not an array', function() {
|
||||||
@@ -43,6 +49,15 @@ describe('Clay', function() {
|
|||||||
}, /must be an Array/i);
|
}, /must be an Array/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws if the config contains appKeys', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
fixture.clay([
|
||||||
|
{type: 'input', appKey: 'foo'},
|
||||||
|
{type: 'someCustomComponent', appKey: 'bar'}
|
||||||
|
]);
|
||||||
|
}, /appKeys are no longer supported/i);
|
||||||
|
});
|
||||||
|
|
||||||
it('throws if customFn is not a function', function() {
|
it('throws if customFn is not a function', function() {
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
fixture.clay([], {});
|
fixture.clay([], {});
|
||||||
@@ -74,8 +89,6 @@ describe('Clay', function() {
|
|||||||
|
|
||||||
it('handles the "showConfiguration" event if autoHandleEvents is not false',
|
it('handles the "showConfiguration" event if autoHandleEvents is not false',
|
||||||
function() {
|
function() {
|
||||||
this.timeout(10000); // 10 seconds
|
|
||||||
|
|
||||||
stubPebble();
|
stubPebble();
|
||||||
var clay = fixture.clay([]);
|
var clay = fixture.clay([]);
|
||||||
|
|
||||||
@@ -94,12 +107,16 @@ describe('Clay', function() {
|
|||||||
stubPebble();
|
stubPebble();
|
||||||
fixture.clay([]);
|
fixture.clay([]);
|
||||||
var logStub = sinon.stub(console, 'log');
|
var logStub = sinon.stub(console, 'log');
|
||||||
|
var expected = {someSetting: 'value'};
|
||||||
|
|
||||||
|
stubMessageKeys(fixture.messageKeysObjToArray(expected));
|
||||||
Pebble.addEventListener
|
Pebble.addEventListener
|
||||||
.withArgs('webviewclosed')
|
.withArgs('webviewclosed')
|
||||||
.callArgWith(1, { response: '%7B%22appKey%22%3A%22value%22%7D' });
|
.callArgWith(1, {response: encodeURIComponent(JSON.stringify(expected))});
|
||||||
|
|
||||||
|
var expectedAppMessage = fixture.messageKeysExpected(expected);
|
||||||
assert(Pebble.addEventListener.calledWith('webviewclosed'));
|
assert(Pebble.addEventListener.calledWith('webviewclosed'));
|
||||||
assert(Pebble.sendAppMessage.calledWith({ appKey: 'value' }));
|
assert(Pebble.sendAppMessage.calledWith(expectedAppMessage));
|
||||||
|
|
||||||
Pebble.sendAppMessage.callArg(1);
|
Pebble.sendAppMessage.callArg(1);
|
||||||
assert(logStub.calledWith('Sent config data to Pebble'));
|
assert(logStub.calledWith('Sent config data to Pebble'));
|
||||||
@@ -135,6 +152,23 @@ describe('Clay', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('.version', function() {
|
||||||
|
it('has the correct version', function() {
|
||||||
|
var config = fixture.config(['input', 'text', 'color']);
|
||||||
|
var clay = fixture.clay(config);
|
||||||
|
assert.strictEqual(clay.version, require('../../package.json').version);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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() {
|
describe('.registerComponent()', function() {
|
||||||
it('adds the component to the this.components', function() {
|
it('adds the component to the this.components', function() {
|
||||||
var clay = fixture.clay([]);
|
var clay = fixture.clay([]);
|
||||||
@@ -156,13 +190,13 @@ describe('Clay', function() {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function decodeUrl(url) {
|
function decodeUrl(url) {
|
||||||
return atob(decodeURIComponent(url.replace(/^.*?[#,]/, '')));
|
return decodeURIComponent(url.replace(/^.*?[#,]/, ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('string substitutions', function() {
|
describe('string substitutions', function() {
|
||||||
var customFn = function() { this.getAllItems(); };
|
var customFn = function() { this.getAllItems(); };
|
||||||
var config = fixture.config(['input', 'color']);
|
var config = fixture.config(['input', 'color']);
|
||||||
var settings = { appKey: 'value' };
|
var settings = { messageKey: 'value' };
|
||||||
var clay;
|
var clay;
|
||||||
var html;
|
var html;
|
||||||
|
|
||||||
@@ -235,24 +269,60 @@ describe('Clay', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getSettings()', function() {
|
describe('.getSettings', function() {
|
||||||
it('stores the response to localStorage and returns the decoded data',
|
it('it writes to localStorage and returns the data when input is encoded',
|
||||||
function() {
|
function() {
|
||||||
var clay = fixture.clay([]);
|
var clay = fixture.clay([]);
|
||||||
var result = clay.getSettings('%7B%22appKey%22%3A%22value%22%7D');
|
var settings = encodeURIComponent(JSON.stringify({
|
||||||
assert.equal(localStorage.getItem('clay-settings'), '{"appKey":"value"}');
|
key1: 'value1',
|
||||||
assert.deepEqual(result, {appKey: 'value'});
|
key2: {value: 'value2'}
|
||||||
|
}));
|
||||||
|
var expected = {
|
||||||
|
key1: 'value1',
|
||||||
|
key2: 'value2'
|
||||||
|
};
|
||||||
|
|
||||||
|
stubMessageKeys(fixture.messageKeysObjToArray(expected));
|
||||||
|
|
||||||
|
var result = clay.getSettings(settings);
|
||||||
|
assert.equal(
|
||||||
|
localStorage.getItem('clay-settings'),
|
||||||
|
JSON.stringify(expected)
|
||||||
|
);
|
||||||
|
assert.deepEqual(result, fixture.messageKeysExpected(expected));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it writes to localStorage and returns the data when input is not encoded',
|
||||||
|
function() {
|
||||||
|
var clay = fixture.clay([]);
|
||||||
|
var settings = JSON.stringify({
|
||||||
|
key1: 'value1',
|
||||||
|
key2: {value: 'value2%7Dbreaks'}
|
||||||
|
});
|
||||||
|
var expected = {
|
||||||
|
key1: 'value1',
|
||||||
|
key2: 'value2%7Dbreaks'
|
||||||
|
};
|
||||||
|
|
||||||
|
stubMessageKeys(fixture.messageKeysObjToArray(expected));
|
||||||
|
|
||||||
|
var result = clay.getSettings(settings);
|
||||||
|
assert.equal(
|
||||||
|
localStorage.getItem('clay-settings'),
|
||||||
|
JSON.stringify(expected)
|
||||||
|
);
|
||||||
|
assert.deepEqual(result, fixture.messageKeysExpected(expected));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not store the response if it is invalid JSON and logs an error',
|
it('does not store the response if it is invalid JSON and logs an error',
|
||||||
function() {
|
function() {
|
||||||
var clay = fixture.clay([]);
|
var clay = fixture.clay([]);
|
||||||
localStorage.setItem('clay-settings', '{"appKey":"value"}');
|
localStorage.setItem('clay-settings', '{"messageKey":"value"}');
|
||||||
|
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
clay.getSettings('not valid JSON');
|
clay.getSettings('not valid JSON');
|
||||||
}, /Not Valid JSON/i);
|
}, /Not Valid JSON/i);
|
||||||
assert.equal(localStorage.getItem('clay-settings'), '{"appKey":"value"}');
|
assert.equal(localStorage.getItem('clay-settings'), '{"messageKey":"value"}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Prepares the settings for sendAppMessage', function() {
|
it('Prepares the settings for sendAppMessage', function() {
|
||||||
@@ -264,19 +334,34 @@ describe('Clay', function() {
|
|||||||
test4: ['cb-1', 'cb-3'],
|
test4: ['cb-1', 'cb-3'],
|
||||||
test5: 12345,
|
test5: 12345,
|
||||||
test6: [1, 2, 3, 4],
|
test6: [1, 2, 3, 4],
|
||||||
test7: [true, false, true]
|
test7: [true, false, true],
|
||||||
|
test8: {
|
||||||
|
precision: 2,
|
||||||
|
value: 12.34
|
||||||
|
},
|
||||||
|
test9: {
|
||||||
|
precision: 1,
|
||||||
|
value: [1, 2, 3, 4]
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
var expected = {
|
var expected = {
|
||||||
test1: 0,
|
test1: 0,
|
||||||
test2: 'val-2',
|
test2: 'val-2',
|
||||||
test3: 1,
|
test3: 1,
|
||||||
test4: ['cb-1', 0, 'cb-3', 0],
|
test4: ['cb-1', 'cb-3'],
|
||||||
test5: 12345,
|
test5: 12345,
|
||||||
test6: [1, 2, 3, 4],
|
test6: [1, 2, 3, 4],
|
||||||
test7: [1, 0, 1]
|
test7: [1, 0, 1],
|
||||||
|
test8: 1234,
|
||||||
|
test9: [10, 20, 30, 40]
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.deepEqual(clay.getSettings(response), expected);
|
stubMessageKeys(fixture.messageKeysObjToArray(expected));
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
clay.getSettings(response),
|
||||||
|
fixture.messageKeysExpected(expected)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not prepare the settings for sendAppMessage if convert is false',
|
it('does not prepare the settings for sendAppMessage if convert is false',
|
||||||
@@ -289,7 +374,11 @@ describe('Clay', function() {
|
|||||||
test4: ['cb-1', 'cb-3'],
|
test4: ['cb-1', 'cb-3'],
|
||||||
test5: 12345,
|
test5: 12345,
|
||||||
test6: [1, 2, 3, 4],
|
test6: [1, 2, 3, 4],
|
||||||
test7: [true, false, true]
|
test7: [true, false, true],
|
||||||
|
test8: {
|
||||||
|
precision: 2,
|
||||||
|
value: 12.34
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var response = encodeURIComponent(JSON.stringify(settings));
|
var response = encodeURIComponent(JSON.stringify(settings));
|
||||||
|
|
||||||
@@ -301,37 +390,34 @@ describe('Clay', function() {
|
|||||||
var emptyMeta = {
|
var emptyMeta = {
|
||||||
activeWatchInfo: null,
|
activeWatchInfo: null,
|
||||||
accountToken: '',
|
accountToken: '',
|
||||||
watchToken: ''
|
watchToken: '',
|
||||||
|
userData: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
it('populates the meta in the showConfiguration handler', function() {
|
it('populates the meta in the showConfiguration handler', function() {
|
||||||
stubPebble();
|
stubPebble();
|
||||||
var clay = fixture.clay([]);
|
var userData = {foo: 'bar'};
|
||||||
|
var clay = fixture.clay([], null, {userData: userData});
|
||||||
|
|
||||||
// meta only gets populated after showConfiguration happens
|
// meta only gets populated after showConfiguration happens
|
||||||
assert.deepEqual(clay.meta, emptyMeta);
|
assert.deepEqual(clay.meta, emptyMeta);
|
||||||
Pebble.addEventListener.withArgs('showConfiguration').callArg(1);
|
Pebble.addEventListener.withArgs('showConfiguration').callArg(1);
|
||||||
|
assert.deepEqual(clay.meta, fixture.meta({userData: userData}));
|
||||||
assert.deepEqual(clay.meta, {
|
|
||||||
activeWatchInfo: activeWatchInfo,
|
|
||||||
accountToken: accountToken,
|
|
||||||
watchToken: watchToken
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('populates the meta in the ready handler', function() {
|
it('populates the meta in the ready handler', function() {
|
||||||
stubPebble();
|
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
|
// meta only gets populated after ready happens
|
||||||
assert.deepEqual(clay.meta, emptyMeta);
|
assert.deepEqual(clay.meta, emptyMeta);
|
||||||
Pebble.addEventListener.withArgs('ready').callArg(1);
|
Pebble.addEventListener.withArgs('ready').callArg(1);
|
||||||
|
|
||||||
assert.deepEqual(clay.meta, {
|
assert.deepEqual(clay.meta, fixture.meta({userData: userData}));
|
||||||
activeWatchInfo: activeWatchInfo,
|
|
||||||
accountToken: accountToken,
|
|
||||||
watchToken: watchToken
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('populates the meta with with empty values when there is no Pebble global',
|
it('populates the meta with with empty values when there is no Pebble global',
|
||||||
@@ -344,49 +430,155 @@ describe('Clay', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Clay.encodeDataUri()', function() {
|
describe('Clay.encodeDataUri()', function() {
|
||||||
|
it('adds the correct prefix', function() {
|
||||||
/**
|
assert.equal(Clay.encodeDataUri('<test>', 'prefix:'), 'prefix:%3Ctest%3E');
|
||||||
* @return {void}
|
assert.equal(
|
||||||
*/
|
Clay.encodeDataUri('<test>'),
|
||||||
function testEncodeDataUri() {
|
'data:text/html;charset=utf-8,%3Ctest%3E'
|
||||||
it('adds the correct prefix', function() {
|
);
|
||||||
assert.equal(Clay.encodeDataUri('test', 'prefix:'), 'prefix:dGVzdA%3D%3D');
|
|
||||||
assert.equal(
|
|
||||||
Clay.encodeDataUri('test'),
|
|
||||||
'data:text/html;base64,dGVzdA%3D%3D'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('encodes the data correctly', function() {
|
|
||||||
assert.equal(Clay.encodeDataUri('test', ''), 'dGVzdA%3D%3D');
|
|
||||||
assert.equal(Clay.encodeDataUri('test{2}', ''), 'dGVzdHsyfQ%3D%3D');
|
|
||||||
assert.equal(Clay.encodeDataUri('test{10}', ''), 'dGVzdHsxMH0%3D');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if the input is invalid', function() {
|
|
||||||
assert.throws(function() {
|
|
||||||
Clay.encodeDataUri('♥');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('native', function() {
|
|
||||||
testEncodeDataUri();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('polyfill', function() {
|
it('encodes the data correctly', function() {
|
||||||
var btoaOriginal = window.btoa;
|
assert.equal(Clay.encodeDataUri('test', ''), 'test');
|
||||||
|
assert.equal(Clay.encodeDataUri('test{2}', ''), 'test%7B2%7D');
|
||||||
before(function() {
|
assert.equal(Clay.encodeDataUri('test{10}', ''), 'test%7B10%7D');
|
||||||
window.btoa = undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
testEncodeDataUri();
|
|
||||||
|
|
||||||
after(function() {
|
|
||||||
window.btoa = btoaOriginal;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not throw if unicode character is provided', function() {
|
||||||
|
assert.doesNotThrow(function() {
|
||||||
|
Clay.encodeDataUri('♥');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.prepareForAppMessage', function() {
|
||||||
|
it('converts an array correctly when array contains strings', function() {
|
||||||
|
assert.deepEqual(
|
||||||
|
Clay.prepareForAppMessage(['one', 'two', 'three']),
|
||||||
|
['one', 'two', 'three']
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
Clay.prepareForAppMessage([{value: 'one'}, 'two', 'three']),
|
||||||
|
['one', 'two', 'three']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts an array correctly when array contains numbers', function() {
|
||||||
|
assert.deepEqual(Clay.prepareForAppMessage([1, 2, 3]), [1, 2, 3]);
|
||||||
|
assert.deepEqual(Clay.prepareForAppMessage(
|
||||||
|
{value: [1.5, 2.34, 3], precision: 2}),
|
||||||
|
[150, 234, 300]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts an array correctly when array contains booleans', function() {
|
||||||
|
assert.deepEqual(Clay.prepareForAppMessage([true, false, true]), [1, 0, 1]);
|
||||||
|
assert.deepEqual(
|
||||||
|
Clay.prepareForAppMessage([{value: true}, false, true]),
|
||||||
|
[1, 0, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts booleans to ints', function() {
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage(false), 0);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage(true), 1);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({value: false}), 0);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({value: true}), 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves strings alone', function() {
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage('test'), 'test');
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({value: 'test'}), 'test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Multiples the number by the precision', function() {
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage(123), 123);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({
|
||||||
|
value: 1.23,
|
||||||
|
precision: 3
|
||||||
|
}), 1230);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({
|
||||||
|
value: 123,
|
||||||
|
precision: 2
|
||||||
|
}), 12300);
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({
|
||||||
|
value: 1.23456,
|
||||||
|
precision: 4
|
||||||
|
}), 12345);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores precision for anything but numbers', function() {
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({
|
||||||
|
value: 'not a number',
|
||||||
|
precision: 3
|
||||||
|
}), 'not a number');
|
||||||
|
|
||||||
|
assert.deepEqual(Clay.prepareForAppMessage({
|
||||||
|
value: ['not', 'a', 'number'],
|
||||||
|
precision: 3
|
||||||
|
}), ['not', 'a', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats an undefined precision as 0', function() {
|
||||||
|
assert.strictEqual(Clay.prepareForAppMessage({
|
||||||
|
value: 12.45,
|
||||||
|
precision: undefined
|
||||||
|
}), 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles sparse arrays', function() {
|
||||||
|
var sparseArr = [];
|
||||||
|
sparseArr[1] = 'two';
|
||||||
|
sparseArr[2] = 'three';
|
||||||
|
assert.deepEqual(Clay.prepareForAppMessage(sparseArr), sparseArr);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.prepareSettingsForAppMessage', function() {
|
||||||
|
it('converts the settings correctly', function() {
|
||||||
|
var settings = {
|
||||||
|
test1: false,
|
||||||
|
'test2[0]': 'val-1',
|
||||||
|
'test2[1]': 'val-2',
|
||||||
|
test3: true,
|
||||||
|
test4: ['cb-1', 'cb-3'],
|
||||||
|
test5: 12345,
|
||||||
|
test6: [1, 2, 3, 4],
|
||||||
|
test7: [true, false, true],
|
||||||
|
test8: {
|
||||||
|
precision: 2,
|
||||||
|
value: 12.34
|
||||||
|
},
|
||||||
|
'test9[1]': 'foo'
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = {
|
||||||
|
test1: 0,
|
||||||
|
test2: ['val-1', 'val-2'],
|
||||||
|
test3: 1,
|
||||||
|
test4: ['cb-1', 'cb-3'],
|
||||||
|
test5: 12345,
|
||||||
|
test6: [1, 2, 3, 4],
|
||||||
|
test7: [1, 0, 1],
|
||||||
|
test8: 1234,
|
||||||
|
test9: [undefined, 'foo']
|
||||||
|
};
|
||||||
|
|
||||||
|
stubMessageKeys(fixture.messageKeysObjToArray(expected));
|
||||||
|
|
||||||
|
var result = Clay.prepareSettingsForAppMessage(settings);
|
||||||
|
|
||||||
|
assert.deepEqual(result, fixture.messageKeysExpected(expected));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if a 2 dimension array is present', function() {
|
||||||
|
var settings = {
|
||||||
|
'test1[1]': ['bad', 'developer!']
|
||||||
|
};
|
||||||
|
|
||||||
|
stubMessageKeys(['test1[2]']);
|
||||||
|
|
||||||
|
assert.throws(function() {
|
||||||
|
Clay.prepareSettingsForAppMessage(settings);
|
||||||
|
}, /2 dimensional array/i);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+316
-32
@@ -12,10 +12,10 @@ describe('ClayConfig', function() {
|
|||||||
it('defines read-only properties', function() {
|
it('defines read-only properties', function() {
|
||||||
var properties = [
|
var properties = [
|
||||||
'EVENTS',
|
'EVENTS',
|
||||||
'getItemByAppKey',
|
'getItemByMessageKey',
|
||||||
'getItemById',
|
'getItemById',
|
||||||
'getItemsByType',
|
'getItemsByType',
|
||||||
'getSettings',
|
'serialize',
|
||||||
'registerComponent',
|
'registerComponent',
|
||||||
'build',
|
'build',
|
||||||
'on',
|
'on',
|
||||||
@@ -27,6 +27,191 @@ describe('ClayConfig', function() {
|
|||||||
checkReadOnly(clayConfig, properties);
|
checkReadOnly(clayConfig, properties);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the provided capabilities of the platform match
|
||||||
|
* @param {string} platform
|
||||||
|
* @param {number} fwMajor
|
||||||
|
* @param {number} fwMinor
|
||||||
|
* @param {Array} capabilities
|
||||||
|
* @param {number} expected
|
||||||
|
* @param {number} [notExpected] - defaults to the inverse of `expected`
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function testCapabilities(platform, fwMajor, fwMinor, capabilities, expected,
|
||||||
|
notExpected) {
|
||||||
|
it(platform + ' is' + (expected ? '' : ' not') + ' included on firmware ' +
|
||||||
|
fwMajor + '.' + fwMinor + ' for capabilities ' + JSON.stringify(capabilities),
|
||||||
|
function() {
|
||||||
|
var item = {
|
||||||
|
type: 'input',
|
||||||
|
capabilities: capabilities
|
||||||
|
};
|
||||||
|
var meta = {
|
||||||
|
activeWatchInfo: {
|
||||||
|
platform: platform,
|
||||||
|
model: 'qemu_platform_' + platform,
|
||||||
|
language: 'en_US',
|
||||||
|
firmware: {
|
||||||
|
major: fwMajor,
|
||||||
|
minor: fwMinor,
|
||||||
|
patch: 0,
|
||||||
|
suffix: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var clayConfig = fixtures.clayConfig([item], true, true, null, meta);
|
||||||
|
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, expected);
|
||||||
|
|
||||||
|
// now we test the NOT_ prefix
|
||||||
|
item.capabilities = item.capabilities.map(function(capability) {
|
||||||
|
return 'NOT_' + capability.replace(/^NOT_/, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
notExpected = typeof notExpected !== 'undefined' ? notExpected : ~expected + 2;
|
||||||
|
clayConfig = fixtures.clayConfig([item], true, true, null, meta);
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, notExpected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('capability filtering', function() {
|
||||||
|
testCapabilities('aplite', 2, 9, ['BW'], 1);
|
||||||
|
testCapabilities('aplite', 2, 9, ['COLOR'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['MICROPHONE'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['RECT'], 1);
|
||||||
|
testCapabilities('aplite', 2, 9, ['ROUND'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['DISPLAY_144x168'], 1);
|
||||||
|
testCapabilities('aplite', 2, 9, ['DISPLAY_180x180_ROUND'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['DISPLAY_200x228'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['PLATFORM_APLITE'], 1);
|
||||||
|
testCapabilities('aplite', 2, 9, ['PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['PLATFORM_CHALK'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['PLATFORM_DIORITE'], 0);
|
||||||
|
testCapabilities('aplite', 2, 9, ['PLATFORM_EMERY'], 0);
|
||||||
|
|
||||||
|
testCapabilities('aplite', 3, 10, ['BW'], 1);
|
||||||
|
testCapabilities('aplite', 3, 10, ['COLOR'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['MICROPHONE'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('aplite', 3, 4, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('aplite', 3, 3, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['HEALTH'], 0);
|
||||||
|
testCapabilities('aplite', 3, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['RECT'], 1);
|
||||||
|
testCapabilities('aplite', 3, 10, ['ROUND'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['DISPLAY_144x168'], 1);
|
||||||
|
testCapabilities('aplite', 3, 10, ['DISPLAY_180x180_ROUND'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['DISPLAY_200x228'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_APLITE'], 1);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_CHALK'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_DIORITE'], 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_EMERY'], 0);
|
||||||
|
|
||||||
|
testCapabilities('basalt', 3, 10, ['BW'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['COLOR'], 1);
|
||||||
|
testCapabilities('basalt', 3, 10, ['MICROPHONE'], 1);
|
||||||
|
testCapabilities('basalt', 3, 10, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('basalt', 3, 4, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('basalt', 3, 3, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('basalt', 3, 4, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('basalt', 3, 3, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['HEALTH'], 1);
|
||||||
|
testCapabilities('basalt', 3, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['RECT'], 1);
|
||||||
|
testCapabilities('basalt', 3, 10, ['ROUND'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['DISPLAY_144x168'], 1);
|
||||||
|
testCapabilities('basalt', 3, 10, ['DISPLAY_180x180_ROUND'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['DISPLAY_200x228'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['PLATFORM_APLITE'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['PLATFORM_BASALT'], 1);
|
||||||
|
testCapabilities('basalt', 3, 10, ['PLATFORM_CHALK'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['PLATFORM_DIORITE'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['PLATFORM_EMERY'], 0);
|
||||||
|
|
||||||
|
testCapabilities('chalk', 3, 10, ['BW'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['COLOR'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['MICROPHONE'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('chalk', 3, 4, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('chalk', 3, 3, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('chalk', 3, 4, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('chalk', 3, 3, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['HEALTH'], 1);
|
||||||
|
testCapabilities('chalk', 3, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['RECT'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['ROUND'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['DISPLAY_144x168'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['DISPLAY_180x180_ROUND'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['DISPLAY_200x228'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_APLITE'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_CHALK'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_DIORITE'], 0);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_EMERY'], 0);
|
||||||
|
|
||||||
|
testCapabilities('diorite', 3, 10, ['BW'], 1);
|
||||||
|
testCapabilities('diorite', 3, 10, ['COLOR'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['MICROPHONE'], 1);
|
||||||
|
testCapabilities('diorite', 3, 10, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('diorite', 3, 4, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('diorite', 3, 3, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('diorite', 3, 4, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('diorite', 3, 3, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['HEALTH'], 1);
|
||||||
|
testCapabilities('diorite', 3, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['RECT'], 1);
|
||||||
|
testCapabilities('diorite', 3, 10, ['ROUND'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['DISPLAY_144x168'], 1);
|
||||||
|
testCapabilities('diorite', 3, 10, ['DISPLAY_180x180_ROUND'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['DISPLAY_200x228'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['PLATFORM_APLITE'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['PLATFORM_CHALK'], 0);
|
||||||
|
testCapabilities('diorite', 3, 10, ['PLATFORM_DIORITE'], 1);
|
||||||
|
testCapabilities('diorite', 3, 10, ['PLATFORM_EMERY'], 0);
|
||||||
|
|
||||||
|
testCapabilities('emery', 3, 10, ['BW'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['COLOR'], 1);
|
||||||
|
testCapabilities('emery', 3, 10, ['MICROPHONE'], 1);
|
||||||
|
testCapabilities('emery', 3, 10, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('emery', 3, 4, ['SMARTSTRAP'], 1);
|
||||||
|
testCapabilities('emery', 3, 3, ['SMARTSTRAP'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('emery', 3, 4, ['SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('emery', 3, 3, ['SMARTSTRAP_POWER'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['HEALTH'], 1);
|
||||||
|
testCapabilities('emery', 3, 9, ['HEALTH'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['RECT'], 1);
|
||||||
|
testCapabilities('emery', 3, 10, ['ROUND'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['DISPLAY_144x168'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['DISPLAY_180x180_ROUND'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['DISPLAY_200x228'], 1);
|
||||||
|
testCapabilities('emery', 3, 10, ['PLATFORM_APLITE'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['PLATFORM_CHALK'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['PLATFORM_DIORITE'], 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['PLATFORM_EMERY'], 1);
|
||||||
|
|
||||||
|
testCapabilities('aplite', 3, 10, ['BW', 'HEALTH'], 0, 0);
|
||||||
|
testCapabilities('emery', 3, 10, ['COLOR', 'HEALTH'], 1);
|
||||||
|
testCapabilities('emery', 3, 10, ['MICROPHONE'], 1);
|
||||||
|
testCapabilities('chalk', 3, 10, ['PLATFORM_APLITE', 'PLATFORM_BASALT'], 0);
|
||||||
|
testCapabilities('basalt', 3, 10, ['SMARTSTRAP', 'SMARTSTRAP_POWER'], 1);
|
||||||
|
testCapabilities('aplite', 3, 10, ['COLOR', 'PLATFORM_APLITE'], 0, 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['PLATFORM_APLITE', 'COLOR'], 0, 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['BW', 'NOT_COLOR'], 1, 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['NOT_BASALT', 'RECT'], 1, 0);
|
||||||
|
testCapabilities('aplite', 3, 10, ['NOT_CHALK', 'BW', 'PLATFORM_APLITE'], 1, 0);
|
||||||
|
});
|
||||||
|
|
||||||
describe('.meta', function() {
|
describe('.meta', function() {
|
||||||
it('populates meta', function() {
|
it('populates meta', function() {
|
||||||
var clayConfig = fixtures.clayConfig(['input']);
|
var clayConfig = fixtures.clayConfig(['input']);
|
||||||
@@ -36,10 +221,10 @@ describe('ClayConfig', function() {
|
|||||||
|
|
||||||
describe('throws when trying to run methods before being built', function() {
|
describe('throws when trying to run methods before being built', function() {
|
||||||
[
|
[
|
||||||
'getItemByAppKey',
|
'getItemByMessageKey',
|
||||||
'getItemById',
|
'getItemById',
|
||||||
'getItemsByType',
|
'getItemsByType',
|
||||||
'getSettings'
|
'serialize'
|
||||||
].forEach(function(method) {
|
].forEach(function(method) {
|
||||||
it('.' + method + '()', function() {
|
it('.' + method + '()', function() {
|
||||||
var clayConfig = fixtures.clayConfig(['input', 'text'], false);
|
var clayConfig = fixtures.clayConfig(['input', 'text'], false);
|
||||||
@@ -64,14 +249,17 @@ describe('ClayConfig', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getItemByAppKey()', function() {
|
describe('.getItemByMessageKey()', function() {
|
||||||
it('it returns the correct item', function() {
|
it('it returns the correct item', function() {
|
||||||
var config = fixtures.config([
|
var config = fixtures.config([
|
||||||
{type: 'input', appKey: 'test-app-key', clayId: 0},
|
{type: 'input', messageKey: 'test-app-key', clayId: 0},
|
||||||
{type: 'input', appKey: undefined, clayId: 1}
|
{type: 'input', messageKey: undefined, clayId: 1}
|
||||||
]);
|
]);
|
||||||
var clayConfig = fixtures.clayConfig(config);
|
var clayConfig = fixtures.clayConfig(config);
|
||||||
assert.deepEqual(clayConfig.getItemByAppKey('test-app-key').config, config[0]);
|
assert.deepEqual(
|
||||||
|
clayConfig.getItemByMessageKey('test-app-key').config,
|
||||||
|
config[0]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -99,20 +287,21 @@ describe('ClayConfig', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getSettings()', function() {
|
describe('.serialize()', function() {
|
||||||
it('returns the correct settings', function() {
|
it('returns the correct settings', function() {
|
||||||
var config = [
|
var config = [
|
||||||
{type: 'input', appKey: 'test1', defaultValue: 'default val'},
|
{type: 'input', messageKey: 'test1', defaultValue: 'default val'},
|
||||||
{type: 'select', appKey: 'test2', options: [
|
{type: 'select', messageKey: 'test2', options: [
|
||||||
{label: 'label-1', value: 'val-1'},
|
{label: 'label-1', value: 'val-1'},
|
||||||
{label: 'label-2', value: 'val-2'}
|
{label: 'label-2', value: 'val-2'}
|
||||||
]},
|
]},
|
||||||
{type: 'toggle', appKey: 'test3'},
|
{type: 'toggle', messageKey: 'test3'},
|
||||||
{type: 'checkboxgroup', appKey: 'test4', options: [
|
{type: 'checkboxgroup', messageKey: 'test4', options: [
|
||||||
{label: 'label-1', value: 'cb-1'},
|
'label-1',
|
||||||
{label: 'label-2', value: 'cb-2'},
|
'label-2',
|
||||||
{label: 'label-2', value: 'cb-3'}
|
'label-3'
|
||||||
]}
|
]},
|
||||||
|
{type: 'slider', messageKey: 'test5', step: 0.05, defaultValue: 12.5}
|
||||||
];
|
];
|
||||||
var settings = {
|
var settings = {
|
||||||
test2: 'val-2'
|
test2: 'val-2'
|
||||||
@@ -120,29 +309,62 @@ describe('ClayConfig', function() {
|
|||||||
|
|
||||||
var clayConfig = fixtures.clayConfig(config, true, true, settings);
|
var clayConfig = fixtures.clayConfig(config, true, true, settings);
|
||||||
|
|
||||||
assert.deepEqual(clayConfig.getSettings(), {
|
assert.deepEqual(clayConfig.serialize(), {
|
||||||
test1: 'default val',
|
test1: {value: 'default val'},
|
||||||
test2: 'val-2',
|
test2: {value: 'val-2'},
|
||||||
test3: false,
|
test3: {value: false},
|
||||||
test4: []
|
test4: {value: [false, false, false]},
|
||||||
|
test5: {value: 12.5, precision: 2}
|
||||||
});
|
});
|
||||||
|
|
||||||
clayConfig.getItemByAppKey('test1').set('val-1');
|
clayConfig.getItemByMessageKey('test1').set('val-1');
|
||||||
clayConfig.getItemByAppKey('test3').set(true);
|
clayConfig.getItemByMessageKey('test3').set(true);
|
||||||
clayConfig.getItemByAppKey('test4').set(['cb-1', 'cb-3']);
|
clayConfig.getItemByMessageKey('test4').set([true, false, true]);
|
||||||
|
|
||||||
assert.deepEqual(clayConfig.getSettings(), {
|
assert.deepEqual(clayConfig.serialize(), {
|
||||||
|
test1: {value: 'val-1'},
|
||||||
|
test2: {value: 'val-2'},
|
||||||
|
test3: {value: true},
|
||||||
|
test4: {value: [true, false, true]},
|
||||||
|
test5: {value: 12.5, precision: 2}
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure the result of serialize() can actually be fed back in to
|
||||||
|
// a new instance of ClayConfig
|
||||||
|
var clay = fixtures.clay(config);
|
||||||
|
var response = encodeURIComponent(JSON.stringify(clayConfig.serialize()));
|
||||||
|
clay.getSettings(response);
|
||||||
|
var loadedSettings = JSON.parse(localStorage.getItem('clay-settings'));
|
||||||
|
|
||||||
|
assert.deepEqual(loadedSettings, {
|
||||||
test1: 'val-1',
|
test1: 'val-1',
|
||||||
test2: 'val-2',
|
test2: 'val-2',
|
||||||
test3: true,
|
test3: true,
|
||||||
test4: ['cb-1', 'cb-3']
|
test4: [true, false, true],
|
||||||
|
test5: 12.5
|
||||||
});
|
});
|
||||||
|
|
||||||
// make sure the result of getSettings() can actually be fed back in to
|
|
||||||
// a new instance of ClayConfig
|
|
||||||
assert.doesNotThrow(function() {
|
assert.doesNotThrow(function() {
|
||||||
settings = clayConfig.getSettings();
|
fixtures.clayConfig(config, true, true, loadedSettings);
|
||||||
fixtures.clayConfig(config, true, true, settings);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only returns the settings present in the config', function() {
|
||||||
|
var config = [
|
||||||
|
{type: 'input', messageKey: 'test1', defaultValue: 'default val'},
|
||||||
|
{type: 'select', messageKey: 'test2', options: [
|
||||||
|
{label: 'label-1', value: 'val-1'},
|
||||||
|
{label: 'label-2', value: 'val-2'}
|
||||||
|
]}
|
||||||
|
];
|
||||||
|
var settings = {
|
||||||
|
test2: 'val-2',
|
||||||
|
notInTheConfig: 'Should not exist'
|
||||||
|
};
|
||||||
|
|
||||||
|
var clayConfig = fixtures.clayConfig(config, true, true, settings);
|
||||||
|
assert.deepEqual(clayConfig.serialize(), {
|
||||||
|
test1: {value: 'default val'},
|
||||||
|
test2: {value: 'val-2'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -240,7 +462,69 @@ describe('ClayConfig', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('.destroy().', function() {
|
||||||
|
it('Destroys all the items on the page and in the items array', function() {
|
||||||
|
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
||||||
|
|
||||||
|
clayConfig.build();
|
||||||
|
clayConfig.destroy();
|
||||||
|
|
||||||
|
// this should throw because the config has not been built yet
|
||||||
|
assert.throws(clayConfig.getAllItems);
|
||||||
|
|
||||||
|
// there should be no DOM inside the container
|
||||||
|
assert.strictEqual(clayConfig.$rootContainer[0].children.length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches the BEFORE_DESTROY event at the right time', function(done) {
|
||||||
|
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
||||||
|
|
||||||
|
clayConfig.on(clayConfig.EVENTS.BEFORE_DESTROY, function() {
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, 3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
clayConfig.build();
|
||||||
|
clayConfig.destroy();
|
||||||
|
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches the AFTER_DESTROY event at the right time', function(done) {
|
||||||
|
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
||||||
|
|
||||||
|
clayConfig.on(clayConfig.EVENTS.AFTER_DESTROY, function() {
|
||||||
|
|
||||||
|
// this should throw because the config has not been built yet
|
||||||
|
assert.throws(clayConfig.getAllItems);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
clayConfig.build();
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, 3);
|
||||||
|
clayConfig.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('.build()', function() {
|
describe('.build()', function() {
|
||||||
|
it('Destroys the config if called a consecutive time', function() {
|
||||||
|
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
||||||
|
var destroyHandlerSpy = sinon.spy();
|
||||||
|
|
||||||
|
clayConfig.on(clayConfig.EVENTS.AFTER_DESTROY, destroyHandlerSpy);
|
||||||
|
|
||||||
|
clayConfig.build();
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, 3);
|
||||||
|
|
||||||
|
clayConfig.config = fixtures.config(['select']);
|
||||||
|
clayConfig.build();
|
||||||
|
assert.strictEqual(clayConfig.getAllItems().length, 1);
|
||||||
|
assert(destroyHandlerSpy.calledOnce);
|
||||||
|
|
||||||
|
clayConfig.build();
|
||||||
|
assert(destroyHandlerSpy.calledTwice);
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches the BEFORE_BUILD event at the right time', function(done) {
|
it('dispatches the BEFORE_BUILD event at the right time', function(done) {
|
||||||
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false);
|
||||||
clayConfig.on(clayConfig.EVENTS.BEFORE_BUILD, function() {
|
clayConfig.on(clayConfig.EVENTS.BEFORE_BUILD, function() {
|
||||||
|
|||||||
+12
-10
@@ -12,7 +12,7 @@ describe('ClayItem', function() {
|
|||||||
it('defines read-only properties', function() {
|
it('defines read-only properties', function() {
|
||||||
var properties = [
|
var properties = [
|
||||||
'id',
|
'id',
|
||||||
'appKey',
|
'messageKey',
|
||||||
'config',
|
'config',
|
||||||
'$element',
|
'$element',
|
||||||
'$manipulatorTarget',
|
'$manipulatorTarget',
|
||||||
@@ -53,24 +53,25 @@ describe('ClayItem', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.appKey', function() {
|
describe('.messageKey', function() {
|
||||||
it('sets appKey correctly', function() {
|
it('sets messageKey correctly', function() {
|
||||||
var config = fixture.configItem('input');
|
var config = fixture.configItem('input');
|
||||||
var clayItem = new ClayItem(config);
|
var clayItem = new ClayItem(config);
|
||||||
assert.strictEqual(clayItem.appKey, config.appKey);
|
assert.strictEqual(clayItem.messageKey, config.messageKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets appKey to null if there is no appKey in the config', function() {
|
it('sets messageKey to null if there is no messageKey in the config',
|
||||||
var clayItem = fixture.clayItem({type: 'input', appKey: undefined});
|
function() {
|
||||||
assert.strictEqual(clayItem.appKey, null);
|
var clayItem = fixture.clayItem({type: 'input', messageKey: undefined});
|
||||||
|
assert.strictEqual(clayItem.messageKey, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.config', function() {
|
describe('.config', function() {
|
||||||
it('sets appKey correctly', function() {
|
it('sets messageKey correctly', function() {
|
||||||
var config = fixture.configItem('input');
|
var config = fixture.configItem('input');
|
||||||
var clayItem = new ClayItem(config);
|
var clayItem = new ClayItem(config);
|
||||||
assert.strictEqual(clayItem.appKey, config.appKey);
|
assert.strictEqual(clayItem.messageKey, config.messageKey);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,7 +105,8 @@ describe('ClayItem', function() {
|
|||||||
|
|
||||||
it('returns itself for chaining', function() {
|
it('returns itself for chaining', function() {
|
||||||
var clayItem = fixture.clayItem('select');
|
var clayItem = fixture.clayItem('select');
|
||||||
assert.strictEqual(clayItem.initialize(), clayItem);
|
var clayConfig = fixture.clayConfig([]);
|
||||||
|
assert.strictEqual(clayItem.initialize(clayConfig), clayItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if there is no initialize function', function() {
|
it('does nothing if there is no initialize function', function() {
|
||||||
|
|||||||
@@ -143,43 +143,67 @@ describe('manipulators', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('html', function() {
|
describe('html', function() {
|
||||||
testSetGet('text', 'test123');
|
var type = 'text';
|
||||||
testSetGet('button', '<span>some HTML</span>');
|
testSetGet(type, 'test123');
|
||||||
testShow('text');
|
testSetGet(type, '<span>some HTML</span>');
|
||||||
testHide('text');
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('button', function() {
|
describe('button', function() {
|
||||||
testSetGet('button', 'test123');
|
var type = 'button';
|
||||||
testSetGet('button', '<span>some HTML</span>');
|
testSetGet(type, 'test123');
|
||||||
testDisable('button');
|
testSetGet(type, '<span>some HTML</span>');
|
||||||
testEnable('button');
|
testDisable(type);
|
||||||
testShow('button');
|
testEnable(type);
|
||||||
testHide('button');
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('val', function() {
|
describe('val', function() {
|
||||||
testSetGet('input', 'test321');
|
var type = 'input';
|
||||||
testSetGet('input', 1234, '1234');
|
testSetGet(type, 'test321');
|
||||||
testDisable('input');
|
testSetGet(type, 1234, '1234');
|
||||||
testEnable('input');
|
testDisable(type);
|
||||||
testShow('text');
|
testEnable(type);
|
||||||
testHide('text');
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('slider', function() {
|
||||||
|
var type = {
|
||||||
|
type: 'slider',
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 0.1
|
||||||
|
};
|
||||||
|
|
||||||
|
testSetGet(type, '12', 12);
|
||||||
|
testSetGet(type, 12);
|
||||||
|
testSetGet(type, '12.3', 12.3);
|
||||||
|
testSetGet(type, 12.3);
|
||||||
|
testSetGet(type, 12.34, 12.3);
|
||||||
|
testSetGet(type, '12.34', 12.3);
|
||||||
|
testDisable(type);
|
||||||
|
testEnable(type);
|
||||||
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('checked', function() {
|
describe('checked', function() {
|
||||||
testSetGet({type: 'toggle', defaultValue: false}, 1, true);
|
var type = 'toggle';
|
||||||
testSetGet({type: 'toggle', defaultValue: false}, true);
|
testSetGet({type: type, defaultValue: false}, 1, true);
|
||||||
testSetGet({type: 'toggle', defaultValue: true}, 0, false);
|
testSetGet({type: type, defaultValue: false}, true);
|
||||||
testSetGet({type: 'toggle', defaultValue: true}, false);
|
testSetGet({type: type, defaultValue: true}, 0, false);
|
||||||
testDisable('toggle');
|
testSetGet({type: type, defaultValue: true}, false);
|
||||||
testEnable('toggle');
|
testDisable(type);
|
||||||
testShow('toggle');
|
testEnable(type);
|
||||||
testHide('toggle');
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('radiogroup', function() {
|
describe('radiogroup', function() {
|
||||||
var item = {
|
var type = {
|
||||||
type: 'radiogroup',
|
type: 'radiogroup',
|
||||||
clayId: 1,
|
clayId: 1,
|
||||||
options: [
|
options: [
|
||||||
@@ -188,48 +212,52 @@ describe('manipulators', function() {
|
|||||||
{ label: '3', value: 'three "quote' }
|
{ label: '3', value: 'three "quote' }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
testSetGet(item, 'one');
|
testSetGet(type, 'one');
|
||||||
testSetGet(item, 'two');
|
testSetGet(type, 'two');
|
||||||
testSetGet(item, 'three "quote');
|
testSetGet(type, 'three "quote');
|
||||||
testDisable(item);
|
testDisable(type);
|
||||||
testEnable(item);
|
testEnable(type);
|
||||||
testShow(item);
|
testShow(type);
|
||||||
testHide(item);
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('checkboxgroup', function() {
|
describe('checkboxgroup', function() {
|
||||||
var item = {
|
var type = {
|
||||||
type: 'checkboxgroup',
|
type: 'checkboxgroup',
|
||||||
clayId: 1,
|
clayId: 1,
|
||||||
defaultValue: ['two'],
|
defaultValue: [true, true, true],
|
||||||
options: [
|
options: ['First', 'Second', 'Third']
|
||||||
{ label: '1', value: 'one' },
|
|
||||||
{ label: '2', value: 'two' },
|
|
||||||
{ label: '3', value: 'three "quote' }
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
testSetGet(item, ['one', 'two']);
|
testSetGet(type, [false, false, true]);
|
||||||
testSetGet(item, ['three "quote']);
|
testSetGet(type, [true, false], [true, false, false]);
|
||||||
testSetGet(item, []);
|
testSetGet(type, [1, 0], [true, false, false]);
|
||||||
testSetGet(item, false, []);
|
testSetGet(type, [true], [true, false, false]);
|
||||||
testDisable(item);
|
testSetGet(type, [1], [true, false, false]);
|
||||||
testEnable(item);
|
testSetGet(type, [], [false, false, false]);
|
||||||
testShow(item);
|
|
||||||
testHide(item);
|
// any non-array values should result in all false
|
||||||
|
testSetGet(type, false, [false, false, false]);
|
||||||
|
testSetGet(type, true, [false, false, false]);
|
||||||
|
testSetGet(type, null, [false, false, false]);
|
||||||
|
testDisable(type);
|
||||||
|
testEnable(type);
|
||||||
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('color', function() {
|
describe('color', function() {
|
||||||
testSetGet('color', 'FF0000', 0xff0000);
|
var type = 'color';
|
||||||
testSetGet('color', '#FF0000', 0xff0000);
|
testSetGet(type, 'FF0000', 0xff0000);
|
||||||
testSetGet('color', '0xFF0000', 0xff0000);
|
testSetGet(type, '#FF0000', 0xff0000);
|
||||||
testSetGet('color', '#ff0000', 0xff0000);
|
testSetGet(type, '0xFF0000', 0xff0000);
|
||||||
testSetGet('color', 0xff0000, 0xff0000);
|
testSetGet(type, '#ff0000', 0xff0000);
|
||||||
testSetGet({type: 'color', defaultValue: 0x00ff00}, '', 0x000000);
|
testSetGet(type, 0xff0000, 0xff0000);
|
||||||
testSetGet({type: 'color', defaultValue: 0x00ff00}, false, 0x000000);
|
testSetGet({type: type, defaultValue: 0x00ff00}, '', 0x000000);
|
||||||
testSetGet({type: 'color', defaultValue: 0x00ff00}, undefined, 0x000000);
|
testSetGet({type: type, defaultValue: 0x00ff00}, false, 0x000000);
|
||||||
testDisable('color');
|
testSetGet({type: type, defaultValue: 0x00ff00}, undefined, 0x000000);
|
||||||
testEnable('color');
|
testDisable(type);
|
||||||
testShow('color');
|
testEnable(type);
|
||||||
testHide('color');
|
testShow(type);
|
||||||
|
testHide(type);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,60 +26,5 @@ describe('Utils', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.prepareForAppMessage', function() {
|
|
||||||
it('converts an array correctly when array contains strings', function() {
|
|
||||||
assert.deepEqual(
|
|
||||||
utils.prepareForAppMessage(['one', 'two']),
|
|
||||||
['one', 0, 'two', 0]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('converts an array correctly when array contains numbers', function() {
|
|
||||||
assert.deepEqual(utils.prepareForAppMessage([1, 2, 3]), [1, 2, 3]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('converts an array correctly when array contains booleans', function() {
|
|
||||||
assert.deepEqual(utils.prepareForAppMessage([true, false, true]), [1, 0, 1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('converts booleans to ints', function() {
|
|
||||||
assert.strictEqual(utils.prepareForAppMessage(false), 0);
|
|
||||||
assert.strictEqual(utils.prepareForAppMessage(true), 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('leaves strings alone', function() {
|
|
||||||
assert.strictEqual(utils.prepareForAppMessage('test'), 'test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('leaves numbers alone', function() {
|
|
||||||
assert.strictEqual(utils.prepareForAppMessage(123), 123);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.prepareSettingsForAppMessage', function() {
|
|
||||||
it('converts the settings correctly', function() {
|
|
||||||
var settings = {
|
|
||||||
test1: false,
|
|
||||||
test2: 'val-2',
|
|
||||||
test3: true,
|
|
||||||
test4: ['cb-1', 'cb-3'],
|
|
||||||
test5: 12345,
|
|
||||||
test6: [1, 2, 3, 4],
|
|
||||||
test7: [true, false, true]
|
|
||||||
};
|
|
||||||
var expected = {
|
|
||||||
test1: 0,
|
|
||||||
test2: 'val-2',
|
|
||||||
test3: 1,
|
|
||||||
test4: ['cb-1', 0, 'cb-3', 0],
|
|
||||||
test5: 12345,
|
|
||||||
test6: [1, 2, 3, 4],
|
|
||||||
test7: [1, 0, 1]
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.deepEqual(utils.prepareSettingsForAppMessage(settings), expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#
|
||||||
|
# This file is the default set of rules to compile a Pebble project.
|
||||||
|
#
|
||||||
|
# Feel free to customize this to your needs.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import waflib
|
||||||
|
|
||||||
|
top = '.'
|
||||||
|
out = 'build'
|
||||||
|
|
||||||
|
|
||||||
|
def distclean(ctx):
|
||||||
|
if os.path.exists('dist.zip'):
|
||||||
|
os.remove('dist.zip')
|
||||||
|
if os.path.exists('dist'):
|
||||||
|
shutil.rmtree('dist')
|
||||||
|
waflib.Scripting.distclean(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def options(ctx):
|
||||||
|
ctx.load('pebble_sdk_lib')
|
||||||
|
|
||||||
|
|
||||||
|
def configure(ctx):
|
||||||
|
ctx.load('pebble_sdk_lib')
|
||||||
|
|
||||||
|
|
||||||
|
def build(ctx):
|
||||||
|
ctx.load('pebble_sdk_lib')
|
||||||
|
|
||||||
|
cached_env = ctx.env
|
||||||
|
for platform in ctx.env.TARGET_PLATFORMS:
|
||||||
|
ctx.env = ctx.all_envs[platform]
|
||||||
|
ctx.set_group(ctx.env.PLATFORM_NAME)
|
||||||
|
lib_name = '{}/{}'.format(ctx.env.BUILD_DIR, ctx.env.PROJECT_INFO['name'])
|
||||||
|
ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=lib_name, bin_type='lib')
|
||||||
|
ctx.env = cached_env
|
||||||
|
|
||||||
|
ctx.set_group('bundle')
|
||||||
|
ctx.pbl_bundle(includes=ctx.path.ant_glob('include/**/*.h'),
|
||||||
|
js=ctx.path.ant_glob(['src/js/**/*.js', 'src/js/**/*.json']),
|
||||||
|
bin_type='lib')
|
||||||
|
|
||||||
|
if ctx.cmd == 'clean':
|
||||||
|
for n in ctx.path.ant_glob(['dist/**/*', 'dist.zip'], quiet=True):
|
||||||
|
n.delete()
|
||||||
Reference in New Issue
Block a user