diff --git a/README.md b/README.md index d8029b2..26d5f93 100755 --- a/README.md +++ b/README.md @@ -629,13 +629,15 @@ The "capabilities" property is an array of features that the connected watch mus in order for the item or section to be included in the page. Note: all capabilities in the array must be present for item/section to be included. +You can also prefix the capability with `NOT_` to negate the capability. Eg: `NOT_HEALTH` +will only be included in the page if the device does **NOT** support health. + **Warning:** Items that do not satisfy the capabilities will not be included in the page at all. You will not be able to use methods like `clayConfig.getItemByAppKey()` to -obttain a reference to them. However, this does mean that you will be able to have +obtain a reference to them. However, this does mean that you will be able to have multiple items with the same `appKey`as long as they do not both satisfy the same conditions. - ##### Examples ```javascript @@ -649,11 +651,11 @@ same conditions. ```javascript { "type": "section", - "capabilities": ["COLOR"], + "capabilities": ["NOT_HEALTH"], "items": [ { "type": "text", - "defaultValue": "Only visible for color watches" + "defaultValue": "Only visible for watches that do not support health" } ] } @@ -663,11 +665,11 @@ Below is the full list of capabilities | Capability | Description | |------------|-------------| -| APLITE | Running on Pebble/Pebble Steel.| -| BASALT | Running on Pebble Time/Pebble Time Steel. | -| CHALK | Running on Pebble Time Round. | -| DIORITE | Running on Pebble 2 | -| EMERY | Running on Time 2. | +| PLATFORM_APLITE | Running on Pebble/Pebble Steel.| +| PLATFORM_BASALT | Running on Pebble Time/Pebble Time Steel. | +| PLATFORM_CHALK | Running on Pebble Time Round. | +| PLATFORM_DIORITE | Running on Pebble 2 | +| PLATFORM_EMERY | Running on Time 2. | | BW | Running on hardware that supports only black and white. | | COLOR | Running on hardware that supports 64 colors. | | MICROPHONE | Running on hardware that includes a microphone. | diff --git a/src/scripts/lib/utils.js b/src/scripts/lib/utils.js index 2e2e54a..58bfc46 100644 --- a/src/scripts/lib/utils.js +++ b/src/scripts/lib/utils.js @@ -19,27 +19,27 @@ module.exports.updateProperties = function(obj, descriptor) { }; module.exports.capabilityMap = { - APLITE: { + PLATFORM_APLITE: { platforms: ['aplite'], minFwMajor: 0, minFwMinor: 0 }, - BASALT: { + PLATFORM_BASALT: { platforms: ['basalt'], minFwMajor: 0, minFwMinor: 0 }, - CHALK: { + PLATFORM_CHALK: { platforms: ['chalk'], minFwMajor: 0, minFwMinor: 0 }, - DIORITE: { + PLATFORM_DIORITE: { platforms: ['diorite'], minFwMajor: 0, minFwMinor: 0 }, - EMERY: { + PLATFORM_EMERY: { platforms: ['emery'], minFwMajor: 0, minFwMinor: 0 @@ -81,8 +81,8 @@ module.exports.capabilityMap = { }, ROUND: { platforms: ['chalk'], - minFwMajor: 3, - minFwMinor: 4 + minFwMajor: 0, + minFwMinor: 0 }, DISPLAY_144x168: { platforms: ['aplite', 'basalt', 'diorite'], @@ -104,25 +104,32 @@ module.exports.capabilityMap = { /** * Checks if all of the provided capabilities are compatible with the watch * @param {Object} activeWatchInfo - * @param {Array} [capabilities] + * @param {Array} [capabilities] * @return {boolean} */ module.exports.includesCapability = function(activeWatchInfo, capabilities) { + var notRegex = /^NOT_/; + var result = []; + if (!capabilities || !capabilities.length) { return true; } for (var i = capabilities.length - 1; i >= 0; i--) { var capability = capabilities[i]; - var mapping = module.exports.capabilityMap[capability]; + 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 ) { - return false; + result.push(!!capability.match(notRegex)); + } else { + result.push(!capability.match(notRegex)); } } - return true; + + return result.indexOf(false) === -1; }; diff --git a/test/spec/.eslintrc b/test/spec/.eslintrc index 7eeefc3..ccad4f8 100644 --- a/test/spec/.eslintrc +++ b/test/spec/.eslintrc @@ -1,5 +1,8 @@ { "env": { "mocha": true + }, + "rules": { + "max-params": [0] } } diff --git a/test/spec/lib/clay-config.js b/test/spec/lib/clay-config.js index eab242f..d628720 100644 --- a/test/spec/lib/clay-config.js +++ b/test/spec/lib/clay-config.js @@ -34,9 +34,11 @@ describe('ClayConfig', function() { * @param {number} fwMinor * @param {Array} capabilities * @param {number} expected + * @param {number} notExpected * @return {void} */ - function testCapabilities(platform, fwMajor, fwMinor, capabilities, expected) { + function testCapabilities(platform, fwMajor, fwMinor, capabilities, expected, + notExpected) { it(platform + ' is' + (expected ? '' : ' not') + ' included on firmware ' + fwMajor + '.' + fwMinor + ' for capabilities ' + JSON.stringify(capabilities), function() { @@ -60,6 +62,15 @@ describe('ClayConfig', function() { 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); }); } @@ -75,11 +86,11 @@ describe('ClayConfig', function() { 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, ['APLITE'], 1); - testCapabilities('aplite', 2, 9, ['BASALT'], 0); - testCapabilities('aplite', 2, 9, ['CHALK'], 0); - testCapabilities('aplite', 2, 9, ['DIORITE'], 0); - testCapabilities('aplite', 2, 9, ['EMERY'], 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); @@ -95,11 +106,11 @@ describe('ClayConfig', function() { 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, ['APLITE'], 1); - testCapabilities('aplite', 3, 10, ['BASALT'], 0); - testCapabilities('aplite', 3, 10, ['CHALK'], 0); - testCapabilities('aplite', 3, 10, ['DIORITE'], 0); - testCapabilities('aplite', 3, 10, ['EMERY'], 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); @@ -117,11 +128,11 @@ describe('ClayConfig', function() { 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, ['APLITE'], 0); - testCapabilities('basalt', 3, 10, ['BASALT'], 1); - testCapabilities('basalt', 3, 10, ['CHALK'], 0); - testCapabilities('basalt', 3, 10, ['DIORITE'], 0); - testCapabilities('basalt', 3, 10, ['EMERY'], 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); @@ -139,11 +150,11 @@ describe('ClayConfig', function() { 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, ['APLITE'], 0); - testCapabilities('chalk', 3, 10, ['BASALT'], 0); - testCapabilities('chalk', 3, 10, ['CHALK'], 1); - testCapabilities('chalk', 3, 10, ['DIORITE'], 0); - testCapabilities('chalk', 3, 10, ['EMERY'], 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); @@ -161,11 +172,11 @@ describe('ClayConfig', function() { 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, ['APLITE'], 0); - testCapabilities('diorite', 3, 10, ['BASALT'], 0); - testCapabilities('diorite', 3, 10, ['CHALK'], 0); - testCapabilities('diorite', 3, 10, ['DIORITE'], 1); - testCapabilities('diorite', 3, 10, ['EMERY'], 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); @@ -183,19 +194,22 @@ describe('ClayConfig', function() { 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, ['APLITE'], 0); - testCapabilities('emery', 3, 10, ['BASALT'], 0); - testCapabilities('emery', 3, 10, ['CHALK'], 0); - testCapabilities('emery', 3, 10, ['DIORITE'], 0); - testCapabilities('emery', 3, 10, ['EMERY'], 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); + 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, ['APLITE', 'BASALT', 'DIORITE'], 0); + testCapabilities('chalk', 3, 10, ['PLATFORM_APLITE', 'PLATFORM_BASALT'], 0); testCapabilities('basalt', 3, 10, ['SMARTSTRAP', 'SMARTSTRAP_POWER'], 1); - testCapabilities('aplite', 3, 10, ['COLOR', 'APLITE'], 0); - testCapabilities('aplite', 3, 10, ['APLITE', 'COLOR'], 0); + 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() {