diff --git a/README.md b/README.md index 6d5fbdd..b9250ab 100755 --- a/README.md +++ b/README.md @@ -11,35 +11,18 @@ Clay will eventually be built into the Pebble SDK. However while it is still in 2. Drop `clay.js` in your project's `src/js` directory. 3. Create a JSON file called `config.json` and place it in your `src/js` directory. 4. in order for JSON files to work you may need to change the line in your `wscript` from `ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'))` to `ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js*'))` -5. Your `app.js` needs to `require` clay and your config file, then be initialized: +5. Your `app.js` needs to `require` clay and your config file, then be initialized. Clay will by default, automatically handle the 'showConfiguration' and 'webviewclosed' events: ```javascript var Clay = require('clay'); var clayConfig = require('config.json'); var clay = new Clay(clayConfig); ``` -6. Now in your `showConfiguration` handler you let clay generate the URL for you. It should look something like: -```javascript -Pebble.addEventListener('showConfiguration', function(e) { - Pebble.openURL(clay.generateUrl()); -}); -``` -7. In order for your Pebble app to receive the configuration, you need to get the settings from Clay. Your `webviewclosed` handler should now look something like: -```javascript -Pebble.addEventListener('webviewclosed', function(e) { - // Send settings to Pebble watchapp - Pebble.sendAppMessage(clay.getSettings(e.response), function(e) { - console.log('Sent config data to Pebble'); - }, function() { - console.log('Failed to send config data!'); - console.log(JSON.stringify(e)); - }); -}); -``` + 8. Next is the fun part. Creating your config page. Edit your `config.json` file using the instructions below # Creating Your Config File -Clay uses javascript objects (or JSON) to generate the config page for you. The structure of the page is totally up to you, but you do need to follow some basic rules. +Clay uses JavaScript objects (or JSON) to generate the config page for you. The structure of the page is totally up to you, but you do need to follow some basic rules. ## Basic JSON structure @@ -48,8 +31,8 @@ Your root element should be an array. This represents the entire page. Inside th #### Example: ```javascript [ - { type: 'heading', defaultValue: 'Example Config Page' }, - { type: 'text', defaultValue: 'Clay makes things easy.' } + { "type": "heading", "defaultValue": "Example Config Page" }, + { "type": "text", "defaultValue": "Clay makes things easy." } //... etc etc ] ``` @@ -446,6 +429,48 @@ Each component has a **manipulator.** This is a set of methods used to talk to t Clay is built to allow developers to add their own basic interactivity to the config page. This is done in a number of ways: +## Handling The 'showConfiguration' and 'webviewclosed' Events Manually + +Clay will by default, automatically handle the 'showConfiguration' and 'webviewclosed' events. If you wish to override this behavior and handle the events yourself, pass an object as the 3rd parameter of the Clay constructor with `autoHandleEvents` set to `false` + +Example: + +```javascript +var Clay = require('./clay'); +var clayConfig = require('./config'); +var clay = new Clay(clayConfig, null, {autoHandleEvents: false}); + +Pebble.addEventListener('showConfiguration', function(e) { + Pebble.openURL(clay.generateUrl()); +}); + +Pebble.addEventListener('webviewclosed', function(e) { + + if (e && !e.response) { return; } + + // Send settings to Pebble watchapp + Pebble.sendAppMessage(clay.getSettings(e.response), function(e) { + console.log('Sent config data to Pebble'); + }, function() { + console.log('Failed to send config data!'); + console.log(JSON.stringify(e)); + }); +}); +``` + +### `Clay([Array] config, [function] customFn, [object] options)` + +#### Methods + +| Method | Returns +| --- +| `Clay(Array] config, [function] customFn=null, [object] options={autoHandleEvents: true})` `config` - an Array representing your config `customFn` - function to be run in the context of the generated page `options.autoHandleEvents` - set to `false` to prevent Clay from automatically handling the "showConfiguration" and "webviewclosed" events | `Clay` - a new instance of Clay +| `.registerComponent( [ClayComponent] component )` Registers a custom component. | `void`. +| `.generateUrl()` | `string` - The URL to open with `Pebble.openURL()` +| `.getSettings(response)` `response` - the response object provided to the "webviewclosed" event | `Object` - hash where the key is the `appKey` and the value is the result from the config page. +--- + + ## Custom Function When initializing Clay in your `app.js`, you can optionally provide a function that will be copied and run on the generated config page. **IMPORTANT:** This function is injected by running `.toString()` on it. If you are making use of `require` or any other dynamic features, they will not work. You must make sure that everything the function needs to execute is available in the function body itself. @@ -492,7 +517,7 @@ module.exports = function(minified) { ### `ClayConfig([Object] settings, [Array] config, [$Minified] $rootContainer)` -This is the main way of talking to your generated config page. A reference to the instance of `ClayConfig` is passed to +This is the main way of talking to your generated config page. #### Properties diff --git a/dev/config.js b/dev/config.js index a5865ec..76aa57a 100644 --- a/dev/config.js +++ b/dev/config.js @@ -47,7 +47,7 @@ module.exports = [ }, { "type": "color", - "appKey": "background", + "appKey": "sunnyColorTest", "defaultValue": "00FF00", "label": "Sunny Color", "sunlight": true diff --git a/dist/clay.js b/dist/clay.js index 68a4b07..7cd0232 100644 --- a/dist/clay.js +++ b/dist/clay.js @@ -1,3 +1,3 @@ -/* Clay - https://github.com/pebble/clay - Version: 0.1.0 - Build Date: 2016-02-16T08:42:54.478Z */ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.clay=t()}}(function(){return function t(e,n,o){function r(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(i)return i(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=n[a]={exports:{}};e[a][0].call(u.exports,function(t){var n=e[a][1][t];return r(n?n:t)},u,u.exports,t,e,n,o)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a0)return"{$circularReference:"+u+"}";if(c.push(t),Array.isArray(t))return"["+s(t.map(function(t){return a(t,e,n,l,c.slice())}))+"]";var f=Object.keys(t);return f.length?"{"+s(f.map(function(r){return(o(r)?r:JSON.stringify(r))+":"+a(t[r],e,n,l,c.slice())}))+"}":"{}"}var c=[];return a(t,e,void 0===n?" ":n||"",i||"",c)};var i=/^(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|undefined|var|void|volatile|while|with)$/,a="\\/"===new RegExp("/").source},{}],2:[function(t,e,n){"use strict";e.exports={name:"checkboxgroup",template:t("../../templates/components/checkboxgroup.tpl"),style:t("../../styles/clay/components/checkboxgroup.scss"),manipulator:"checkboxgroup",defaults:{label:"",options:[],attributes:{}}}},{"../../styles/clay/components/checkboxgroup.scss":13,"../../templates/components/checkboxgroup.tpl":19}],3:[function(t,e,n){"use strict";e.exports={name:"color",template:t("../../templates/components/color.tpl"),style:t("../../styles/clay/components/color.scss"),manipulator:"color",defaults:{label:""},initialize:function(t){function e(t){if(t===!1)return"transparent";for("number"==typeof t&&(t=t.toString(16));t.length<6;)t="0"+t;return"#"+(r?i[t]:t)}for(var n=t.HTML,o=this,r=o.config.sunlight!==!1,i={"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"},a=o.config.layout||[[!1,!1,"55ff00","aaff55",!1,"ffff55","ffffaa",!1,!1],[!1,"aaffaa","55ff55","00ff00","aaff00","ffff00","ffaa55","ffaaaa",!1],["55ffaa","00ff55","00aa00","55aa00","aaaa55","aaaa00","ffaa00","ff5500","ff5555"],["aaffff","00ffaa","00aa55","55aa55","005500","555500","aa5500","ff0000","ff0055"],[!1,"55aaaa","00aaaa","005555","ffffff","000000","aa5555","aa0000",!1],["55ffff","00ffff","00aaff","0055aa","aaaaaa","555555","550000","aa0055","ff55aa"],["55aaff","0055ff","0000ff","0000aa","000055","550055","aa00aa","ff00aa","ffaaff"],[!1,"5555aa","5555ff","5500ff","5500aa","aa00ff","ff00ff","ff55ff",!1],[!1,!1,!1,"aaaaff","aa55ff","aa55aa",!1,!1,!1]],c="",s=100/a[0].length,l=100/a.length,u=o.$element,f=0;f'}u.select(".color-box-container").add(n(c));var y=u.select(".value"),v=u.select(".picker-wrap"),x=o.$manipulatorTarget.get("disabled");u.select("label").on("click",function(t){x||v.set("show")}),o.on("change",function(){var t=o.get();y.set("$background-color",e(t)),u.select(".color-box").set("-selected"),u.select('.color-box[data-value="'+t+'"]').set("+selected")}),u.select(".color-box.selectable").on("click",function(t){o.set(parseInt(t.target.dataset.value,10)),v.set("-show")}),v.on("click",function(){v.set("-show")}),o.on("disabled",function(){x=!0}),o.on("enabled",function(){x=!1})}}},{"../../styles/clay/components/color.scss":14,"../../templates/components/color.tpl":20}],4:[function(t,e,n){"use strict";e.exports={name:"footer",template:t("../../templates/components/footer.tpl"),manipulator:"html"}},{"../../templates/components/footer.tpl":21}],5:[function(t,e,n){"use strict";e.exports={name:"heading",template:t("../../templates/components/heading.tpl"),manipulator:"html",defaults:{size:4}}},{"../../templates/components/heading.tpl":22}],6:[function(t,e,n){"use strict";e.exports={color:t("./color"),footer:t("./footer"),heading:t("./heading"),input:t("./input"),select:t("./select"),submit:t("./submit"),text:t("./text"),toggle:t("./toggle"),radiogroup:t("./radiogroup"),checkboxgroup:t("./checkboxgroup")}},{"./checkboxgroup":2,"./color":3,"./footer":4,"./heading":5,"./input":7,"./radiogroup":8,"./select":9,"./submit":10,"./text":11,"./toggle":12}],7:[function(t,e,n){"use strict";e.exports={name:"input",template:t("../../templates/components/input.tpl"),style:t("../../styles/clay/components/input.scss"),manipulator:"val",defaults:{label:"",attributes:{}}}},{"../../styles/clay/components/input.scss":15,"../../templates/components/input.tpl":23}],8:[function(t,e,n){"use strict";e.exports={name:"radiogroup",template:t("../../templates/components/radiogroup.tpl"),style:t("../../styles/clay/components/radiogroup.scss"),manipulator:"radiogroup",defaults:{label:"",options:[],attributes:{}}}},{"../../styles/clay/components/radiogroup.scss":16,"../../templates/components/radiogroup.tpl":24}],9:[function(t,e,n){"use strict";e.exports={name:"select",template:t("../../templates/components/select.tpl"),style:t("../../styles/clay/components/select.scss"),manipulator:"val",defaults:{label:"",options:[],attributes:{}},initialize:function(){var t=this,e=t.$element.select(".value");t.on("change",function(n){var o=t.$manipulatorTarget.select("option:checked").get("innerHTML");e.set("innerHTML",o)})}}},{"../../styles/clay/components/select.scss":17,"../../templates/components/select.tpl":25}],10:[function(t,e,n){"use strict";e.exports={name:"submit",template:t("../../templates/components/submit.tpl"),manipulator:"html",defaults:{attributes:{}}}},{"../../templates/components/submit.tpl":26}],11:[function(t,e,n){"use strict";e.exports={name:"text",template:t("../../templates/components/text.tpl"),manipulator:"html"}},{"../../templates/components/text.tpl":27}],12:[function(t,e,n){"use strict";e.exports={name:"toggle",template:t("../../templates/components/toggle.tpl"),style:t("../../styles/clay/components/toggle.scss"),manipulator:"checked",defaults:{label:"",attributes:{}}}},{"../../styles/clay/components/toggle.scss":18,"../../templates/components/toggle.tpl":28}],13:[function(t,e,n){e.exports=".component-checkbox { -webkit-tap-highlight-color: transparent; display: block; }\n\n.component-checkbox:active { background-color: transparent; }\n\n.component-checkbox > .label { display: block; padding-bottom: 0.35rem; }\n\n.component-checkbox .checkbox-group label { padding: 0.35rem 0; -webkit-tap-highlight-color: rgba(255, 255, 255, 0.1); }\n\n.component-checkbox .checkbox-group label:active { background-color: rgba(255, 255, 255, 0.1); }\n\n.component-checkbox .checkbox-group .label { font-size: 0.9em; padding: 0 0.375rem; }\n\n.component-checkbox .checkbox-group input { opacity: 0; position: absolute; }\n\n.component-checkbox .checkbox-group i { display: block; position: relative; border-radius: 0.25rem; width: 1.4rem; height: 1.4rem; border: 0.11765rem solid #767676; -webkit-flex-shrink: 0; flex-shrink: 0; }\n\n.component-checkbox .checkbox-group input:checked ~ i { border-color: #ff4700; background: #ff4700; }\n\n.component-checkbox .checkbox-group input:checked ~ i:after { content: ''; box-sizing: border-box; -webkit-transform: rotate(45deg); transform: rotate(45deg); position: absolute; left: 0.35rem; top: -0.05rem; display: block; width: 0.5rem; height: 1rem; border: 0 solid #ffffff; border-right-width: 0.11765rem; border-bottom-width: 0.11765rem; }\n"},{}],14:[function(t,e,n){e.exports=".component-color .value { width: 2.2652rem; height: 1.4rem; border-radius: 0.7rem; box-shadow: #2f2f2f 0 0.1rem 0.1rem; }\n\n.component-color .picker-wrap { left: 0; top: 0; right: 0; bottom: 0; position: fixed; padding: 1rem; background: rgba(0, 0, 0, 0.7); opacity: 0; -webkit-transition: opacity 70ms ease-in 175ms; transition: opacity 70ms ease-in 175ms; pointer-events: none; z-index: 100; }\n\n.component-color .picker-wrap .picker { padding: 1rem; background: #f2f2f2; border-radius: 0.25rem; }\n\n.component-color .picker-wrap.show { -webkit-transition-delay: 0ms; transition-delay: 0ms; pointer-events: auto; opacity: 1; }\n\n.component-color .color-box-wrap { box-sizing: border-box; position: relative; height: 0; width: 100%; padding: 0 0 100% 0; margin: 0.6em 0 0; }\n\n.component-color .color-box-wrap .color-box-container { position: absolute; height: 99.97%; width: 100%; left: 0; top: 0; }\n\n.component-color .color-box-wrap .color-box-container .color-box { float: left; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-tl { border-top-left-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-tr { border-top-right-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-bl { border-bottom-left-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-br { border-bottom-right-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.selected { -webkit-transform: scale(1.15); transform: scale(1.15); border-radius: 0.25rem; box-shadow: #111 0 0 0.24rem; position: relative; z-index: 100; }\n"},{}],15:[function(t,e,n){e.exports=".component-input { display: block; }\n\n.component-input .label { padding-bottom: 0.7rem; }\n\n.component-input .input { position: relative; min-width: 100%; margin-top: 0.7rem; margin-left: 0; }\n\n.component-input input { display: block; width: 100%; background: #333333; border-radius: 0.25rem; padding: 0.35rem 0.375rem; border: none; vertical-align: baseline; color: #ffffff; font-size: inherit; }\n\n.component-input input::-webkit-input-placeholder { color: #858585; }\n\n.component-input input::-moz-placeholder { color: #858585; }\n\n.component-input input:-moz-placeholder { color: #858585; }\n\n.component-input input:-ms-input-placeholder { color: #858585; }\n\n.component-input input:focus { border: none; box-shadow: none; }\n\n.component-input input:focus::-webkit-input-placeholder { color: #666666; }\n\n.component-input input:focus::-moz-placeholder { color: #666666; }\n\n.component-input input:focus:-moz-placeholder { color: #666666; }\n\n.component-input input:focus:-ms-input-placeholder { color: #666666; }\n"},{}],16:[function(t,e,n){e.exports=".component-radio { -webkit-tap-highlight-color: transparent; display: block; }\n\n.component-radio:active { background-color: transparent; }\n\n.component-radio > .label { display: block; padding-bottom: 0.35rem; }\n\n.component-radio .radio-group label { padding: 0.35rem 0; -webkit-tap-highlight-color: rgba(255, 255, 255, 0.1); }\n\n.component-radio .radio-group label:active { background-color: rgba(255, 255, 255, 0.1); }\n\n.component-radio .radio-group .label { font-size: 0.9em; padding: 0 0.375rem; }\n\n.component-radio .radio-group input { opacity: 0; position: absolute; }\n\n.component-radio .radio-group i { display: block; position: relative; border-radius: 1.4rem; width: 1.4rem; height: 1.4rem; border: 2px solid #767676; -webkit-flex-shrink: 0; flex-shrink: 0; }\n\n.component-radio .radio-group input:checked ~ i { border-color: #ff4700; }\n\n.component-radio .radio-group input:checked ~ i:after { content: ''; display: block; position: absolute; left: 15%; right: 15%; top: 15%; bottom: 15%; border-radius: 1.4rem; background: #ff4700; }\n"},{}],17:[function(t,e,n){e.exports='.component-select { position: relative; }\n\n.component-select .value { position: relative; padding-right: 1.1rem; }\n\n.component-select .value:after { content: ""; position: absolute; right: 0; top: 50%; margin-top: -0.1rem; height: 0; width: 0; border-left: 0.425rem solid transparent; border-right: 0.425rem solid transparent; border-top: 0.425rem solid #ececec; }\n\n.component-select select { opacity: 0; position: absolute; display: block; left: 0; right: 0; top: 0; bottom: 0; background: #000; width: 100%; border: none; margin: 0; padding: 0; }\n'},{}],18:[function(t,e,n){e.exports=".component-toggle input { display: none; }\n\n.component-toggle .graphic { display: inline-block; position: relative; }\n\n.component-toggle .graphic .slide { display: block; border-radius: 1.05rem; height: 1.05rem; width: 2.2652rem; background: #2f2f2f; -webkit-transition: background-color 150ms linear; transition: background-color 150ms linear; }\n\n.component-toggle .graphic .marker { background: #ececec; width: 1.4rem; height: 1.4rem; border-radius: 1.4rem; position: absolute; left: 0; display: block; top: -0.175rem; -webkit-transition: -webkit-transform 150ms linear; transition: -webkit-transform 150ms linear; transition: transform 150ms linear; transition: transform 150ms linear, -webkit-transform 150ms linear; box-shadow: #2f2f2f 0 0.1rem 0.1rem; }\n\n.component-toggle input:checked + .graphic .slide { background: #993d19; }\n\n.component-toggle input:checked + .graphic .marker { background: #ff4700; -webkit-transform: translateX(0.8652rem); transform: translateX(0.8652rem); }\n"},{}],19:[function(t,e,n){e.exports='\n {{{label}}}\n \n {{each options}}\n \n {{{this.label}}}\n \n \n \n {{/each}}\n \n\n'},{}],20:[function(t,e,n){e.exports='\n \n \n {{{label}}}\n \n \n \n \n \n \n \n \n \n\n'},{}],21:[function(t,e,n){e.exports='\n'},{}],22:[function(t,e,n){e.exports='\n \n\n'},{}],23:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n \n\n'},{}],24:[function(t,e,n){e.exports='\n {{{label}}}\n \n {{each options}}\n \n {{{this.label}}}\n \n \n \n {{/each}}\n \n\n'},{}],25:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n {{each options}}\n {{this.label}}\n {{/each}}\n \n\n'},{}],26:[function(t,e,n){e.exports='\n \n\n'},{}],27:[function(t,e,n){e.exports='\n \n\n'},{}],28:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n \n \n \n \n \n\n'},{}],29:[function(t,e,n){e.exports=''; -},{}],"pebble-clay":[function(t,e,n){"use strict";function o(t,e){if(e="undefined"!=typeof e?e:"data:text/html;base64,",window.btoa)return e+encodeURIComponent(window.btoa(t));var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t=String(t);var o,r,i,a,c,s,l,u=0,f=[];if(/[^\x00-\xFF]/.test(t))throw Error("InvalidCharacterError");for(;u>2,c=(3&o)<<4|r>>4,s=(15&r)<<2|i>>6,l=63&i,u===t.length+2?(s=64,l=64):u===t.length+1&&(l=64),f.push(n.charAt(a),n.charAt(c),n.charAt(s),n.charAt(l));return e+encodeURIComponent(f.join(""))}function r(t,e){function n(t){Array.isArray(t)?t.forEach(function(t){n(t)}):"section"===t.type?n(t.items):c[t.type]&&o.registerComponent(c[t.type])}var o=this;o.config=t,o.customFn=e||function(){},o.components=[],n(o.config)}var i=t("./tmp/config-page.html"),a=t("tosource"),c=t("./src/scripts/components");r.prototype.registerComponent=function(t){this.components.push(t)},r.prototype.generateUrl=function(){var t,e=!Pebble||"pypkjs"===Pebble.platform,n=e?"$$$RETURN_TO$$$":"pebblejs://close#";try{t=JSON.parse(localStorage.getItem("clay-settings"))||{}}catch(r){console.error(r),t={}}var c=i.replace("$$RETURN_TO$$",n).replace("$$CUSTOM_FN$$",a(this.customFn)).replace("$$CONFIG$$",a(this.config)).replace("$$SETTINGS$$",a(t)).replace("$$COMPONENTS$$",a(this.components));return e?o(c,"http://clay.pebble.com.s3-website-us-west-2.amazonaws.com/#"):o(c)},r.prototype.getSettings=function(t){var e=JSON.parse(decodeURIComponent(t));return e?(localStorage.setItem("clay-settings",JSON.stringify(e)),e):{}},e.exports=r},{"./src/scripts/components":6,"./tmp/config-page.html":29,tosource:1}]},{},["pebble-clay"])("pebble-clay")}); \ No newline at end of file +/* Clay - https://github.com/pebble/clay - Version: 0.1.0 - Build Date: 2016-02-17T01:04:02.060Z */ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.clay=t()}}(function(){return function t(e,n,o){function r(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(i)return i(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=n[a]={exports:{}};e[a][0].call(u.exports,function(t){var n=e[a][1][t];return r(n?n:t)},u,u.exports,t,e,n,o)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a0)return"{$circularReference:"+u+"}";if(c.push(t),Array.isArray(t))return"["+s(t.map(function(t){return a(t,e,n,l,c.slice())}))+"]";var f=Object.keys(t);return f.length?"{"+s(f.map(function(r){return(o(r)?r:JSON.stringify(r))+":"+a(t[r],e,n,l,c.slice())}))+"}":"{}"}var c=[];return a(t,e,void 0===n?" ":n||"",i||"",c)};var i=/^(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|undefined|var|void|volatile|while|with)$/,a="\\/"===new RegExp("/").source},{}],2:[function(t,e,n){"use strict";e.exports={name:"checkboxgroup",template:t("../../templates/components/checkboxgroup.tpl"),style:t("../../styles/clay/components/checkboxgroup.scss"),manipulator:"checkboxgroup",defaults:{label:"",options:[],attributes:{}}}},{"../../styles/clay/components/checkboxgroup.scss":13,"../../templates/components/checkboxgroup.tpl":19}],3:[function(t,e,n){"use strict";e.exports={name:"color",template:t("../../templates/components/color.tpl"),style:t("../../styles/clay/components/color.scss"),manipulator:"color",defaults:{label:""},initialize:function(t){function e(t){if(t===!1)return"transparent";for("number"==typeof t&&(t=t.toString(16));t.length<6;)t="0"+t;return"#"+(r?i[t]:t)}for(var n=t.HTML,o=this,r=o.config.sunlight!==!1,i={"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"},a=o.config.layout||[[!1,!1,"55ff00","aaff55",!1,"ffff55","ffffaa",!1,!1],[!1,"aaffaa","55ff55","00ff00","aaff00","ffff00","ffaa55","ffaaaa",!1],["55ffaa","00ff55","00aa00","55aa00","aaaa55","aaaa00","ffaa00","ff5500","ff5555"],["aaffff","00ffaa","00aa55","55aa55","005500","555500","aa5500","ff0000","ff0055"],[!1,"55aaaa","00aaaa","005555","ffffff","000000","aa5555","aa0000",!1],["55ffff","00ffff","00aaff","0055aa","aaaaaa","555555","550000","aa0055","ff55aa"],["55aaff","0055ff","0000ff","0000aa","000055","550055","aa00aa","ff00aa","ffaaff"],[!1,"5555aa","5555ff","5500ff","5500aa","aa00ff","ff00ff","ff55ff",!1],[!1,!1,!1,"aaaaff","aa55ff","aa55aa",!1,!1,!1]],c="",s=100/a[0].length,l=100/a.length,u=o.$element,f=0;f'}u.select(".color-box-container").add(n(c));var y=u.select(".value"),v=u.select(".picker-wrap"),x=o.$manipulatorTarget.get("disabled");u.select("label").on("click",function(t){x||v.set("show")}),o.on("change",function(){var t=o.get();y.set("$background-color",e(t)),u.select(".color-box").set("-selected"),u.select('.color-box[data-value="'+t+'"]').set("+selected")}),u.select(".color-box.selectable").on("click",function(t){o.set(parseInt(t.target.dataset.value,10)),v.set("-show")}),v.on("click",function(){v.set("-show")}),o.on("disabled",function(){x=!0}),o.on("enabled",function(){x=!1})}}},{"../../styles/clay/components/color.scss":14,"../../templates/components/color.tpl":20}],4:[function(t,e,n){"use strict";e.exports={name:"footer",template:t("../../templates/components/footer.tpl"),manipulator:"html"}},{"../../templates/components/footer.tpl":21}],5:[function(t,e,n){"use strict";e.exports={name:"heading",template:t("../../templates/components/heading.tpl"),manipulator:"html",defaults:{size:4}}},{"../../templates/components/heading.tpl":22}],6:[function(t,e,n){"use strict";e.exports={color:t("./color"),footer:t("./footer"),heading:t("./heading"),input:t("./input"),select:t("./select"),submit:t("./submit"),text:t("./text"),toggle:t("./toggle"),radiogroup:t("./radiogroup"),checkboxgroup:t("./checkboxgroup")}},{"./checkboxgroup":2,"./color":3,"./footer":4,"./heading":5,"./input":7,"./radiogroup":8,"./select":9,"./submit":10,"./text":11,"./toggle":12}],7:[function(t,e,n){"use strict";e.exports={name:"input",template:t("../../templates/components/input.tpl"),style:t("../../styles/clay/components/input.scss"),manipulator:"val",defaults:{label:"",attributes:{}}}},{"../../styles/clay/components/input.scss":15,"../../templates/components/input.tpl":23}],8:[function(t,e,n){"use strict";e.exports={name:"radiogroup",template:t("../../templates/components/radiogroup.tpl"),style:t("../../styles/clay/components/radiogroup.scss"),manipulator:"radiogroup",defaults:{label:"",options:[],attributes:{}}}},{"../../styles/clay/components/radiogroup.scss":16,"../../templates/components/radiogroup.tpl":24}],9:[function(t,e,n){"use strict";e.exports={name:"select",template:t("../../templates/components/select.tpl"),style:t("../../styles/clay/components/select.scss"),manipulator:"val",defaults:{label:"",options:[],attributes:{}},initialize:function(){var t=this,e=t.$element.select(".value");t.on("change",function(n){var o=t.$manipulatorTarget.select("option:checked").get("innerHTML");e.set("innerHTML",o)})}}},{"../../styles/clay/components/select.scss":17,"../../templates/components/select.tpl":25}],10:[function(t,e,n){"use strict";e.exports={name:"submit",template:t("../../templates/components/submit.tpl"),manipulator:"html",defaults:{attributes:{}}}},{"../../templates/components/submit.tpl":26}],11:[function(t,e,n){"use strict";e.exports={name:"text",template:t("../../templates/components/text.tpl"),manipulator:"html"}},{"../../templates/components/text.tpl":27}],12:[function(t,e,n){"use strict";e.exports={name:"toggle",template:t("../../templates/components/toggle.tpl"),style:t("../../styles/clay/components/toggle.scss"),manipulator:"checked",defaults:{label:"",attributes:{}}}},{"../../styles/clay/components/toggle.scss":18,"../../templates/components/toggle.tpl":28}],13:[function(t,e,n){e.exports=".component-checkbox { -webkit-tap-highlight-color: transparent; display: block; }\n\n.component-checkbox:active { background-color: transparent; }\n\n.component-checkbox > .label { display: block; padding-bottom: 0.35rem; }\n\n.component-checkbox .checkbox-group label { padding: 0.35rem 0; -webkit-tap-highlight-color: rgba(255, 255, 255, 0.1); }\n\n.component-checkbox .checkbox-group label:active { background-color: rgba(255, 255, 255, 0.1); }\n\n.component-checkbox .checkbox-group .label { font-size: 0.9em; padding: 0 0.375rem; }\n\n.component-checkbox .checkbox-group input { opacity: 0; position: absolute; }\n\n.component-checkbox .checkbox-group i { display: block; position: relative; border-radius: 0.25rem; width: 1.4rem; height: 1.4rem; border: 0.11765rem solid #767676; -webkit-flex-shrink: 0; flex-shrink: 0; }\n\n.component-checkbox .checkbox-group input:checked ~ i { border-color: #ff4700; background: #ff4700; }\n\n.component-checkbox .checkbox-group input:checked ~ i:after { content: ''; box-sizing: border-box; -webkit-transform: rotate(45deg); transform: rotate(45deg); position: absolute; left: 0.35rem; top: -0.05rem; display: block; width: 0.5rem; height: 1rem; border: 0 solid #ffffff; border-right-width: 0.11765rem; border-bottom-width: 0.11765rem; }\n"},{}],14:[function(t,e,n){e.exports=".component-color .value { width: 2.2652rem; height: 1.4rem; border-radius: 0.7rem; box-shadow: #2f2f2f 0 0.1rem 0.1rem; }\n\n.component-color .picker-wrap { left: 0; top: 0; right: 0; bottom: 0; position: fixed; padding: 1rem; background: rgba(0, 0, 0, 0.7); opacity: 0; -webkit-transition: opacity 70ms ease-in 175ms; transition: opacity 70ms ease-in 175ms; pointer-events: none; z-index: 100; }\n\n.component-color .picker-wrap .picker { padding: 1rem; background: #f2f2f2; border-radius: 0.25rem; }\n\n.component-color .picker-wrap.show { -webkit-transition-delay: 0ms; transition-delay: 0ms; pointer-events: auto; opacity: 1; }\n\n.component-color .color-box-wrap { box-sizing: border-box; position: relative; height: 0; width: 100%; padding: 0 0 100% 0; margin: 0.6em 0 0; }\n\n.component-color .color-box-wrap .color-box-container { position: absolute; height: 99.97%; width: 100%; left: 0; top: 0; }\n\n.component-color .color-box-wrap .color-box-container .color-box { float: left; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-tl { border-top-left-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-tr { border-top-right-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-bl { border-bottom-left-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.rounded-br { border-bottom-right-radius: 0.25rem; }\n\n.component-color .color-box-wrap .color-box-container .color-box.selected { -webkit-transform: scale(1.15); transform: scale(1.15); border-radius: 0.25rem; box-shadow: #111 0 0 0.24rem; position: relative; z-index: 100; }\n"},{}],15:[function(t,e,n){e.exports=".component-input { display: block; }\n\n.component-input .label { padding-bottom: 0.7rem; }\n\n.component-input .input { position: relative; min-width: 100%; margin-top: 0.7rem; margin-left: 0; }\n\n.component-input input { display: block; width: 100%; background: #333333; border-radius: 0.25rem; padding: 0.35rem 0.375rem; border: none; vertical-align: baseline; color: #ffffff; font-size: inherit; }\n\n.component-input input::-webkit-input-placeholder { color: #858585; }\n\n.component-input input::-moz-placeholder { color: #858585; }\n\n.component-input input:-moz-placeholder { color: #858585; }\n\n.component-input input:-ms-input-placeholder { color: #858585; }\n\n.component-input input:focus { border: none; box-shadow: none; }\n\n.component-input input:focus::-webkit-input-placeholder { color: #666666; }\n\n.component-input input:focus::-moz-placeholder { color: #666666; }\n\n.component-input input:focus:-moz-placeholder { color: #666666; }\n\n.component-input input:focus:-ms-input-placeholder { color: #666666; }\n"},{}],16:[function(t,e,n){e.exports=".component-radio { -webkit-tap-highlight-color: transparent; display: block; }\n\n.component-radio:active { background-color: transparent; }\n\n.component-radio > .label { display: block; padding-bottom: 0.35rem; }\n\n.component-radio .radio-group label { padding: 0.35rem 0; -webkit-tap-highlight-color: rgba(255, 255, 255, 0.1); }\n\n.component-radio .radio-group label:active { background-color: rgba(255, 255, 255, 0.1); }\n\n.component-radio .radio-group .label { font-size: 0.9em; padding: 0 0.375rem; }\n\n.component-radio .radio-group input { opacity: 0; position: absolute; }\n\n.component-radio .radio-group i { display: block; position: relative; border-radius: 1.4rem; width: 1.4rem; height: 1.4rem; border: 2px solid #767676; -webkit-flex-shrink: 0; flex-shrink: 0; }\n\n.component-radio .radio-group input:checked ~ i { border-color: #ff4700; }\n\n.component-radio .radio-group input:checked ~ i:after { content: ''; display: block; position: absolute; left: 15%; right: 15%; top: 15%; bottom: 15%; border-radius: 1.4rem; background: #ff4700; }\n"},{}],17:[function(t,e,n){e.exports='.component-select { position: relative; }\n\n.component-select .value { position: relative; padding-right: 1.1rem; }\n\n.component-select .value:after { content: ""; position: absolute; right: 0; top: 50%; margin-top: -0.1rem; height: 0; width: 0; border-left: 0.425rem solid transparent; border-right: 0.425rem solid transparent; border-top: 0.425rem solid #ececec; }\n\n.component-select select { opacity: 0; position: absolute; display: block; left: 0; right: 0; top: 0; bottom: 0; background: #000; width: 100%; border: none; margin: 0; padding: 0; }\n'},{}],18:[function(t,e,n){e.exports=".component-toggle input { display: none; }\n\n.component-toggle .graphic { display: inline-block; position: relative; }\n\n.component-toggle .graphic .slide { display: block; border-radius: 1.05rem; height: 1.05rem; width: 2.2652rem; background: #2f2f2f; -webkit-transition: background-color 150ms linear; transition: background-color 150ms linear; }\n\n.component-toggle .graphic .marker { background: #ececec; width: 1.4rem; height: 1.4rem; border-radius: 1.4rem; position: absolute; left: 0; display: block; top: -0.175rem; -webkit-transition: -webkit-transform 150ms linear; transition: -webkit-transform 150ms linear; transition: transform 150ms linear; transition: transform 150ms linear, -webkit-transform 150ms linear; box-shadow: #2f2f2f 0 0.1rem 0.1rem; }\n\n.component-toggle input:checked + .graphic .slide { background: #993d19; }\n\n.component-toggle input:checked + .graphic .marker { background: #ff4700; -webkit-transform: translateX(0.8652rem); transform: translateX(0.8652rem); }\n"},{}],19:[function(t,e,n){e.exports='\n {{{label}}}\n \n {{each options}}\n \n {{{this.label}}}\n \n \n \n {{/each}}\n \n\n'},{}],20:[function(t,e,n){e.exports='\n \n \n {{{label}}}\n \n \n \n \n \n \n \n \n \n\n'},{}],21:[function(t,e,n){e.exports='\n'},{}],22:[function(t,e,n){e.exports='\n \n\n'},{}],23:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n \n\n'},{}],24:[function(t,e,n){e.exports='\n {{{label}}}\n \n {{each options}}\n \n {{{this.label}}}\n \n \n \n {{/each}}\n \n\n'},{}],25:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n {{each options}}\n {{this.label}}\n {{/each}}\n \n\n'},{}],26:[function(t,e,n){e.exports='\n \n\n'},{}],27:[function(t,e,n){e.exports='\n \n\n'},{}],28:[function(t,e,n){e.exports='\n {{{label}}}\n \n \n \n \n \n \n \n\n'},{}],29:[function(t,e,n){e.exports=''; +},{}],"pebble-clay":[function(t,e,n){"use strict";function o(t,e){if(e="undefined"!=typeof e?e:"data:text/html;base64,",window.btoa)return e+encodeURIComponent(window.btoa(t));var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t=String(t);var o,r,i,a,c,s,l,u=0,f=[];if(/[^\x00-\xFF]/.test(t))throw Error("InvalidCharacterError");for(;u>2,c=(3&o)<<4|r>>4,s=(15&r)<<2|i>>6,l=63&i,u===t.length+2?(s=64,l=64):u===t.length+1&&(l=64),f.push(n.charAt(a),n.charAt(c),n.charAt(s),n.charAt(l));return e+encodeURIComponent(f.join(""))}function r(t,e,n){function o(t){Array.isArray(t)?t.forEach(function(t){o(t)}):"section"===t.type?o(t.items):c[t.type]&&r.registerComponent(c[t.type])}var r=this;if(!Array.isArray(t))throw new Error("config must be an Array");if(e&&"function"!=typeof e)throw new Error('customFn must be an function or "null"');n=n||{},r.config=t,r.customFn=e||function(){},r.components=[],n.autoHandleEvents!==!1&&Pebble&&(Pebble.addEventListener("showConfiguration",function(t){Pebble.openURL(r.generateUrl())}),Pebble.addEventListener("webviewclosed",function(t){(!t||t.response)&&Pebble.sendAppMessage(r.getSettings(t.response),function(t){console.log("Sent config data to Pebble")},function(){console.log("Failed to send config data!"),console.log(JSON.stringify(t))})})),o(r.config)}var i=t("./tmp/config-page.html"),a=t("tosource"),c=t("./src/scripts/components");r.prototype.registerComponent=function(t){this.components.push(t)},r.prototype.generateUrl=function(){var t,e=!Pebble||"pypkjs"===Pebble.platform,n=e?"$$$RETURN_TO$$$":"pebblejs://close#";try{t=JSON.parse(localStorage.getItem("clay-settings"))||{}}catch(r){console.error(r),t={}}var c=i.replace("$$RETURN_TO$$",n).replace("$$CUSTOM_FN$$",a(this.customFn)).replace("$$CONFIG$$",a(this.config)).replace("$$SETTINGS$$",a(t)).replace("$$COMPONENTS$$",a(this.components));return e?o(c,"http://clay.pebble.com.s3-website-us-west-2.amazonaws.com/#"):o(c)},r.prototype.getSettings=function(t){var e=JSON.parse(decodeURIComponent(t));return e?(localStorage.setItem("clay-settings",JSON.stringify(e)),e):{}},e.exports=r},{"./src/scripts/components":6,"./tmp/config-page.html":29,tosource:1}]},{},["pebble-clay"])("pebble-clay")}); \ No newline at end of file diff --git a/index.js b/index.js index 39e474c..bc2fa6d 100755 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ var standardComponents = require('./src/scripts/components'); /** * @param {string} input * @param {string} [prefix] + * @private * @returns {string} */ function encodeDataUri(input, prefix) { @@ -62,17 +63,51 @@ function encodeDataUri(input, prefix) { /** * @param {Array} config - the Clay config - * @param {function} [customFn] - custom code to run from the config page. - * Will run with api as context + * @param {function} [customFn] - Custom code to run from the config page. Will run + * with the ClayConfig instance as context + * @param {Object} [options] - Additional options to pass to Clay + * @param {boolean} [options.autoHandleEvents] - If false, Clay will not + * automatically handle the 'showConfiguration' and 'webviewclosed' events * @constructor */ -function Clay(config, customFn) { +function Clay(config, customFn, options) { var self = this; + if (!Array.isArray(config)) { + throw new Error('config must be an Array'); + } + + if (customFn && typeof customFn !== 'function') { + throw new Error('customFn must be an function or "null"'); + } + + options = options || {}; + self.config = config; self.customFn = customFn || function() {}; self.components = []; + // Let Clay handle all the magic + if (options.autoHandleEvents !== false && Pebble) { + + Pebble.addEventListener('showConfiguration', function(e) { + 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(e) { + console.log('Sent config data to Pebble'); + }, function() { + console.log('Failed to send config data!'); + console.log(JSON.stringify(e)); + }); + }); + } + /** * @private * @param {Clay~ConfigItem|Array} item @@ -93,6 +128,18 @@ function Clay(config, customFn) { _registerStandardComponents(self.config); } +/** + * Register a component to Clay. + * @param {Object} component - the clay component to register + * @param {string} component.name - the name of the component + * @param {string} component.template - HTML template to use for the component + * @param {string|Object} component.manipulator - methods to attach to the component + * @param {function} component.manipulator.set - set manipulator method + * @param {function} component.manipulator.get - get manipulator method + * @param {Object} [component.defaults] - template defaults + * @param {function} [component.initialize] - method to scaffold the component + * @return {boolean} - Returns true if component was registered correctly + */ Clay.prototype.registerComponent = function(component) { this.components.push(component); }; @@ -136,12 +183,12 @@ Clay.prototype.generateUrl = function() { /** * Parse the response from the webviewclosed event data * @param {string} response - * @returns {{}} + * @returns {Object} */ Clay.prototype.getSettings = function(response) { // Decode and parse config data as JSON var settings = JSON.parse(decodeURIComponent(response)); - // @todo get defaults in here + if (!settings) return {}; localStorage.setItem('clay-settings', JSON.stringify(settings)); diff --git a/src/scripts/config-page.js b/src/scripts/config-page.js index 82011da..e0bf3f6 100755 --- a/src/scripts/config-page.js +++ b/src/scripts/config-page.js @@ -12,7 +12,7 @@ var returnTo = window.returnTo || 'pebblejs://close#'; var customFn = window.customFn || function() {}; var clayComponents = window.clayComponents || {}; -var platform = window.navigator.userAgent.match(/Android/) ? 'android' : 'ios'; +var platform = window.navigator.userAgent.match(/android/i) ? 'android' : 'ios'; document.documentElement.classList.add('platform-' + platform); // Register the passed components diff --git a/src/scripts/lib/clay-config.js b/src/scripts/lib/clay-config.js index 2d97dfe..38d2e1f 100644 --- a/src/scripts/lib/clay-config.js +++ b/src/scripts/lib/clay-config.js @@ -2,13 +2,13 @@ /** * A Clay config Item - * @typedef {object} Clay~ConfigItem + * @typedef {Object} Clay~ConfigItem * @property {string} type * @property {string|boolean|number} defaultValue * @property {string} [appKey] * @property {string} [id] * @property {string} [label] - * @property {object} [attributes] + * @property {Object} [attributes] * @property {Array} [options] * @property {Array} [items] */ @@ -23,8 +23,8 @@ var manipulators = require('./manipulators'); /** * @extends ClayEvents - * @param {{}} settings - setting that were set from a previous session - * @param {[]|{}} config + * @param {Object} settings - setting that were set from a previous session + * @param {Array|Object} config * @param {M} $rootContainer * @constructor */ @@ -150,7 +150,7 @@ function ClayConfig(settings, config, $rootContainer) { }; /** - * @returns {object} + * @returns {Object} */ self.getSettings = function() { _checkBuilt('getSettings'); @@ -188,13 +188,13 @@ function ClayConfig(settings, config, $rootContainer) { /** * Register a component to Clay. This must be called prior to .build(); - * @param {{}} component - the clay component to register + * @param {Object} component - the clay component to register * @param {string} component.name - the name of the component * @param {string} component.template - HTML template to use for the component - * @param {string|{}} component.manipulator - methods to attach to the component + * @param {string|Object} component.manipulator - methods to attach to the component * @param {function} component.manipulator.set - set manipulator method * @param {function} component.manipulator.get - get manipulator method - * @param {{}} [component.defaults] - template defaults + * @param {Object} [component.defaults] - template defaults * @param {function} [component.initialize] - method to scaffold the component * @return {boolean} - Returns true if component was registered correctly */ @@ -217,6 +217,10 @@ ClayConfig.registerComponent = function(component) { } } + if (!_component.manipulator) { + throw new Error('The manipulator must be defined'); + } + if (typeof _component.manipulator.set !== 'function' || typeof _component.manipulator.get !== 'function') { throw new Error('The manipulator must have both a `get` and `set` method'); diff --git a/src/scripts/lib/clay-events.js b/src/scripts/lib/clay-events.js index ae3c246..6eacdac 100644 --- a/src/scripts/lib/clay-events.js +++ b/src/scripts/lib/clay-events.js @@ -88,7 +88,7 @@ function ClayEvents($eventTarget) { /** * Trigger an event. * @param {string} name - a single event name to trigger - * @param {object} [eventObj] - an object to pass to the event handler, + * @param {Object} [eventObj] - an object to pass to the event handler, * provided the handler does not have custom arguments. * @returns {ClayEvents} */ diff --git a/src/scripts/lib/clay-item.js b/src/scripts/lib/clay-item.js index 8dd4fee..3d38138 100644 --- a/src/scripts/lib/clay-item.js +++ b/src/scripts/lib/clay-item.js @@ -19,7 +19,7 @@ function ClayItem(config) { var _component = componentRegistry[config.type]; if (!_component) { - throw new Error('the component: ' + config.type + ' is not registered. ' + + throw new Error('The component: ' + config.type + ' is not registered. ' + 'Make sure to register it with ClayConfig.registerComponent()'); } @@ -31,7 +31,7 @@ function ClayItem(config) { /** @type {string|null} */ self.appKey = config.appKey || null; - /** @type {object} */ + /** @type {Object} */ self.config = config; /** @type {M} */ diff --git a/src/scripts/lib/utils.js b/src/scripts/lib/utils.js index 8805bb6..02079f3 100644 --- a/src/scripts/lib/utils.js +++ b/src/scripts/lib/utils.js @@ -2,8 +2,8 @@ /** * Batch update all the properties of an object. - * @param {object} obj - * @param {object} descriptor + * @param {Object} obj + * @param {Object} descriptor * @param {boolean} [descriptor.configurable] * @param {boolean} [descriptor.enumerable] * @param {*} [descriptor.value] diff --git a/test/fixture.js b/test/fixture.js index 157c482..2c23cf5 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -10,7 +10,7 @@ var componentRegistry = require('../src/scripts/lib/component-registry'); var idCounter = 0; /** - * @param {string|{}} config + * @param {string|Object} config * @param {boolean} [autoRegister=true] * @returns {Clay~ConfigItem} */ @@ -37,7 +37,7 @@ module.exports.configItem = function(config, autoRegister) { }; /** - * @param {string|{}} config + * @param {string|Object} config * @param {boolean} [autoRegister=true] * @returns {ClayItem} */ @@ -46,7 +46,7 @@ module.exports.clayItem = function(config, autoRegister) { }; /** - * @param {[]} types + * @param {Array} types * @param {boolean} [autoRegister=true] * @returns {*} */ @@ -59,10 +59,10 @@ module.exports.config = function(types, autoRegister) { }; /** - * @param {[]} types + * @param {Array} types * @param {boolean} [build=true] - run the build method on the result * @param {boolean} [autoRegister=true] - * @param {{}} [settings] - settings to pass to constructor + * @param {Object} [settings] - settings to pass to constructor * @returns {ClayConfig} */ module.exports.clayConfig = function(types, build, autoRegister, settings) { diff --git a/test/spec/lib/clay-config.js b/test/spec/lib/clay-config.js index 443dec2..c6bcf45 100644 --- a/test/spec/lib/clay-config.js +++ b/test/spec/lib/clay-config.js @@ -181,6 +181,23 @@ describe('ClayConfig', function() { clayConfig.build(); }); + it('throws if there is no manipulator', + function(done) { + delete componentRegistry.select; + var clayConfig = fixtures.clayConfig(['select'], false, false); + var _selectComponent = _.copyObj(selectComponent); + _selectComponent.manipulator = undefined; + + clayConfig.on(clayConfig.EVENTS.BEFORE_BUILD, function() { + assert.throws(function() { + clayConfig.registerComponent(_selectComponent); + }, /manipulator must be defined/); + done(); + }); + + clayConfig.build(); + }); + it('only registers the component once', function() { delete componentRegistry.select; var warnStub = sinon.stub(console, 'warn');