diff --git a/.gitignore b/.gitignore index 523dc5f20..231851dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ appdates.csv _config.yml tests/Layout/bin/tmp.* tests/Layout/testresult.bmp -apps.local.json \ No newline at end of file +apps.local.json +_site +.jekyll-cache diff --git a/README.md b/README.md index 9cf30065a..ee555cad2 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,18 @@ try and keep filenames short to avoid overflowing the buffer. }, ``` +### Screenshots + +In the app `metadata.json` file you can add a list of screenshots with a line like: `"screenshots" : [ { "url":"screenshot.png" } ],` + +To get a screenshot you can: + +* Type `g.dump()` in the left-hand side of the Web IDE when connected to a Bangle.js 2 - you can then +right-click and save the image shown in the terminal (this only works on Bangle.js 2 - Bangle.js 1 is +unable to read data back from the LCD controller). +* Run your code in the emulator and use the screenshot button in the bottom right of the window. + + ## Testing ### Online @@ -174,7 +186,7 @@ The widget example is available in [`apps/_example_widget`](apps/_example_widget Widgets are just small bits of code that run whenever an app that supports them calls `Bangle.loadWidgets()`. If they want to display something in the 24px high -widget bar at the top of the screen they can add themselves to the global +widget bar at the top of the screen they can add themselves to the global `WIDGETS` array with: ``` @@ -214,10 +226,8 @@ and which gives information about the app for the Launcher. "name":"Short Name", // for Bangle.js menu "icon":"*myappid", // for Bangle.js menu "src":"-myappid", // source file - "type":"widget/clock/app/bootloader", // optional, default "app" - // if this is 'widget' then it's not displayed in the menu - // if it's 'clock' then it'll be loaded by default at boot time - // if this is 'bootloader' then it's code that is run at boot time, but is not in a menu + "type":"widget/clock/app/bootloader/...", // optional, default "app" + // see 'type' in 'metadata.json format' below for more options/info "version":"1.23", // added by BangleApps loader on upload based on metadata.json "files:"file1,file2,file3", @@ -240,16 +250,23 @@ and which gives information about the app for the Launcher. "version": "0v01", // the version of this app "description": "...", // long description (can contain markdown) "icon": "icon.png", // icon in apps/ - "screenshots" : [ { url:"screenshot.png" } ], // optional screenshot for app + "screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app "type":"...", // optional(if app) - // 'app' - an application + // 'clock' - a clock - required for clocks to automatically start // 'widget' - a widget - // 'launch' - replacement launcher app - // 'bootloader' - code that runs at startup only + // 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js' // 'RAM' - code that runs and doesn't upload anything to storage + // 'launch' - replacement 'Launcher' + // 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle + // 'scheduler' - provides 'sched' library and boot code for scheduling alarms/timers + // (currently only 'sched' app) + // 'notify' - provides 'notify' library for showing notifications + // 'locale' - provides 'locale' library for language-specific date/distance/etc + // (a version of 'locale' is included in the firmware) "tags": "", // comma separated tag list for searching "supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2 - "dependencies" : { "notify":"type" } // optional, app 'types' we depend on + "dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above) "dependencies" : { "messages":"app" } // optional, depend on a specific app ID // for instance this will use notify/notifyfs is they exist, or will pull in 'notify' "readme": "README.md", // if supplied, a link to a markdown-style text file @@ -402,7 +419,7 @@ Example `settings.js` // make sure to enclose the function in parentheses (function(back) { let settings = require('Storage').readJSON('myappid.json',1)||{}; - if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value + if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value function save(key, value) { settings[key] = value; require('Storage').write('myappid.json', settings); diff --git a/_config.yml b/_config.yml index 2f7efbeab..c74188174 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-minimal \ No newline at end of file +theme: jekyll-theme-slate \ No newline at end of file diff --git a/apps/2047pp/2047pp.app.js b/apps/2047pp/2047pp.app.js new file mode 100644 index 000000000..9163aaf3a --- /dev/null +++ b/apps/2047pp/2047pp.app.js @@ -0,0 +1,140 @@ +class TwoK { + constructor() { + this.b = Array(4).fill().map(() => Array(4).fill(0)); + this.score = 0; + this.cmap = {0: "#caa", 2:"#ccc", 4: "#bcc", 8: "#ba6", 16: "#e61", 32: "#d20", 64: "#d00", 128: "#da0", 256: "#ec0", 512: "#dd0"}; + } + drawBRect(x1, y1, x2, y2, th, c, cf, fill) { + g.setColor(c); + for (i=0; i 4) g.setColor(1, 1, 1); + else g.setColor(0, 0, 0); + g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); + if (b>0) g.drawString(b.toString(), xo+(x+0.5)*bw+1, yo+(y+0.5)*bh); + } + } + shift(d) { // +/-1: shift x, +/- 2: shift y + var crc = E.CRC32(this.b.toString()); + if (d==-1) { // shift x left + for (y=0; y<4; ++y) { + for (x=2; x>=0; x--) + if (this.b[y][x]==0) { + for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; + this.b[y][3] = 0; + } + for (x=0; x<3; ++x) + if (this.b[y][x]==this.b[y][x+1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x+1]; + for (j=x+1; j<3; ++j) this.b[y][j] = this.b[y][j+1]; + this.b[y][3] = 0; + } + } + } + else if (d==1) { // shift x right + for (y=0; y<4; ++y) { + for (x=1; x<4; x++) + if (this.b[y][x]==0) { + for (i=x; i>0; i--) this.b[y][i] = this.b[y][i-1]; + this.b[y][0] = 0; + } + for (x=3; x>0; --x) + if (this.b[y][x]==this.b[y][x-1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x-1] ; + for (j=x-1; j>0; j--) this.b[y][j] = this.b[y][j-1]; + this.b[y][0] = 0; + } + } + } + else if (d==-2) { // shift y down + for (x=0; x<4; ++x) { + for (y=1; y<4; y++) + if (this.b[y][x]==0) { + for (i=y; i>0; i--) this.b[i][x] = this.b[i-1][x]; + this.b[0][x] = 0; + } + for (y=3; y>0; y--) + if (this.b[y][x]==this.b[y-1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y-1][x]; + for (j=y-1; j>0; j--) this.b[j][x] = this.b[j-1][x]; + this.b[0][x] = 0; + } + } + } + else if (d==2) { // shift y up + for (x=0; x<4; ++x) { + for (y=2; y>=0; y--) + if (this.b[y][x]==0) { + for (i=y; i<3; i++) this.b[i][x] = this.b[i+1][x]; + this.b[3][x] = 0; + } + for (y=0; y<3; ++y) + if (this.b[y][x]==this.b[y+1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y+1][x]; + for (j=y+1; j<3; ++j) this.b[j][x] = this.b[j+1][x]; + this.b[3][x] = 0; + } + } + } + return (E.CRC32(this.b.toString())!=crc); + } + addDigit() { + var d = Math.random()>0.9 ? 4 : 2; + var id = Math.floor(Math.random()*16); + while (this.b[Math.floor(id/4)][id%4] > 0) id = Math.floor(Math.random()*16); + this.b[Math.floor(id/4)][id%4] = d; + } +} + +function dragHandler(e) { + if (e.b && (Math.abs(e.dx)>7 || Math.abs(e.dy)>7)) { + var res = false; + if (Math.abs(e.dx)>Math.abs(e.dy)) { + if (e.dx>0) res = twok.shift(1); + if (e.dx<0) res = twok.shift(-1); + } + else { + if (e.dy>0) res = twok.shift(-2); + if (e.dy<0) res = twok.shift(2); + } + if (res) twok.addDigit(); + twok.render(); + } +} + +function swipeHandler() { + +} + +function buttonHandler() { + +} + +var twok = new TwoK(); +twok.addDigit(); twok.addDigit(); +twok.render(); +if (process.env.HWVERSION==2) Bangle.on("drag", dragHandler); +if (process.env.HWVERSION==1) { + Bangle.on("swipe", (e) => { res = twok.shift(e); if (res) twok.addDigit(); twok.render(); }); + setWatch(() => { res = twok.shift(2); if (res) twok.addDigit(); twok.render(); }, BTN1, {repeat: true}); + setWatch(() => { res = twok.shift(-2); if (res) twok.addDigit(); twok.render(); }, BTN3, {repeat: true}); +} diff --git a/apps/2047pp/2047pp_screenshot.png b/apps/2047pp/2047pp_screenshot.png new file mode 100644 index 000000000..8c407fb6f Binary files /dev/null and b/apps/2047pp/2047pp_screenshot.png differ diff --git a/apps/2047pp/ChangeLog b/apps/2047pp/ChangeLog new file mode 100644 index 000000000..a1f88e5ec --- /dev/null +++ b/apps/2047pp/ChangeLog @@ -0,0 +1,2 @@ +0.01: New app! +0.02: Better support for watch themes diff --git a/apps/2047pp/README.md b/apps/2047pp/README.md new file mode 100644 index 000000000..cac3323a6 --- /dev/null +++ b/apps/2047pp/README.md @@ -0,0 +1,9 @@ + +# Game of 2047pp (2047++) + +Tile shifting game inspired by the well known 2048 game. Also very similar to another Bangle game, Game1024. + +Attempt to combine equal numbers by swiping left, right, up or down (on Bangle 2) or swiping left/right and using +the top/bottom button (Bangle 1). + +![Screenshot](./2047pp_screenshot.png) diff --git a/apps/2047pp/app-icon.js b/apps/2047pp/app-icon.js new file mode 100644 index 000000000..4086d1879 --- /dev/null +++ b/apps/2047pp/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A31gAeFtoxPF9wujGBYQG1YAWF6ur5gAYGIovOFzIABF6ReaMAwv/F/4v/F7ejv9/0Yvq1Eylksv4vqvIuBF9ZeDF9ZeBqovr1AsB0YvrLwXMF9ReDF9ZeBq1/v4vBqowKF7lWFYIAFF/7vXAAa/qF+jxB0YvsABov/F/4v/F6WsF7YgEF5xgaLwgvPGIQAWDwwvQADwvJGEguKF+AxhFpoA/AH4A/AFI=")) \ No newline at end of file diff --git a/apps/2047pp/app.png b/apps/2047pp/app.png new file mode 100644 index 000000000..d1fb4a5e5 Binary files /dev/null and b/apps/2047pp/app.png differ diff --git a/apps/2047pp/metadata.json b/apps/2047pp/metadata.json new file mode 100644 index 000000000..033354ac6 --- /dev/null +++ b/apps/2047pp/metadata.json @@ -0,0 +1,15 @@ +{ "id": "2047pp", + "name": "2047pp", + "shortName":"2047pp", + "icon": "app.png", + "version":"0.02", + "description": "Bangle version of a tile shifting game", + "supports" : ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "tags": "game", + "storage": [ + {"name":"2047pp.app.js","url":"2047pp.app.js"}, + {"name":"2047pp.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/90sclk/ChangeLog b/apps/90sclk/ChangeLog new file mode 100644 index 000000000..feb008f5f --- /dev/null +++ b/apps/90sclk/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Fullscreen settings. \ No newline at end of file diff --git a/apps/90sclk/README.md b/apps/90sclk/README.md new file mode 100644 index 000000000..c09c6fe23 --- /dev/null +++ b/apps/90sclk/README.md @@ -0,0 +1,13 @@ +# 90s Clock + +A watch face in 90s style: + +![](screenshot_2.png) + +Fullscreen mode can be enabled in the settings: + +![](screenshot.png) + + +## Creator +- [David Peer](https://github.com/peerdavid) diff --git a/apps/90sclk/app-icon.js b/apps/90sclk/app-icon.js new file mode 100644 index 000000000..28f75c4e6 --- /dev/null +++ b/apps/90sclk/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgc8+fAgEgwAMDvPnz99BYdl2weHtu27ft2AGBiEcuEAhAPDg4jGgECIRMN23fthUNgP374vBAB3gAgc/gAXNjlx4EDxwJEpAjG/6IBjkBL4UAjVgBAJuCgPHBQMFEIkkyQjFhwEClgXBEYNBwkQJoibCBwNFBAUCEAVAQZAjC/8euPHDon//hKB//xEYMP//jBYP/+ARDNYM///+EYIgBj1B/8fCIUhEYQRB//FUIM/EZU4EYMkEYP/8VhEYUH/gRBWAUfI4MD+AjBoAsBwEH8EB/EDwE4HwYjCuEHWAOHgExEYKbBCIZNB8fAEYQHByE/EwPABAY+BgRHDBANyJQXHNwIjD8CSBj/+BwMSTwOOBYK2D/4CCNYZQB/iJBQwYjCCIcAgeBSoOAWYQjEVoIRCNAIjKAQKJBgAFC8ZoCWwJbDABMHGQPAAoMQB5EDx/4A4gqBZwIGCWwIABuBWC4EBZwPgv/AcwS/EAAcIU4IRBVQIRKEwIjBv0ARIUDCJIjD//x/ARK/5HC/+BCJkcI45uDgECUgQjCWAM4WwUBWYanEAA8cTARWBEYUC5RAHw1YgEOFQXADQPHIIkAhgICuARBh0A23blhHBagIKBsOGjNswhHDEYUUAoTUBhkxEYMwKwU503bvuwXILmCEYMYsumWYYjB85lDEYovBEYXm7fs25EBI4kYtOWNwIjD4+8NYsw4YjGz9/2hrEoOGjVBwE4NYdzNYSwBuEDEYcxaIUA8+atugGogjBiVgWAI")) diff --git a/apps/90sclk/app.js b/apps/90sclk/app.js new file mode 100644 index 000000000..6babbfec2 --- /dev/null +++ b/apps/90sclk/app.js @@ -0,0 +1,144 @@ +const SETTINGS_FILE = "90sclk.setting.json"; +const locale = require('locale'); +const storage = require('Storage'); + + +/* + * Load settings + */ +let settings = { + fullscreen: false, +}; + +let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; +for (const key in saved_settings) { + settings[key] = saved_settings[key] +} + + +function getImg() { + return require("heatshrink").decompress(atob("2Gwgc8+fPAQnACY+ShAmQj9/8+evICF//evv3799FguCpMgAwdly1ZAQNbtu2II8EyRoR///23bEIICE//7AoewC4tJkmAAoSDPCggANg//QAwCBvv/QAKDIFgRuDQYZeDCY0BkiCRn53EAQlv/4IEMpGSpKDI+ASGhraEABkB/8eQY+fQZ0AgVJkGAQYTZKCIKCRv/4gaDJ/wFDtgcJhMkyCDGmAQFwVIICEcuJxBQY+ev6ACAQNxDxUgycly1bKpWSoBBQj7gCQZF/QZ4ABiUMuaDDggNFkmCICEH/gECQY+fQYtwEBcCgEGKoXYBgsBkmAIKE/8AEChqDH/6DEEZ/HjlwdIIAEhMgIKEB/4FEQYp9BQYncMyAuJCSMP/AGEmyDHAoaxGAE1/TwsHQYZ9B7/9QYcYIFcD/wHFgiDF76DEQVkAuBBGjyDBz6DG4xBtRhCDE//9AoWAIOsAQASDFSowAxhqDE9oFBIG4ABg6DC/4CBjAWOuPHIVMDtu///sCh//AAKFqgOH/iAPIARBrgE/IJ0fH4WOIFcAg5BMgPHH4PHH9iDC8ANLh5AB/xNI4D4WwAONeRkcv//SQkFAYUDJgJcMABFJKBwmMGgP4A4kEboUfBgLgMAA0EjVpQbILB//xXIkBQYU/ZwM4/4rNAAkkyVICBr4BBZLCBGQ8sBYSACj/wICFB02atLFPBRC2CjgUG4sDZwhBRgVJQaL4FAAS2BGogADhcsj7OEIJ8Bg0atKDBsB6NVoIIGuJABF5FxorOFIJ8JQAKDJn/gQZq2BfAYAGpcsZwpBOiCACQZPHQZsHIAKMHAAMUqNFBApBOQAYCCoCYNQYsPH4P+CZMC5csIKUBwyADAQVwIJsBPQccRIP8C5UcQaiAFQaX/8EAn7CCAoIAJ2SDSgkaQAoCBz1gQZ4AE4ASKgsUQaSAHAQRANAAKAD/+ACJckQaVhQAwCB0+cIJ8Hj//46VNoqDILJECQBFNmnSIJ4AQgMsQZ8BgyAFz1584CCQaBBRQaEJQA9JmnTQYPQIMELQZEH/gGEjCAEAQOnQc6ABQY8HQYqAGPoKACQccCpaDNgJ9BvKACPoaDmQASDIIIUCQAtJQAwCC4BBfiSABQZUEjVpQYaAIAQRAfQZc/8EAQB4CCwBBfQASDGgP/+FhOgNpPpKDlgqDJIIPzQAR9KAQpBfkiDK8+atOnQByDMkDQTgKADQY0JmmSQYKAOQZcBkhBUQAaDFiF5QCSDLhKDVsqDIgRuByaAQAQMwFZFJH6QABhaDJgFxQCQCB4gqHwVIIKiAEQYsAv6ARQZUEyVAICcCpaDKv/HQafAFQ0kwSCUPoWUQZPkQaYpGhMkICgYCQZfEjyDYgUJkA6PjgGFgNFiyDHgf/4EDQaHz54nFiVIIB8///wBI2SQY5BCgCDXkGShAtFiFBggIFv//AAKeFCYKDI/wDBhqDN588+fOEgkkyBvHpMkwAHDj4/B8YvCAA0kQYoUBKYMBQaFxEIcJkA2EDwMMmZUB+FABAMH//xFgN/QYwABgqDJgE8QBHzQAQCC9ghDpMgFIsJkiDCyRBCn//KQRBJgECpaDZuAZCwVIE4sBE4JUD4Y7B/8cBwRBKgFBQY0DboPzQBX79++/fgDAMEOoZpEQAQCDkf//AODvxAJAAxZBQwP/jyDRkmCEA1y5bYE58f+J9EQZZBHAAP4gVPQYKAE+aACAQTZCkggHQAgCBrNnwANDWYQAPuJAB+AbBuKDOgUJkAfGgmyLQoCBBwkf+BBQgE4egRyBEoqAEAQWAiVJDw8EQYuWrNkQYkfQaIAFgMHQZecsGShAZHjiAFAQUQQa5qGEQM+QAwCC0mQDBKDEkqDBsqDEeQJBXgEeQZUdkgtEQZ2wBoUHQbMAgaAIAQNJkAXJQAQCBQAKDILZIAQQZPCrBBKQZJ9Dg/8IDMAh6DH92SpAWKQYaACQY0/ILcBQY2cumXjgWKQZMwQb8AnyDF9Mk33gCpUEQYMlQYltQYgaLACEHQYnHpN1QwKDUU4f/IDYAB7aDD2VLt/+NBiDBQAQCBraDDh5BejyDCuPSrgFBChcEuaDKuJBegF/QYOkySGB/7xDABCADQYdgBYUP/BBeuPnjsl4+eQZsAQY8EMQfAGJzvMAAUB31JlqGB//+CZcD+yDDtu27ILD/5BPACGGpKABQZ0f+PHQYnDBYUcQaAAQyVL9/+///dhn/wEJQYNbQYKPETpgACiBAPkGCjyDPg/8G4McQYVwR4nwOJ4POgMkwDpBAASDLn/gbgdZsCPFIJ0EIJ0ChMgwEAQYbsKgP/EJaDLjl/DRgAEiVJAgUPQYQnKh6PLQYJhBTZAnCcALUPhB0DQZt/BZUAg4yJuI/B+PHIJ7UGLgQ1KvwhLIJMDEgPgSRgADhEkHAsHQZccuBBUg5AB44dDQRtJkjsH/wUJj/wERc/O4QADgJABC4iDNgVIkB3HQZRBMHAKDGv4IFahIAEiVAfaZlNQYyBB/APFIJkgyQLJfZJlNQYIFE4//+KkFDpkBkmQSJgAUIIKDDh6CBTA0HSQoAEgUJkCuMIK4ECjl//8cUI4bKgVJH8JBFg6BB/APHn6DKwUIIMscuJAB+PABw/HDRMEyRAjOgQ/BAALuJn4JIhEkwRAkOgI/BO5TUDAA1JkiClAAP//xQL/AJHgVIkBBnSRvABI8SoBA0gEPII8gyRA1QZEBkmQIO0OA40JkGAIOwAGgVJH/oAByVIIH0EyVAIHsIkmCQX0JkhA+QYMgC6scuPHIM1IC63/AAPgBhF/4CZwuI/B+PH/hB6gaAE/+AIPEHQAIyDj/wSRBBugKBBPokfQZAAvv//jgHEQZIAuj//WYyJFAGMH/56HRIoAxn/8BI6DRsBAjv//RhBKIABEcmAwRgQPOgf//BBasOGIMEHj/wBZJBQgccuPBIKGCBxs/fZU/8CDSjATQiQNMgP/jgLKFiCDBuHHEBIAGyQNMh/4JpbzRQYVlUhIAEgmQBpccv/AILkHhlxQYNwEZQACkhRM//wR6yDKAQMIe5kkMRn8BpaPLQZYCBCRcIkoOKWwPgDRUD/yCRQYtYsARK9MvdhV/LhkfaJaDNjkwOhHHpKSKOhxBTQYsYsuWB48P/dJGR8HMQUUQbUxQYlx4APFji2BpAdK/+AAgMKlmy5cs0QICAAIODQbEYBwv//0SoAbIgP/jgEBijmFMQcH/hASQYMwEAvHhgyD4/8uGSDhMP/AEC23btoCBQwUoBQM/8BBSQY1hy1ZsqhCh//EYJBKv53DL4oCCiiSBICaDJjlwoEcv/HCIPCDZED/wEClKDEtiDBQwOBIKkQQZOWgH/GQUEyAbIj/wAYNRL44CCvZBUgaDB4cMEY98uAQBkjMCAA38AYUKtKDJ//4IKaAEAQaDCrNsB4MBkjgJjiRCQBACCv/AG5MBeoSDSjkwgEJkAjIWYcCzSDJ2//oBBJh//eQaDMjKDCtuSIIJlJOIaAJAQMf/PFDhCPBIIP/SQuDQYcxExHChCDDg4ZDhwED0yDB0yDDQwf//coIA8/H4XjIIyDIjCDCAQPapCoJJQaDL//x44WGuI/B+I+Bv5BFQAKDNpg4EZJHmQYWbQYp3B5cs0AVEgZAB8AGCv/4QZ1hQYeSpGAIJaDL9/8AoMUCgkf/6MEQalw6YCBIJc586DCQAYCC//2QYyMB/wcEIIyDKwyDB22SQwIjDYg6AIAQX/AoYUCBAP8MoZBHgKDEmAmHmmHAoPDCgP/QZOeQYOaQYnf/+yQYOwCQMD///8AbEBAKDT0mZQYNZsCDUv/vQYkHj//44dGQZnDEw0kAoiDJvKDBvKDBtKDC2///qDC2WAn///wbGgYIGQZlpkiDDsuHII6DKPQQIDoP//lwDg0f+AjFQZkNmIvF76DR3//QAKDB2f///gDY8fQaUapMmQAKDCy1YQaXxAoceIIJAHQZ8MPoVw4dIQY3HQZfmQYOmQYP/7aDCAoP/4BBI/+AQaOapKADQav8Aofv/53FAAcHBY6DHmAgC4VMF4xoIQYfnQYXav6DBtuy76CBO4yDXyVJwyDEzxBHQZP/QYZGBjhAJg/8BAyDIAQWTF41/QaHb//27dsQIP92SDJII8EQZWkyQFBjKDCt/+Eo6DKAoX//YIBQbkcmgvGj/wExE5QYeeQYOf/+276CB7ct2BBJn/gQY0QQZMkzCDDw0A/5BIQZF/QYICB3gICQbkNkgsEuEHQaF503//qBB//27dt0CDctMkAodgCYP/wBoJQZAABBAcUQaMgQZUxEYcMTxLGDQYvm75BCtu2QaEDNYSDBoMGQYsapMmBAZcCQaVz/+8BAhAJNAQRCA4SAHAQVMuARBmHATxIAEQYvnzd982bQYVoIJaVB/AHDQZOSpIFCPoc/IJaDGAQ3FIJYOBNwSDL6QCBSokB/5BLgaDFzVp0yDC7QYKABCDNCQk/8B6CuAgHQZh0EAByDJycMmPDCIaDBAgX//wgHjyDHzSDZgiDE0mQAoIREIIKDCA==")); +} + +Graphics.prototype.setFontTime = function(scale) { + // Actual height 54 (56 - 3) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAPwAAAAAAAA/gAAAAAAAF/AAAAAAAA/8AAAAAAAD/wAAAAAAAP/AAAAAAAA/wAAAAAAAB/AAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAB+AAAAAAAAf4AAAAAAAH/wAAAAAAB//AAAAAAAf/+AAAAAAH//8AAAAAB///wAAAAAf///gAAAAH///+AAAAB////4AAAAf///+AAAAH////gAAAB////8AAAA/////AAAAP////wAAAD////8AAAA/////AAAAP////gAAAD////4AAAA////+AAAAD////gAAAAP///4AAAAAf//+AAAAAB///gAAAAAD//4AAAAAAP/+AAAAAAAf/gAAAAAAB/wAAAAAAAH8AAAAAAAAPAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAf/wAAAAAAH//gAAAAAD///gAAAAAf///AAAAAB///+AAAAgP///8AAAHg////wAAAfn////gAAD/t////AAAP/j///8AAA//D///4AAH//D///gAAf//H//+AAB///H//4AAH//+P//gAAP//+P/+AAA///8f/4AAD///8f/gAAP///8f+AAAf///8/wAAB////w/AAAD////A4AAAH///4BgAAAP///gAAAAAf//8AAAAAA///AAAAAAA//wAAAAAAA/+AAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAD+AAAAAAAAP4AAAAAAAB/gAAAAAAAP+AAAAAAAA/5//////4D/v//////gP+//////+A/7//////4D/v//////gP+//////+A/7//////4D/v//////gP+//////+Af7//////4A/v//////gA+//////+AA7///hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAD/4AAfwAAAf/gAD/AAAH/+AA/8AAA//4AH/wAAP//gA//AAD//+AH/8AA///4Af/wAP///gD//AD///+AP/8Af///4A//wH////gD//B////+AP/8f//7/4A//3///P/gD/////w/+AP////8D/4A/////AP/gD////4A/+AP///+AD/4Af///gAH/gB///4AAf/AD///AAB/8AH//wAAH/wAP/8AAAf/AAf/gAAB/4AA/4AAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAf/wD/+AAAB//AP/4AAAH/8A//gAAAf/wD/+AAAB//AP/4AAAH/8A//gAAAf/wB/+AAAAAAAAAB/4A//8A////////wH////////Af///////8A////////wD///+////AP///7///8A////P///wB///8f//+AH///h///4AP//+D///AAf//wH//4AA//+AP//AAB//wAf/4AAD/+AAf/AAAD/gAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAH//AAAAAAA//+AAAAAAP//8AAAAAB///4AAAAAP///wAAAAA////gAAAAH////AAAAA////8AAAAD////4AAAAf////j//gB////+//+AP///////4A////////gD///////+Af///////4B////////gH///////+Af///////gB//////AAAH8AAAHAAAAAAA//wAAAAAAD//4AAAAAAP//gAAAAAA//+AAAAAAD//4AAAAAAP//gAAAAAA//+AAAAAAD//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////AB/AAf///+AP/+B////4A//4H////AD//gf///8AP/+B////4A//4H////gD//gf/////+AAAAAAAP///wP//gA////A//+AD///8D//4AP///wP//gAf//+A//+AB///4D//4AD///gAB/gAH//8AAAAAAP//gAAAAAAf/8AAAAAAA//gAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAP/8AAAAAAB//8AAAAAAf//8AAAAAD///4AAAAAf///wAAAAD////wAAAAf////AAAAD////+AAAAP////8AAAB/////wAAAP/////gAAA/////+AAAH/////8AAAf/////wAAB//////gAAP/////+AAA//////4AAD//////gAAP/////+AAA//////4AAD//////gAAP//////AAA//////8AAAAf////wAAAAAAP//gAAAAB///+AAAAAH///4AAAAAf///gAAAAB///+AAAAAH///wAAAAAf///AAAAAA///8AAAAAD///gAAAAAH//+AAAAAAP//wAAAAAAf/+AAAAAAA//wAAAAAAB/+AAAAAAAB/gAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAH///AAAAAAf//8AAAAAB///wAAAAAH///AAAAAAf//8AAAAAB///wAAAAAH///AAAAAAf//8AAAAAA//AAAAAAAAAAAAAAAAwAAP//////A////////8D////////wP////////A////////8D////////wP////////A////////8D////////wP////////A////////8D////////wP////////A////0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAAD/gAH//AAA//gA//+AAP//AH//8AB//+A///4AP//8H///wA///4f///AH///j///+Af///P///4D///9////wP////////A////////8D////////wP///3///+AAAAAAAAAAB/wAAAAAAAP///x4AAAA////P///4D///8////gH///z///+Af///H///4B///8f///gD///g///8AH//8D///wAf//wH//+AA//+AP//wAA//gA//+AAA/4AA//gAAAEAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAH/8AAAAAAB//8AAAAAAP//4AAAAAB///wAAAAAP///gAAAAB////AAAAAP///8AAAAA////4AAAAH////gAAAAf////AAAAD////8AAAAP////wAAAA/////AAAAD////8AAAAH////wAAAAAAAAAAAAAD///+AAAAAP///////AA///////8AD///////wAP///////AA///////8AB///////wAH//////+AAf//////4AA///////gAD//////8AAH//////wAAf/////+AAA//////4AAB//////AAAH/////4AAAP/////AAAAf////4AAAA////+AAAAB////wAAAAB///8AAAAAB///AAAAAAB//wAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AB8AAAAAH8AP4AAAAA/4B/wAAAAH/gO/AAAAAf+A/8AAAAB/4D/wAAAAH/AP/AAAAAP4AfwAAAAAfAA+AAAAAAQAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="), 46, atob("FionHyIiJiIyJScyFA=="), 58+(scale<<8)+(1<<16)); + return this; +}; + + +Graphics.prototype.setFontDate = function(scale) { + // Actual height 28 (27 - 0) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///wf///B///8H///wf//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAfwAAB+AAAAAAAAfwAAB/AAAH8AAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAADg8AAfH+AB//4A///gD//+AP//AA/w/gBPf+AB//4A///wB//8AH//AAfw8AAPDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAD4AAAfwAAD/h4B/+HwH/4/Af//+AP5/4A/n/gB8f8ADg/gAAD8AAADgAAAAAAAAAAAAAAAAAAAAAAAAB8AAAP4CAB/gcAH+D4Af4/wB/n+AH9/wAHv+AAB/wAAf8AAD/gAA/54AH/PwAf5/gB+H+ADw/4AEB/gAAH8AAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAP4AB9/wAP//gB///AP//8A///4D///gH//+Af3/wAfP/AAAf8AAB/gAAP/AAB/8AAP/wAAfOAAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+AAAP4AAA/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBgAH///g////n////f///9/////8AB/fgAH8AAAPAAAAAAAAAAAAAAAAAAAAAAD9/AAf38AB/f///9////z///+H///wP//8AAAAAAAAAAAAAAAAAAAAAAAAFAAAA+AAAH8AAAfwAAB/AAAD8AAAEAAAAAAAAAAAAAAAAAAAAAAAAfAAAB8AAAHwAAAfAAA//wAD//AAP/8AA//wAD//AAAfAAAB8AAAHwAAAfAAAAAAAAAAAAAAAAAAAAAAABkAAAPwAAA/AAAB4AAACAAAAAAAAAAAAAAAAAAAAAD4AAAPgAAA+AAAD4AAAPgAAA+AAAD4AAAPgAAA+AAAD4AAAPgAAA+AAAAAAAAAAAAAAAAAAAAAAACAAAAeAAAD4AAAPgAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAPAAAD+AAA/4AAP/wAD//AB//wAf/8AH//AB//wAf/8AA//AAD/wAAH8AAAeAAAAgAAAAAAAAAAAAAAAAAAAAAAAA8AAAP8AAD/4AEP/wAd//gD9//AP9/8A/9/wD/7/AP/78Af/7wA//CAB/8AAD/AAADwAAAAAAAAAAAAAAAAAAAABwAAAPAAAB8AAAH3//+ff//59///n3//+Pf//4d///gAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAD8B4AfwfgH/B+A/8P4P/w/j//D+//8P//vw//4/D/+B8H/gHwf8AfAfAA8AAAAAAAAAAAAAAfgAPx/AB/H8AH8fwAPwH/f/H///8f///x/+//H/7/8P/H/gf8P8A/APgAAAAAAAAAAAAAAAAAAAAAAAAD8AAA/8AAH/4AA//gAD//AAf/9/h///+H///4f///h//8AAD/gAAP+AAA/4AAD/gAAAAAAAAAAAAAAAAAAAAAAAAf/4PB//g/n/+D+f/4P5/gf/n/B/+f8H/5/wP/AAAf4AAA/AAAAAAAAAAAAAAAAAAAAAAAAAA8AAAP8AAD/8AAf/4AD//wAP//gB//+AH//8A///wD///AP//8A///wAAP/AAD/+AAH/4AAf/AAB/8AAD/gAAH+AAAPgAAAAAAAAAAAAAAAAAAAAeAAD/4AAP/gAA/+AAB/4AAAAAM+f///5////n///+f///5////n///+f5AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AP4H+B/w/8P/n/4////n///+f///4AAAAH/9/+P///4/+f/j/5/+H/D/wH4H+AAAHgAAAAAAAAAAAAAAAAAAAA/AAAP/AAB/+AAP/4AA//wAH//AAf/8AB//wAH///wf///B///8H///wP//+A///4B///gD//8AP//gAP/4AAf+AAAPAAAAAAAAAAAAAAAAAAAAAAGAwAA8HgAH4+AAfD4AA4HAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAB4+AAPn4AA+PgABwcAAAAAAAAAAAAAAAAAAAAABgAAAOAAAB8AAAf4AAD/wAAf/AAD/+AAfz8AA8HwADgfgAEA+AAABgAAAAAAAAAAAAAAAAAAAAHj4AAfPgAB8+AAHz4AAfPgAB8+AAHz4AAfPgAB8+AAHz4AAfPgAB8+AAHj4AAAAAAAAAAAAAAAAAAAAAAAAAAAYAAQDwADgfgAfD8AB/fgAD/8AAH/wAAP+AAAfwAAA+AAABwAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAf8AAD/wAAf/gAB/+AAH/4AgA//3B///cH//9wf//3A//8YD/+AAH/4AAP+AAAPgAAAAAAAAAAAAAAAAAD8AAA/8AAH/4AA//wAH8fgAfu+AAf98AOf7wA///ADn/8AO//wA///AD//YAP/8AA/gMAB//wAD//AAH/4AAP/AAAPwAAAAAAAAAAAAAAAAAAAAP///h///+P///5////n///+f//AB+D8AH///+f///4////h///+B///4AAAAAAAAAAAAAAAAAAAAAAAf///j////P///8////z////P///8////z///AP///+f///5////D/5/8H/H/gPwH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/gAA//gAH//AA//+AH//8A///4D///gf///B///8H///wf///B/gH8H+Afwf4B/BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//8A////D///8P///w////B///8H///gP//+A///wB//+AD//wAD/8AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////n///+f///5////n///+f///5////n/fP+f9+/5/37/n/AP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAf///5////n///+f///5////n///+f///5/z8AH/PwAf8AAAAAAAAAAAAAAAAAAAAAAA/gAAP/wAD//wAf//gD///Af//+B///4P///w////H///8f///x////H///8f4H/x/gf/D+B/8AAHAAAAcAAABwAAAHAAAAAAAAAAAAAAAAAAAAAAABgH///gf//+B///4H///gf//+B///4AB+AAf/9+B///4H///gf//+B///4H///gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8P///w////D///8P///w////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAfAAAB+AAAH4AAAfgAAB+f///5////n///+f///x///8AAAAAAAAAAAAAAAAAAAAAAAAAAANH///8f///x////H///8f///x////H//+AAH/8AA//4AH//wA///gD//+Af//8B///wH///Af//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX5////n///+f///5////v///+////5//7/gAAP+AAA/4AAD/gAAAAAAAAAAAAAAAAAAAAAAAAAAvh///+P///5////n///+f///5/wAAD+AAAP8AAAfwAAD/AAAP4AAB////n///+f///5////j///+H///4AAAAAAAAAAAAAAAAAAAAAAAH///gf//+B///4H///gf//+B///4B//4AB//4B///4H///gf//+B///4H///gf//+B///4AAAAAAAAAAAAAAAD/AAA//gAH//AA//+AH//8Af//wD///gf//+B///8H///wf///B/z/8H//wAf///B///8H///wf///A///8D///gH//8AP//wAf/+AA//gAA/4AAAAAAAAAAAAAAAAAA/x////H///8f///x////H///8f///x//gAH/+AAf/4AA//gAB/8AAD/gAAH8AAAAAAAAAAAAAAAAAAAAAAAcAAAP8AAD/8AA//8AH//4Af//wD///AP//+B///4H///wf///B///8H//+Af///B///8H///wf///A///8D///gH//+Af//4A///gB//+AB//4AB/PgAAAYAAAAAAAAAAAAAAAAAAAP///5////n///+f///5////n///+P///4////D///+P///4////h/9/+D/gAAD4AAAAAAAAAAAAAAAAAAAAAAAAAADwAAA/wAAH/gAA//AfH/8D8f/4f5////n+P/+f4P/5/g//j8D/8DgH/wAAP8AAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAfAAAB8//8H///wf///B///8H///wf//AB8AAAHwAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAB///8H///8f///5////n///+AAAH5////n///+f///x////H///wf/gAAAAAAAAAAAAAAAAQAAAD8AAAP/AAA//wAH//8Af//+B////gf//+AP//4AP//gP//+P///5////n///gP//gA//AAD/gAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///+D///8P///4////j///+PAAfwAAB/AAAH4AAAfw////j//++P//74///vj///8AAB7AAAAAAAAAAAAAAAAAAAAAAAAAAABgeAA+B/AP8P/n/w////j///+H///gH//4D///8P///4////h/4/8H8AfwPAAOAgAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAHwAAAfgAAB/AAAH8H/wf///B///8H///wf///B///8H/8cAfwAAB+AAAHwAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAABwAAAHAAD8cAD/xwD//HH//+f///5////n///+P//w4f/wDh/gAOHgAA4AAADgAAAOAAAAAAAAAAAAAA///+////////////////////////+AAA/wAAD/gAAP+AAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAA/AAAD/AAAf/AAB//gAD//gAD//gAD//gAD//gAD//gAD//AAD/4AAB/AAAB8AAABgAAAAAAAAAAAAAAAAAAOAAAe4AAB7gAAHuAAAe////7////v///+////7////v///YwAAAAAAAA="), 32, atob("HA4NFhEaFwoNDQsRCRALFBMPEBESEBgSExgKCRARERIXEhQVExEPGBMOERYRFhQaExwUExARFRYTFhMPFA4="), 28+(scale<<8)+(1<<16)); + return this; +} + + +// timeout used to update every minute +var drawTimeout; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + + +function drawBorderString(str, x, y, b, fc){ + g.setColor("#000"); + g.drawString(str, x, y+b); + g.drawString(str, x, y-b); + g.drawString(str, x+b, y); + g.drawString(str, x-b, y); + + g.setColor(fc); + g.drawString(str,x+1,y); +} + + +function getSteps() { + try{ + if (WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom.getSteps(); + } else if (WIDGETS.activepedom !== undefined) { + return WIDGETS.activepedom.getSteps(); + } + } catch(ex) { + // In case we failed, we can only show 0 steps. + } + + return 0; +} + + +function draw() { + // queue draw in one minute + queueDraw(); + + var x = g.getWidth()/2; + var y_offset = settings.fullscreen ? 0 : 10; + var y = g.getHeight()/2-20 + y_offset; + + g.reset().clearRect(0,24,g.getWidth(),g.getHeight()); + g.drawImage(getImg(),0,0); + + // Draw time + var date = new Date(); + var timeStr = locale.time(date,1); + g.setFontAlign(0,0); + g.setFontTime(); + drawBorderString(timeStr, x, y, 5, "#fff"); + + // Draw date + y += 50; + x = x - g.stringWidth(timeStr) / 2 + 5; + g.setFontDate(); + g.setFontAlign(-1,0); + var dateStr = locale.dow(date, true).toUpperCase() + date.getDate(); + var fc = Bangle.isLocked() ? "#0ff" :"#fff"; + fc = E.getBattery() < 50 ? "#f00" : fc; + drawBorderString(dateStr, x, y, 3, fc); + + // Draw steps + g.setFontAlign(1,1); + var steps = parseInt(getSteps() / 1000); + drawBorderString(steps, g.getWidth()-10, g.getHeight()-10, 3, "#f0f"); + + // Draw widgets if not fullscreen + if(settings.fullscreen){ + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} + } else { + Bangle.drawWidgets(); + } +} + +Bangle.loadWidgets(); + +// Clear the screen once, at startup +g.setTheme({bg:"#000",fg:"#fff",dark:false}).clear(); +// draw immediately at first, queue update +draw(); +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + + +Bangle.on('lock', function(isLocked) { + print("LOCK"); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + draw(); +}); + + +// Show launcher when middle button pressed +Bangle.setUI("clock"); diff --git a/apps/90sclk/app.png b/apps/90sclk/app.png new file mode 100644 index 000000000..29875b1dc Binary files /dev/null and b/apps/90sclk/app.png differ diff --git a/apps/90sclk/bg.png b/apps/90sclk/bg.png new file mode 100644 index 000000000..4ebf755ad Binary files /dev/null and b/apps/90sclk/bg.png differ diff --git a/apps/90sclk/metadata.json b/apps/90sclk/metadata.json new file mode 100644 index 000000000..fb2824a6f --- /dev/null +++ b/apps/90sclk/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "90sclk", + "name": "90s Clock", + "version": "0.02", + "description": "A 90s style watch-face", + "readme": "README.md", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_2.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"90sclk.app.js","url":"app.js"}, + {"name":"90sclk.img","url":"app-icon.js","evaluate":true}, + {"name":"90sclk.settings.js","url":"settings.js"} + ] +} diff --git a/apps/90sclk/screenshot.png b/apps/90sclk/screenshot.png new file mode 100644 index 000000000..182a85321 Binary files /dev/null and b/apps/90sclk/screenshot.png differ diff --git a/apps/90sclk/screenshot_2.png b/apps/90sclk/screenshot_2.png new file mode 100644 index 000000000..9646b1168 Binary files /dev/null and b/apps/90sclk/screenshot_2.png differ diff --git a/apps/90sclk/settings.js b/apps/90sclk/settings.js new file mode 100644 index 000000000..8f97cd317 --- /dev/null +++ b/apps/90sclk/settings.js @@ -0,0 +1,31 @@ +(function(back) { + const SETTINGS_FILE = "90sclk.setting.json"; + + // initialize with default settings... + const storage = require('Storage') + let settings = { + fullscreen: false, + }; + let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; + for (const key in saved_settings) { + settings[key] = saved_settings[key] + } + + function save() { + storage.write(SETTINGS_FILE, settings) + } + + + E.showMenu({ + '': { 'title': '90s Clock' }, + '< Back': back, + 'Full Screen': { + value: settings.fullscreen, + format: () => (settings.fullscreen ? 'Yes' : 'No'), + onchange: () => { + settings.fullscreen = !settings.fullscreen; + save(); + }, + } + }); + }) diff --git a/apps/_example_app/app.js b/apps/_example_app/app.js index af367779a..06c254a36 100644 --- a/apps/_example_app/app.js +++ b/apps/_example_app/app.js @@ -1,12 +1,34 @@ // place your const, vars, functions or classes here -// special function to handle display switch on -Bangle.on('lcdPower', (on) => { - if (on) { - // call your app function here - // If you clear the screen, do Bangle.drawWidgets(); +// clear the screen +g.clear(); + +var n = 0; + +// redraw the screen +function draw() { + g.reset().clearRect(Bangle.appRect); + g.setFont("6x8").setFontAlign(0,0).drawString("Up / Down",g.getWidth()/2,g.getHeight()/2 - 20); + g.setFont("Vector",60).setFontAlign(0,0).drawString(n,g.getWidth()/2,g.getHeight()/2 + 30); +} + +// Respond to user input +Bangle.setUI({mode: "updown"}, function(dir) { + if (dir<0) { + n--; + draw(); + } else if (dir>0) { + n++; + draw(); + } else { + n = 0; + draw(); } }); -g.clear(); -// call your app function here +// First draw... +draw(); + +// Load widgets +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/accellog/ChangeLog b/apps/accellog/ChangeLog index c0097db80..80981fe27 100644 --- a/apps/accellog/ChangeLog +++ b/apps/accellog/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Use the new multiplatform 'Layout' library 0.03: Exit as first menu option, dont show decimal places for seconds +0.04: Localisation, change Exit->Back to allow back-arrow to appear on 2v13 firmware diff --git a/apps/accellog/app.js b/apps/accellog/app.js index c54c5002b..f4c1b3c5a 100644 --- a/apps/accellog/app.js +++ b/apps/accellog/app.js @@ -7,21 +7,21 @@ function getFileName(n) { function showMenu() { var menu = { - "" : { title : "Accel Logger" }, - "Exit" : function() { + "" : { title : /*LANG*/"Accel Logger" }, + "< Back" : function() { load(); }, - "File No" : { + /*LANG*/"File No" : { value : fileNumber, min : 0, max : MAXLOGS, onchange : v => { fileNumber=v; } }, - "Start" : function() { + /*LANG*/"Start" : function() { E.showMenu(); startRecord(); }, - "View Logs" : function() { + /*LANG*/"View Logs" : function() { viewLogs(); }, }; @@ -29,7 +29,7 @@ function showMenu() { } function viewLog(n) { - E.showMessage("Loading..."); + E.showMessage(/*LANG*/"Loading..."); var f = require("Storage").open(getFileName(n), "r"); var records = 0, l = "", ll=""; while ((l=f.readLine())!==undefined) {records++;ll=l;} @@ -37,29 +37,29 @@ function viewLog(n) { if (ll) length = Math.round( (ll.split(",")[0]|0)/1000 ); var menu = { - "" : { title : "Log "+n } + "" : { title : "Log "+n }, + "< Back" : () => { viewLogs(); } }; - menu[records+" Records"] = ""; - menu[length+" Seconds"] = ""; - menu["DELETE"] = function() { - E.showPrompt("Delete Log "+n).then(ok=>{ + menu[records+/*LANG*/" Records"] = ""; + menu[length+/*LANG*/" Seconds"] = ""; + menu[/*LANG*/"DELETE"] = function() { + E.showPrompt(/*LANG*/"Delete Log "+n).then(ok=>{ if (ok) { - E.showMessage("Erasing..."); + E.showMessage(/*LANG*/"Erasing..."); f.erase(); viewLogs(); } else viewLog(n); }); }; - menu["< Back"] = function() { - viewLogs(); - }; + E.showMenu(menu); } function viewLogs() { var menu = { - "" : { title : "Logs" } + "" : { title : /*LANG*/"Logs" }, + "< Back" : () => { showMenu(); } }; var hadLogs = false; @@ -67,14 +67,13 @@ function viewLogs() { var f = require("Storage").open(getFileName(i), "r"); if (f.readLine()!==undefined) { (function(i) { - menu["Log "+i] = () => viewLog(i); + menu[/*LANG*/"Log "+i] = () => viewLog(i); })(i); hadLogs = true; } } if (!hadLogs) - menu["No Logs Found"] = function(){}; - menu["< Back"] = function() { showMenu(); }; + menu[/*LANG*/"No Logs Found"] = function(){}; E.showMenu(menu); } @@ -83,7 +82,7 @@ function startRecord(force) { // check for existing file var f = require("Storage").open(getFileName(fileNumber), "r"); if (f.readLine()!==undefined) - return E.showPrompt("Overwrite Log "+fileNumber+"?").then(ok=>{ + return E.showPrompt(/*LANG*/"Overwrite Log "+fileNumber+"?").then(ok=>{ if (ok) startRecord(true); else showMenu(); }); } @@ -93,14 +92,14 @@ function startRecord(force) { var Layout = require("Layout"); var layout = new Layout({ type: "v", c: [ - {type:"txt", font:"6x8", label:"Samples", pad:2}, + {type:"txt", font:"6x8", label:/*LANG*/"Samples", pad:2}, {type:"txt", id:"samples", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg}, - {type:"txt", font:"6x8", label:"Time", pad:2}, + {type:"txt", font:"6x8", label:/*LANG*/"Time", pad:2}, {type:"txt", id:"time", font:"6x8:2", label:" - ", pad:5, bgCol:g.theme.bg}, - {type:"txt", font:"6x8:2", label:"RECORDING", bgCol:"#f00", pad:5, fillx:1}, + {type:"txt", font:"6x8:2", label:/*LANG*/"RECORDING", bgCol:"#f00", pad:5, fillx:1}, ] },{btns:[ // Buttons... - {label:"STOP", cb:()=>{ + {label:/*LANG*/"STOP", cb:()=>{ Bangle.removeListener('accel', accelHandler); showMenu(); }} diff --git a/apps/accellog/metadata.json b/apps/accellog/metadata.json index a30c9a6fc..fdf6cf320 100644 --- a/apps/accellog/metadata.json +++ b/apps/accellog/metadata.json @@ -2,7 +2,7 @@ "id": "accellog", "name": "Acceleration Logger", "shortName": "Accel Log", - "version": "0.03", + "version": "0.04", "description": "Logs XYZ acceleration data to a CSV file that can be downloaded to your PC", "icon": "app.png", "tags": "outdoor", diff --git a/apps/aclock/metadata.json b/apps/aclock/metadata.json index c483a4e8c..5e4b4b680 100644 --- a/apps/aclock/metadata.json +++ b/apps/aclock/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"aclock.app.js","url":"clock-analog.js"}, diff --git a/apps/activityreminder/ChangeLog b/apps/activityreminder/ChangeLog new file mode 100644 index 000000000..d4b5100a2 --- /dev/null +++ b/apps/activityreminder/ChangeLog @@ -0,0 +1,5 @@ +0.01: New App! +0.02: Fix the settings bug and some tweaking +0.03: Do not alarm while charging +0.04: Obey system quiet mode +0.05: Battery optimisation, add the pause option, bug fixes diff --git a/apps/activityreminder/README.md b/apps/activityreminder/README.md new file mode 100644 index 000000000..25e2c8d35 --- /dev/null +++ b/apps/activityreminder/README.md @@ -0,0 +1,14 @@ +# Activity reminder + +A reminder to take short walks for the ones with a sedentary lifestyle. +The alert will popup only if you didn't take your short walk yet. + +Different settings can be personalized: +- Enable : Enable/Disable the app +- Start hour: Hour to start the reminder +- End hour: Hour to end the reminder +- Max inactivity: Maximum inactivity time to allow before the alert. From 15 to 120 min +- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min +- Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min +- Min steps: Minimal amount of steps to count as an activity + diff --git a/apps/activityreminder/app-icon.js b/apps/activityreminder/app-icon.js new file mode 100644 index 000000000..418657961 --- /dev/null +++ b/apps/activityreminder/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwYda7dtwAQNmwRB2wQMgO2CIXACJcNCIfYCJYOCCgQRNJQYRM2ADBgwR/CKprRWAKPQWZ0DCIjXLjYREGpYODAQVgCBB3Btj+EAoQAGO4IdCgImDCAwLCAoo4IF4J3DCIPDCIQ4FO4VtwARCAoIRGRgQCBa4IRCKAQRERgOwIIIRDAoOACIoIBwwRHLIqMCFgIRCGQQRIWAYRLYQoREWwTmHO4IRCFgLXHPoi/CbogAFEAIRCWwTpKEwZBCHwK5BCJZEBCJZcCGQTLDCJK/BAQIRKMoaSDOIYAFeQYRMcYRWBXIUAWYPACIq8DagfACJQLCCIYsBU4QRF7B9CAogRGI4QLCAoprIMoZKER5C/DAoShMAo4AGfAQFIACQ=")) diff --git a/apps/activityreminder/app.js b/apps/activityreminder/app.js new file mode 100644 index 000000000..f3d72976e --- /dev/null +++ b/apps/activityreminder/app.js @@ -0,0 +1,42 @@ +function drawAlert() { + E.showPrompt("Inactivity detected", { + title: "Activity reminder", + buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } + }).then(function (v) { + if (v == 1) { + activityreminder_data.okDate = new Date(); + } + if (v == 2) { + activityreminder_data.dismissDate = new Date(); + } + if (v == 3) { + activityreminder_data.pauseDate = new Date(); + } + activityreminder.saveData(activityreminder_data); + load(); + }); + + // Obey system quiet mode: + if (!(storage.readJSON('setting.json', 1) || {}).quiet) { + Bangle.buzz(400); + } + setTimeout(load, 20000); +} + +function run() { + if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) { + drawAlert(); + } else { + eval(storage.read("activityreminder.settings.js"))(() => load()); + } +} + + +const activityreminder = require("activityreminder"); +const storage = require("Storage"); +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +const activityreminder_settings = activityreminder.loadSettings(); +const activityreminder_data = activityreminder.loadData(); +run(); diff --git a/apps/activityreminder/app.png b/apps/activityreminder/app.png new file mode 100644 index 000000000..91073c444 Binary files /dev/null and b/apps/activityreminder/app.png differ diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js new file mode 100644 index 000000000..7c094f521 --- /dev/null +++ b/apps/activityreminder/boot.js @@ -0,0 +1,45 @@ +function run() { + if (isNotWorn()) return; + let now = new Date(); + let h = now.getHours(); + let health = Bangle.getHealthStatus("day"); + + if (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) { + if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed + || health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch + activityreminder_data.stepsOnDate = health.steps; + activityreminder_data.stepsDate = now; + activityreminder.saveData(activityreminder_data); + /* todo in a futur release + add settimer to trigger like 10 secs after the stepsDate + minSteps + cancel all other timers of this app + */ + } + + if(activityreminder.mustAlert(activityreminder_data, activityreminder_settings)){ + load('activityreminder.app.js'); + } + } + +} + +function isNotWorn() { + // todo in a futur release check temperature and mouvement in a futur release + return Bangle.isCharging(); +} + +const activityreminder = require("activityreminder"); +const activityreminder_settings = activityreminder.loadSettings(); +if (activityreminder_settings.enabled) { + const activityreminder_data = activityreminder.loadData(); + if(activityreminder_data.firstLoad){ + activityreminder_data.firstLoad =false; + activityreminder.saveData(activityreminder_data); + } + setInterval(run, 60000); + /* todo in a futur release + increase setInterval time to something that is still sensible (5 mins ?) + add settimer to trigger like 10 secs after the stepsDate + minSteps + cancel all other timers of this app + */ +} diff --git a/apps/activityreminder/lib.js b/apps/activityreminder/lib.js new file mode 100644 index 000000000..5b7959827 --- /dev/null +++ b/apps/activityreminder/lib.js @@ -0,0 +1,57 @@ +const storage = require("Storage"); + +exports.loadSettings = function () { + return Object.assign({ + enabled: true, + startHour: 9, + endHour: 20, + maxInnactivityMin: 30, + dismissDelayMin: 15, + pauseDelayMin: 120, + minSteps: 50 + }, storage.readJSON("activityreminder.s.json", true) || {}); +}; + +exports.writeSettings = function (settings) { + storage.writeJSON("activityreminder.s.json", settings); +}; + +exports.saveData = function (data) { + storage.writeJSON("activityreminder.data.json", data); +}; + +exports.loadData = function () { + let health = Bangle.getHealthStatus("day"); + const data = Object.assign({ + firstLoad: true, + stepsDate: new Date(), + stepsOnDate: health.steps, + okDate: new Date(1970), + dismissDate: new Date(1970), + pauseDate: new Date(1970), + }, + storage.readJSON("activityreminder.data.json") || {}); + + if(typeof(data.stepsDate) == "string") + data.stepsDate = new Date(data.stepsDate); + if(typeof(data.okDate) == "string") + data.okDate = new Date(data.okDate); + if(typeof(data.dismissDate) == "string") + data.dismissDate = new Date(data.dismissDate); + if(typeof(data.pauseDate) == "string") + data.pauseDate = new Date(data.pauseDate); + + return data; +}; + +exports.mustAlert = function(activityreminder_data, activityreminder_settings) { + let now = new Date(); + if ((now - activityreminder_data.stepsDate) / 60000 > activityreminder_settings.maxInnactivityMin) { // inactivity detected + if ((now - activityreminder_data.okDate) / 60000 > 3 && // last alert anwsered with ok was more than 3 min ago + (now - activityreminder_data.dismissDate) / 60000 > activityreminder_settings.dismissDelayMin && // last alert was more than dismissDelayMin ago + (now - activityreminder_data.pauseDate) / 60000 > activityreminder_settings.pauseDelayMin) { // last alert was more than pauseDelayMin ago + return true; + } + } + return false; +} \ No newline at end of file diff --git a/apps/activityreminder/metadata.json b/apps/activityreminder/metadata.json new file mode 100644 index 000000000..15f10f2ed --- /dev/null +++ b/apps/activityreminder/metadata.json @@ -0,0 +1,23 @@ +{ + "id": "activityreminder", + "name": "Activity Reminder", + "shortName":"Activity Reminder", + "description": "A reminder to take short walks for the ones with a sedentary lifestyle", + "version":"0.05", + "icon": "app.png", + "type": "app", + "tags": "tool,activity", + "supports": ["BANGLEJS", "BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name": "activityreminder.app.js", "url":"app.js"}, + {"name": "activityreminder.boot.js", "url": "boot.js"}, + {"name": "activityreminder.settings.js", "url": "settings.js"}, + {"name": "activityreminder", "url": "lib.js"}, + {"name": "activityreminder.img", "url": "app-icon.js", "evaluate": true} + ], + "data": [ + {"name": "activityreminder.s.json"}, + {"name": "activityreminder.data.json"} + ] +} diff --git a/apps/activityreminder/settings.js b/apps/activityreminder/settings.js new file mode 100644 index 000000000..9dff61f48 --- /dev/null +++ b/apps/activityreminder/settings.js @@ -0,0 +1,76 @@ +(function (back) { + // Load settings + const activityreminder = require("activityreminder"); + const settings = activityreminder.loadSettings(); + + // Show the menu + E.showMenu({ + "": { "title": "Activity Reminder" }, + "< Back": () => back(), + 'Enable': { + value: settings.enabled, + format: v => v ? "Yes" : "No", + onchange: v => { + settings.enabled = v; + activityreminder.writeSettings(settings); + } + }, + 'Start hour': { + value: settings.startHour, + min: 0, max: 24, + onchange: v => { + settings.startHour = v; + activityreminder.writeSettings(settings); + } + }, + 'End hour': { + value: settings.endHour, + min: 0, max: 24, + onchange: v => { + settings.endHour = v; + activityreminder.writeSettings(settings); + } + }, + 'Max inactivity': { + value: settings.maxInnactivityMin, + min: 15, max: 120, + onchange: v => { + settings.maxInnactivityMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Dismiss delay': { + value: settings.dismissDelayMin, + min: 5, max: 60, + onchange: v => { + settings.dismissDelayMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Pause delay': { + value: settings.pauseDelayMin, + min: 30, max: 240, + onchange: v => { + settings.pauseDelayMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Min steps': { + value: settings.minSteps, + min: 10, max: 500, + onchange: v => { + settings.minSteps = v; + activityreminder.writeSettings(settings); + } + } + }); +}) diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 4576237a5..b952b1dcd 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -14,3 +14,16 @@ 0.13: Alarm widget state now updates when setting/resetting an alarm 0.14: Order of 'back' menu item 0.15: Fix hour/minute wrapping code for new menu system +0.16: Adding alarm library +0.17: Moving alarm internals to 'sched' library +0.18: Cope with >1 identical alarm at once (#1667) +0.19: Ensure rescheduled alarms that already fired have 'last' reset +0.20: Use the new 'sched' factories to initialize new alarms/timers +0.21: Fix time reset after a day of week change (#1676) +0.22: Refactor some methods to scheduling library +0.23: Fix regression with Days of Week (#1735) +0.24: Automatically save the alarm/timer when the user returns to the main menu using the back arrow + Add "Enable All", "Disable All" and "Remove All" actions +0.25: Fix redrawing selected Alarm/Timer entry inside edit submenu +0.26: Add support for Monday as first day of the week (#1780) +0.27: New UI! diff --git a/apps/alarm/README.md b/apps/alarm/README.md new file mode 100644 index 000000000..741946b0c --- /dev/null +++ b/apps/alarm/README.md @@ -0,0 +1,31 @@ +# Alarms & Timers + +This app allows you to add/modify any alarms and timers. + +It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps. + +## Menu overview + +- `New...` + - `New Alarm` → Configure a new alarm + - `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely) + - `New Timer` → Configure a new timer +- `Advanced` + - `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details + - `Enable All` → Enable _all_ disabled alarms & timers + - `Disable All` → Disable _all_ enabled alarms & timers + - `Delete All` → Delete _all_ alarms & timers + +## Creator + +- [Gordon Williams](https://github.com/gfwilliams) + +## Main Contributors + +- [Alessandro Cocco](https://github.com/alessandrococco) - New UI, full rewrite, new features +- [Sabin Iacob](https://github.com/m0n5t3r) - Auto snooze support +- [storm64](https://github.com/storm64) - Fix redrawing in submenus + +## Attributions + +All icons used in this app are from [icons8](https://icons8.com). diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js deleted file mode 100644 index a655dad1e..000000000 --- a/apps/alarm/alarm.js +++ /dev/null @@ -1,72 +0,0 @@ -// Chances are boot0.js got run already and scheduled *another* -// 'load(alarm.js)' - so let's remove it first! -clearInterval(); - -function formatTime(t) { - var hrs = 0|t; - var mins = Math.round((t-hrs)*60); - return hrs+":"+("0"+mins).substr(-2); -} - -function getCurrentHr() { - var time = new Date(); - return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); -} - -function showAlarm(alarm) { - var msg = formatTime(alarm.hr); - var buzzCount = 10; - if (alarm.msg) - msg += "\n"+alarm.msg; - Bangle.loadWidgets(); - Bangle.drawWidgets(); - E.showPrompt(msg,{ - title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", - buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins - }).then(function(sleep) { - buzzCount = 0; - if (sleep) { - if(alarm.ohr===undefined) alarm.ohr = alarm.hr; - alarm.hr += 10/60; // 10 minutes - } else { - alarm.last = (new Date()).getDate(); - if (alarm.ohr!==undefined) { - alarm.hr = alarm.ohr; - delete alarm.ohr; - } - if (!alarm.rp) alarm.on = false; - } - require("Storage").write("alarm.json",JSON.stringify(alarms)); - load(); - }); - function buzz() { - if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence - Bangle.buzz(100).then(()=>{ - setTimeout(()=>{ - Bangle.buzz(100).then(function() { - if (buzzCount--) - setTimeout(buzz, 3000); - else if(alarm.as) { // auto-snooze - buzzCount = 10; - setTimeout(buzz, 600000); - } - }); - },100); - }); - } - buzz(); -} - -// Check for alarms -var day = (new Date()).getDate(); -var hr = getCurrentHr()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early -var alarms = require("Storage").readJSON("alarm.json",1)||[]; -var active = alarms.filter(a=>a.on&&(a.hra.hr-b.hr); - showAlarm(active[0]); -} else { - // otherwise just go back to default app - setTimeout(load, 100); -} diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 56184edf1..0cf1f3d6f 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -1,181 +1,349 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -var alarms = require("Storage").readJSON("alarm.json",1)||[]; -/*alarms = [ - { on : true, - hr : 6.5, // hours + minutes/60 - msg : "Eat chocolate", - last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! - rp : true, // repeat - as : false, // auto snooze - timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes +// 0 = Sunday (default), 1 = Monday +const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0; +const WORKDAYS = 62 +const WEEKEND = firstDayOfWeek ? 192 : 65; +const EVERY_DAY = firstDayOfWeek ? 254 : 127; + +const iconAlarmOn = "\0" + atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA=="); +const iconAlarmOff = "\0" + (g.theme.dark + ? atob("GBjBAP////8AAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg=") + : atob("GBjBAP//AAAAAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg=")); + +const iconTimerOn = "\0" + (g.theme.dark + ? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA=") + : atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA=")); +const iconTimerOff = "\0" + (g.theme.dark + ? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg=") + : atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg=")); + +// An array of alarm objects (see sched/README.md) +var alarms = require("sched").getAlarms(); + +function handleFirstDayOfWeek(dow) { + if (firstDayOfWeek == 1) { + if ((dow & 1) == 1) { + // In the scheduler API Sunday is 1. + // Here the week starts on Monday and Sunday is ON so + // when I read the dow I need to move Sunday to 128... + dow += 127; + } else if ((dow & 128) == 128) { + // ... and then when I write the dow I need to move Sunday back to 1. + dow -= 127; + } } -];*/ - -function formatTime(t) { - var hrs = 0|t; - var mins = Math.round((t-hrs)*60); - return hrs+":"+("0"+mins).substr(-2); + return dow; } -function formatMins(t) { - mins = (0|t)%60; - hrs = 0|(t/60); - return hrs+":"+("0"+mins).substr(-2); -} - -function getCurrentHr() { - var time = new Date(); - return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); -} +// Check the first day of week and update the dow field accordingly. +alarms.forEach(alarm => alarm.dow = handleFirstDayOfWeek(alarm.dow)); function showMainMenu() { const menu = { - '': { 'title': 'Alarm/Timer' }, - /*LANG*/'< Back' : ()=>{load();}, - /*LANG*/'New Alarm': ()=>editAlarm(-1), - /*LANG*/'New Timer': ()=>editTimer(-1) + "": { "title": /*LANG*/"Alarms & Timers" }, + "< Back": () => load(), + /*LANG*/"New...": () => showNewMenu() }; - alarms.forEach((alarm,idx)=>{ - if (alarm.timer) { - txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer); - } else { - txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr); - if (alarm.rp) txt += /*LANG*/" (repeat)"; - } - menu[txt] = function() { - if (alarm.timer) editTimer(idx); - else editAlarm(idx); + + alarms.forEach((e, index) => { + var label = e.timer + ? require("time_utils").formatDuration(e.timer) + : require("time_utils").formatTime(e.t) + (e.dow > 0 ? (" " + decodeDOW(e)) : ""); + menu[label] = { + value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff), + onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index) }; }); - if (WIDGETS["alarm"]) WIDGETS["alarm"].reload(); - return E.showMenu(menu); + menu[/*LANG*/"Advanced"] = () => showAdvancedMenu(); + + E.showMenu(menu); } -function editAlarm(alarmIndex) { - var newAlarm = alarmIndex<0; - var hrs = 12; - var mins = 0; - var en = true; - var repeat = true; - var as = false; - if (!newAlarm) { - var a = alarms[alarmIndex]; - hrs = 0|a.hr; - mins = Math.round((a.hr-hrs)*60); - en = a.on; - repeat = a.rp; - as = a.as; - } - const menu = { - '': { 'title': /*LANG*/'Alarm' }, - /*LANG*/'< Back' : showMainMenu, - /*LANG*/'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v - }, - /*LANG*/'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v - }, - /*LANG*/'Enabled': { - value: en, - format: v=>v?"On":"Off", - onchange: v=>en=v - }, - /*LANG*/'Repeat': { - value: en, - format: v=>v?"Yes":"No", - onchange: v=>repeat=v - }, - /*LANG*/'Auto snooze': { - value: as, - format: v=>v?"Yes":"No", - onchange: v=>as=v - } - }; - function getAlarm() { - var hr = hrs+(mins/60); - var day = 0; - // If alarm is for tomorrow not today (eg, in the past), set day - if (hr < getCurrentHr()) - day = (new Date()).getDate(); - // Save alarm - return { - on : en, hr : hr, - last : day, rp : repeat, as: as - }; - } - menu[/*LANG*/"> Save"] = function() { - if (newAlarm) alarms.push(getAlarm()); - else alarms[alarmIndex] = getAlarm(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showMainMenu(); - }; - if (!newAlarm) { - menu[/*LANG*/"> Delete"] = function() { - alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showMainMenu(); - }; - } - return E.showMenu(menu); +function showNewMenu() { + E.showMenu({ + "": { "title": /*LANG*/"New..." }, + "< Back": () => showMainMenu(), + /*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined), + /*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined) + }); } -function editTimer(alarmIndex) { - var newAlarm = alarmIndex<0; - var hrs = 0; - var mins = 5; - var en = true; - if (!newAlarm) { - var a = alarms[alarmIndex]; - mins = (0|a.timer)%60; - hrs = 0|(a.timer/60); - en = a.on; +function showEditAlarmMenu(selectedAlarm, alarmIndex) { + var isNew = alarmIndex === undefined; + + var alarm = require("sched").newDefaultAlarm(); + alarm.dow = handleFirstDayOfWeek(alarm.dow); + + if (selectedAlarm) { + Object.assign(alarm, selectedAlarm); } + + var time = require("time_utils").decodeTime(alarm.t); + const menu = { - '': { 'title': /*LANG*/'Timer' }, - /*LANG*/'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v + "": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" }, + "< Back": () => { + saveAlarm(alarm, alarmIndex, time); + showMainMenu(); }, - /*LANG*/'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v + /*LANG*/"Hour": { + value: time.h, + format: v => ("0" + v).substr(-2), + min: 0, + max: 23, + wrap: true, + onchange: v => time.h = v }, - /*LANG*/'Enabled': { - value: en, - format: v=>v?/*LANG*/"On":/*LANG*/"Off", - onchange: v=>en=v + /*LANG*/"Minute": { + value: time.m, + format: v => ("0" + v).substr(-2), + min: 0, + max: 59, + wrap: true, + onchange: v => time.m = v + }, + /*LANG*/"Enabled": { + value: alarm.on, + onchange: v => alarm.on = v + }, + /*LANG*/"Repeat": { + value: decodeDOW(alarm), + onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.dow, dow => { + alarm.rp = dow > 0; + alarm.dow = dow; + alarm.t = require("time_utils").encodeTime(time); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex); + }) + }, + /*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v), + /*LANG*/"Auto Snooze": { + value: alarm.as, + onchange: v => alarm.as = v + }, + /*LANG*/"Cancel": () => showMainMenu() + }; + + if (!isNew) { + menu[/*LANG*/"Delete"] = () => { + E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => { + if (confirm) { + alarms.splice(alarmIndex, 1); + saveAndReload(); + showMainMenu(); + } else { + alarm.t = require("time_utils").encodeTime(time); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex); + } + }); + }; + } + + E.showMenu(menu); +} + +function saveAlarm(alarm, alarmIndex, time) { + alarm.t = require("time_utils").encodeTime(time); + alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0; + + if (alarmIndex === undefined) { + alarms.push(alarm); + } else { + alarms[alarmIndex] = alarm; + } + + saveAndReload(); +} + +function saveAndReload() { + // Before saving revert the dow to the standard format + alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek)); + + require("sched").setAlarms(alarms); + require("sched").reload(); + + // Fix after save + alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek)); +} + +function decodeDOW(alarm) { + return alarm.rp + ? require("date_utils") + .dows(firstDayOfWeek, 2) + .map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_") + .join("") + .toLowerCase() + : "Once" +} + +function showEditRepeatMenu(dow, dowChangeCallback) { + var originalDow = dow; + var isCustom = dow > 0 && dow != WORKDAYS && dow != WEEKEND && dow != EVERY_DAY; + + const menu = { + "": { "title": /*LANG*/"Repeat Alarm" }, + "< Back": () => dowChangeCallback(dow), + /*LANG*/"Once": { // No days set: the alarm will fire once + value: dow == 0, + onchange: () => dowChangeCallback(0) + }, + /*LANG*/"Workdays": { + value: dow == WORKDAYS, + onchange: () => dowChangeCallback(WORKDAYS) + }, + /*LANG*/"Weekends": { + value: dow == WEEKEND, + onchange: () => dowChangeCallback(WEEKEND) + }, + /*LANG*/"Every Day": { + value: dow == EVERY_DAY, + onchange: () => dowChangeCallback(EVERY_DAY) + }, + /*LANG*/"Custom": { + value: isCustom ? decodeDOW({ rp: true, dow: dow }) : false, + onchange: () => setTimeout(showCustomDaysMenu, 10, isCustom ? dow : EVERY_DAY, dowChangeCallback, originalDow) } }; - function getTimer() { - var d = new Date(Date.now() + ((hrs*60)+mins)*60000); - var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600); - // Save alarm - return { - on : en, - timer : (hrs*60)+mins, - hr : hr, - rp : false, as: false - }; - } - menu["> Save"] = function() { - if (newAlarm) alarms.push(getTimer()); - else alarms[alarmIndex] = getTimer(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showMainMenu(); + + E.showMenu(menu); +} + +function showCustomDaysMenu(dow, dowChangeCallback, originalDow) { + const menu = { + "": { "title": /*LANG*/"Custom Days" }, + "< Back": () => dowChangeCallback(dow), }; - if (!newAlarm) { - menu["> Delete"] = function() { - alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); + + require("date_utils").dows(firstDayOfWeek).forEach((day, i) => { + menu[day] = { + value: !!(dow & (1 << (i + firstDayOfWeek))), + onchange: v => v ? (dow |= 1 << (i + firstDayOfWeek)) : (dow &= ~(1 << (i + firstDayOfWeek))) + }; + }); + + menu[/*LANG*/"Cancel"] = () => setTimeout(showEditRepeatMenu, 10, originalDow, dowChangeCallback) + + E.showMenu(menu); +} + +function showEditTimerMenu(selectedTimer, timerIndex) { + var isNew = timerIndex === undefined; + + var timer = require("sched").newDefaultTimer(); + + if (selectedTimer) { + Object.assign(timer, selectedTimer); + } + + var time = require("time_utils").decodeTime(timer.timer); + + const menu = { + "": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" }, + "< Back": () => { + saveTimer(timer, timerIndex, time); showMainMenu(); + }, + /*LANG*/"Hours": { + value: time.h, + min: 0, + max: 23, + wrap: true, + onchange: v => time.h = v + }, + /*LANG*/"Minutes": { + value: time.m, + min: 0, + max: 59, + wrap: true, + onchange: v => time.m = v + }, + /*LANG*/"Enabled": { + value: timer.on, + onchange: v => timer.on = v + }, + /*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v), + }; + + if (!isNew) { + menu[/*LANG*/"Delete"] = () => { + E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => { + if (confirm) { + alarms.splice(timerIndex, 1); + saveAndReload(); + showMainMenu(); + } else { + timer.timer = require("time_utils").encodeTime(time); + setTimeout(showEditTimerMenu, 10, timer, timerIndex) + } + }); }; } - return E.showMenu(menu); + + E.showMenu(menu); +} + +function saveTimer(timer, timerIndex, time) { + timer.timer = require("time_utils").encodeTime(time); + timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer; + timer.last = 0; + + if (timerIndex === undefined) { + alarms.push(timer); + } else { + alarms[timerIndex] = timer; + } + + saveAndReload(); +} + +function showAdvancedMenu() { + E.showMenu({ + "": { "title": /*LANG*/"Advanced" }, + "< Back": () => showMainMenu(), + /*LANG*/"Scheduler Settings": () => eval(require("Storage").read("sched.settings.js"))(() => showAdvancedMenu()), + /*LANG*/"Enable All": () => enableAll(true), + /*LANG*/"Disable All": () => enableAll(false), + /*LANG*/"Delete All": () => deleteAll() + }); +} + +function enableAll(on) { + if (alarms.filter(e => e.on == !on).length == 0) { + E.showPrompt(on ? /*LANG*/"Nothing to Enable" : /*LANG*/"Nothing to Disable", { + title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All", + buttons: { /*LANG*/"Ok": true } + }).then(() => showAdvancedMenu()); + } else { + E.showPrompt(/*LANG*/"Are you sure?", { title: on ? "/*LANG*/Enable All" : /*LANG*/"Disable All" }).then((confirm) => { + if (confirm) { + alarms.forEach(alarm => alarm.on = on); + saveAndReload(); + showMainMenu(); + } else { + showAdvancedMenu(); + } + }); + } +} + +function deleteAll() { + if (alarms.length == 0) { + E.showPrompt(/*LANG*/"Nothing to delete", { title: /*LANG*/"Delete All", buttons: { /*LANG*/"Ok": true } }).then(() => showAdvancedMenu()); + } else { + E.showPrompt(/*LANG*/"Are you sure?", { + title: /*LANG*/"Delete All" + }).then((confirm) => { + if (confirm) { + alarms = []; + saveAndReload(); + showMainMenu(); + } else { + showAdvancedMenu(); + } + }); + } } showMainMenu(); diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js deleted file mode 100644 index 47dae5361..000000000 --- a/apps/alarm/boot.js +++ /dev/null @@ -1,25 +0,0 @@ -// check for alarms -(function() { - var alarms = require('Storage').readJSON('alarm.json',1)||[]; - var time = new Date(); - var active = alarms.filter(a=>a.on); - if (active.length) { - active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24); - var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); - if (!require('Storage').read("alarm.js")) { - console.log("No alarm app!"); - require('Storage').write('alarm.json',"[]"); - } else { - var t = 3600000*(active[0].hr-hr); - if (active[0].last == time.getDate() || t < 0) t += 86400000; - if (t<1000) t=1000; - /* execute alarm at the correct time. We avoid execing immediately - since this code will get called AGAIN when alarm.js is loaded. alarm.js - will then clearInterval() to get rid of this call so it can proceed - normally. */ - setTimeout(function() { - load("alarm.js"); - },t); - } - } -})(); diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index d29298309..b9ce55756 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -1,18 +1,30 @@ { "id": "alarm", - "name": "Default Alarm & Timer", + "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.15", - "description": "Set and respond to alarms and timers", + "version": "0.27", + "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm,widget", - "supports": ["BANGLEJS","BANGLEJS2"], + "supports": [ "BANGLEJS", "BANGLEJS2" ], + "readme": "README.md", + "dependencies": { "scheduler":"type" }, "storage": [ - {"name":"alarm.app.js","url":"app.js"}, - {"name":"alarm.boot.js","url":"boot.js"}, - {"name":"alarm.js","url":"alarm.js"}, - {"name":"alarm.img","url":"app-icon.js","evaluate":true}, - {"name":"alarm.wid.js","url":"widget.js"} + { "name": "alarm.app.js", "url": "app.js" }, + { "name": "alarm.img", "url": "app-icon.js", "evaluate": true }, + { "name": "alarm.wid.js", "url": "widget.js" } ], - "data": [{"name":"alarm.json"}] + "screenshots": [ + { "url": "screenshot-1.png" }, + { "url": "screenshot-2.png" }, + { "url": "screenshot-3.png" }, + { "url": "screenshot-4.png" }, + { "url": "screenshot-5.png" }, + { "url": "screenshot-6.png" }, + { "url": "screenshot-7.png" }, + { "url": "screenshot-8.png" }, + { "url": "screenshot-9.png" }, + { "url": "screenshot-10.png" }, + { "url": "screenshot-11.png" } + ] } diff --git a/apps/alarm/screenshot-1.png b/apps/alarm/screenshot-1.png new file mode 100644 index 000000000..d2bd3a409 Binary files /dev/null and b/apps/alarm/screenshot-1.png differ diff --git a/apps/alarm/screenshot-10.png b/apps/alarm/screenshot-10.png new file mode 100644 index 000000000..1e6e516c3 Binary files /dev/null and b/apps/alarm/screenshot-10.png differ diff --git a/apps/alarm/screenshot-11.png b/apps/alarm/screenshot-11.png new file mode 100644 index 000000000..197c84194 Binary files /dev/null and b/apps/alarm/screenshot-11.png differ diff --git a/apps/alarm/screenshot-2.png b/apps/alarm/screenshot-2.png new file mode 100644 index 000000000..1cbc255a9 Binary files /dev/null and b/apps/alarm/screenshot-2.png differ diff --git a/apps/alarm/screenshot-3.png b/apps/alarm/screenshot-3.png new file mode 100644 index 000000000..a165d3594 Binary files /dev/null and b/apps/alarm/screenshot-3.png differ diff --git a/apps/alarm/screenshot-4.png b/apps/alarm/screenshot-4.png new file mode 100644 index 000000000..7fd7e99b6 Binary files /dev/null and b/apps/alarm/screenshot-4.png differ diff --git a/apps/alarm/screenshot-5.png b/apps/alarm/screenshot-5.png new file mode 100644 index 000000000..4174c5670 Binary files /dev/null and b/apps/alarm/screenshot-5.png differ diff --git a/apps/alarm/screenshot-6.png b/apps/alarm/screenshot-6.png new file mode 100644 index 000000000..dc579ca5c Binary files /dev/null and b/apps/alarm/screenshot-6.png differ diff --git a/apps/alarm/screenshot-7.png b/apps/alarm/screenshot-7.png new file mode 100644 index 000000000..49da44710 Binary files /dev/null and b/apps/alarm/screenshot-7.png differ diff --git a/apps/alarm/screenshot-8.png b/apps/alarm/screenshot-8.png new file mode 100644 index 000000000..86d69cd93 Binary files /dev/null and b/apps/alarm/screenshot-8.png differ diff --git a/apps/alarm/screenshot-9.png b/apps/alarm/screenshot-9.png new file mode 100644 index 000000000..2d8c7fc83 Binary files /dev/null and b/apps/alarm/screenshot-9.png differ diff --git a/apps/alarm/widget.js b/apps/alarm/widget.js index e8bb79fc7..052ac9ebd 100644 --- a/apps/alarm/widget.js +++ b/apps/alarm/widget.js @@ -1,7 +1,8 @@ WIDGETS["alarm"]={area:"tl",width:0,draw:function() { if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); },reload:function() { - WIDGETS["alarm"].width = (require('Storage').readJSON('alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0; + // don't include library here as we're trying to use as little RAM as possible + WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0; } }; WIDGETS["alarm"].reload(); diff --git a/apps/alpinenav/app-icon.js b/apps/alpinenav/app-icon.js index dba084202..6708ee67f 100644 --- a/apps/alpinenav/app-icon.js +++ b/apps/alpinenav/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mUywkEIf4A/AHUBiAYWgcwDC0v+IYW///C6sC+c/kAYUj/xj/wDCgvBgfyVihhBAQQASh6TCMikvYoRkU/73CMicD+ZnFViJFBj5MBMiU/+IuBJoJkRCoUvfIPy/5kQVgM//7gBC4KCDFxSsDgTHCl8QWgaRKmBJBFIzmDSJXzYBECWobbJAAKNIMhYlBOoK/IMhZXCmYMLABAkCS4RkSXZoNJRBo/CgK6UBwTWBBIs/SJBAGl7UFegIXMaogHEehAAHj/yIYsfehAAGMQISFMRxbCiEDU4ZiQZY5iQZYpiSbQ8/cwzLOCiQA/AH4A1A")) \ No newline at end of file +require("heatshrink").decompress(atob("mEkgIRO4AFJgPgAocDAoswAocHAokGjAFDhgFFhgFDjEOAoc4gxSE44FDuPjAod//+AAoXfn4FCgPMjJUCmIJBAoU7AoJUCv4CBsACBtwCBuACB4w3CEQIaCKgMBFgQFBgYFCLQMDMIfAg55D4BcDg/gNAcD+B0DSIMcOgiGEjCYEjgFEhhVCUgQ")) diff --git a/apps/altimeter/ChangeLog b/apps/altimeter/ChangeLog new file mode 100644 index 000000000..29388520e --- /dev/null +++ b/apps/altimeter/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Actually upload correct code diff --git a/apps/altimeter/app-icon.js b/apps/altimeter/app-icon.js new file mode 100644 index 000000000..1f8dfb637 --- /dev/null +++ b/apps/altimeter/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///t9TmuV3+GJf4AN+ALVgf8BasP/4LVn//4ALUWgJUJBZUDBYJUIBZcP3/nKhEOt/WBZE5r+VKg0KgEVr9V3wLHqtaqt9sALElWAqoABt1QBZNeBYuq0ILCrVUBYulBYVWBYkCBYgABBZ8K1WVBYlABZegKQWqBQlVqALKqWoKQWpBYtWBZeqKRAAB1WABZZSHAANq0ALLKQ6qC1ALLKQ5UEAH4AG")) diff --git a/apps/altimeter/app.js b/apps/altimeter/app.js new file mode 100644 index 000000000..cac4e80fd --- /dev/null +++ b/apps/altimeter/app.js @@ -0,0 +1,30 @@ +Bangle.setBarometerPower(true, "app"); + +g.clear(1); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +var zero = 0; +var R = Bangle.appRect; +var y = R.y + R.h/2; +var MEDIANLENGTH = 20; +var avr = [], median; +var value = 0; + +Bangle.on('pressure', function(e) { + while (avr.length>MEDIANLENGTH) avr.pop(); + avr.unshift(e.altitude); + median = avr.slice().sort(); + g.reset().clearRect(0,y-30,g.getWidth()-10,y+30); + if (median.length>10) { + var mid = median.length>>1; + value = E.sum(median.slice(mid-4,mid+5)) / 9; + g.setFont("Vector",50).setFontAlign(0,0).drawString((value-zero).toFixed(1), g.getWidth()/2, y); + } +}); + +g.reset(); +g.setFont("6x8").setFontAlign(0,0).drawString(/*LANG*/"ALTITUDE (m)", g.getWidth()/2, y-40); +g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"ZERO", g.getWidth()-5, g.getHeight()/2); +setWatch(function() { + zero = value; +}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true}); diff --git a/apps/altimeter/app.png b/apps/altimeter/app.png new file mode 100644 index 000000000..9c9d69077 Binary files /dev/null and b/apps/altimeter/app.png differ diff --git a/apps/altimeter/metadata.json b/apps/altimeter/metadata.json new file mode 100644 index 000000000..8bdbf3022 --- /dev/null +++ b/apps/altimeter/metadata.json @@ -0,0 +1,12 @@ +{ "id": "altimeter", + "name": "Altimeter", + "version":"0.02", + "description": "Simple altimeter that can display height changed using Bangle.js 2's built in pressure sensor.", + "icon": "app.png", + "tags": "tool,outdoors", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"altimeter.app.js","url":"app.js"}, + {"name":"altimeter.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index 59cb23a46..96b50c3a0 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -6,3 +6,4 @@ 0.05: Fix handling of message actions 0.06: Option to keep messages after a disconnect (default false) (fix #1186) 0.07: Include charging state in battery updates to phone +0.08: Handling of alarms diff --git a/apps/android/README.md b/apps/android/README.md index c10718aac..580eeec9a 100644 --- a/apps/android/README.md +++ b/apps/android/README.md @@ -21,6 +21,7 @@ of Gadgetbridge - making your phone make noise so you can find it. * `Keep Msgs` - default is `Off`. When Gadgetbridge disconnects, should Bangle.js keep any messages it has received, or should it delete them? * `Messages` - launches the messages app, showing a list of messages +* `Alarms` - opens a submenu where you can set default settings for alarms such as vibration pattern, repeat, and auto snooze ## How it works diff --git a/apps/android/boot.js b/apps/android/boot.js index eb3d26c6e..9e24c9893 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -5,6 +5,11 @@ } var settings = require("Storage").readJSON("android.settings.json",1)||{}; + //default alarm settings + if (settings.rp == undefined) settings.rp = true; + if (settings.as == undefined) settings.as = true; + if (settings.vibrate == undefined) settings.vibrate = ".."; + require('Storage').writeJSON("android.settings.json", settings); var _GB = global.GB; global.GB = (event) => { // feed a copy to other handlers if there were any @@ -44,6 +49,40 @@ title:event.name||"Call", body:"Incoming call\n"+event.number}); require("messages").pushMessage(event); }, + "alarm" : function() { + //wipe existing GB alarms + var sched; + try { sched = require("sched"); } catch (e) {} + if (!sched) return; // alarms may not be installed + var gbalarms = sched.getAlarms().filter(a=>a.appid=="gbalarms"); + for (var i = 0; i < gbalarms.length; i++) + sched.setAlarm(gbalarms[i].id, undefined); + var alarms = sched.getAlarms(); + var time = new Date(); + var currentTime = time.getHours() * 3600000 + + time.getMinutes() * 60000 + + time.getSeconds() * 1000; + for (var j = 0; j < event.d.length; j++) { + // prevents all alarms from going off at once?? + var dow = event.d[j].rep; + if (!dow) dow = 127; //if no DOW selected, set alarm to all DOW + var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0; + var a = { + id : "gb"+j, + appid : "gbalarms", + on : true, + t : event.d[j].h * 3600000 + event.d[j].m * 60000, + dow : ((dow&63)<<1) | (dow>>6), // Gadgetbridge sends DOW in a different format + last : last, + rp : settings.rp, + as : settings.as, + vibrate : settings.vibrate + }; + alarms.push(a); + } + sched.setAlarms(alarms); + sched.reload(); + }, }; var h = HANDLERS[event.t]; if (h) h(); else console.log("GB Unknown",event); diff --git a/apps/android/metadata.json b/apps/android/metadata.json index d126b869a..203cd18b1 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.07", + "version": "0.08", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", diff --git a/apps/android/settings.js b/apps/android/settings.js index 7c46a1fc0..9f72947ab 100644 --- a/apps/android/settings.js +++ b/apps/android/settings.js @@ -10,8 +10,8 @@ "" : { "title" : "Android" }, "< Back" : back, /*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" }, - "Find Phone" : () => E.showMenu({ - "" : { "title" : "Find Phone" }, + /*LANG*/"Find Phone" : () => E.showMenu({ + "" : { "title" : /*LANG*/"Find Phone" }, "< Back" : ()=>E.showMenu(mainmenu), /*LANG*/"On" : _=>gb({t:"findPhone",n:true}), /*LANG*/"Off" : _=>gb({t:"findPhone",n:false}), @@ -24,7 +24,28 @@ updateSettings(); } }, - /*LANG*/"Messages" : ()=>load("messages.app.js") + /*LANG*/"Messages" : ()=>load("messages.app.js"), + /*LANG*/"Alarms" : () => E.showMenu({ + "" : { "title" : /*LANG*/"Alarms" }, + "< Back" : ()=>E.showMenu(mainmenu), + /*LANG*/"Vibrate": require("buzz_menu").pattern(settings.vibrate, v => {settings.vibrate = v; updateSettings();}), + /*LANG*/"Repeat": { + value: settings.rp, + format : v=>v?/*LANG*/"Yes":/*LANG*/"No", + onchange: v => { + settings.rp = v; + updateSettings(); + } + }, + /*LANG*/"Auto snooze": { + value: settings.as, + format : v=>v?/*LANG*/"Yes":/*LANG*/"No", + onchange: v => { + settings.as = v; + updateSettings(); + } + }, + }) }; E.showMenu(mainmenu); }) diff --git a/apps/antonclk/ChangeLog b/apps/antonclk/ChangeLog index ac49dcbd7..73a63f7c7 100644 --- a/apps/antonclk/ChangeLog +++ b/apps/antonclk/ChangeLog @@ -8,4 +8,5 @@ 0.06: fixes #1271 - wrong settings name when weekday name and calendar weeknumber are on then display is # week is buffered until date or timezone changes -0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users) \ No newline at end of file +0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users) +0.08: fixed calendar weeknumber not shortened to two digits \ No newline at end of file diff --git a/apps/antonclk/app.js b/apps/antonclk/app.js index 7b40d8eb5..4b1e71bda 100644 --- a/apps/antonclk/app.js +++ b/apps/antonclk/app.js @@ -99,7 +99,7 @@ function updateState() { } function isoStr(date) { - return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2); + return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2); } var calWeekBuffer = [false,false,false]; //buffer tz, date, week no (once calculated until other tz or date is requested) @@ -140,7 +140,7 @@ function draw() { g.setFontAlign(0, 0).setFont("Anton").drawString(timeStr, x, y); // draw time if (secondsScreen) { y += 65; - var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).substr(-2); + var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).slice(-2); if (doColor()) g.setColor(0, 0, 1); g.setFont("AntonSmall"); @@ -193,7 +193,7 @@ function draw() { if (calWeek || weekDay) { var dowcwStr = ""; if (calWeek) - dowcwStr = " #" + ("0" + ISO8601calWeek(date)).substring(-2); + dowcwStr = " #" + ("0" + ISO8601calWeek(date)).slice(-2); if (weekDay) dowcwStr = require("locale").dow(date, calWeek ? 1 : 0) + dowcwStr; //weekDay e.g. Monday or weekDayShort # e.g. Mon #01 else //week #01 diff --git a/apps/antonclk/metadata.json b/apps/antonclk/metadata.json index 4d26dd0c7..c58ee2a1b 100644 --- a/apps/antonclk/metadata.json +++ b/apps/antonclk/metadata.json @@ -1,7 +1,7 @@ { "id": "antonclk", "name": "Anton Clock", - "version": "0.07", + "version": "0.08", "description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.", "readme":"README.md", "icon": "app.png", diff --git a/apps/arrow/icon.js b/apps/arrow/icon.js index 380728484..917a5c979 100644 --- a/apps/arrow/icon.js +++ b/apps/arrow/icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mUywIebg/4AocP//AAoUf//+BYgMDh/+j/8Dol/wEAgYFBg/wgEBFIV+AQIVCh4fBnwFBgISBj8AhgJCh+Ag4BB4ED8ED+ASCAYJDBnkAvkAIYIWBjw8B/EB8AcBn//gF4DwJdBAQMA/EP738FYM8g/nz+A+EPgHx8YKBgfAjF4sAKBHIItBBQJMBFoJEBHII1BIQIDCvAUCAYYUBHIIDBMIXACgQpBRAIUBMIIrBDAIWCVYaiBTYQJCn4FBQgIIBEYKrDQ4MBVYUf8CQCCoP/w6DBAAKIBAocHAoIwBBgb5DDoYAZA=")) +require("heatshrink").decompress(atob("kkkwIEBgf8AYMB//4AgN///ggEf4E/wED+EACQN8C4Pgh4TBh8BCYMAvEcEoWD4AEBnk4gFggPHwAXBj1wgIwB88An/Ah3gg/+gF+gH/+EH8Ef/+ABAPvuAIBgnyCIQjBBAMAJAIIEuAICFgIIBh14BAMB8eAg0Ajk8KAXBKAU4jwDBg+ADoIXBg4NBnxPBEgPAgP8gZaBg//KoKLBKAIEBMQMAA")) diff --git a/apps/astral/app-icon.js b/apps/astral/app-icon.js index 19d0998ff..d10e7a498 100644 --- a/apps/astral/app-icon.js +++ b/apps/astral/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mUyxH+AH4AG3YAGF1w0oExYykEZwyhEIyRJGUAfEYpgxjLxQNEGEajMGTohPGMBTQOZwwTGKoyXDASVWGSwtHKYYAJZbYVEGR7bSGKQWkDRQbOCAoxYRI4wMCIYxXXpQSYP6L4NCRLGXLZwdVMJwAWGKgwbD6aUTSzoRKfCAxbAogcJBxQx/GP4x/GP4xNAAoKKBxwxaGRQZPSqwZmGOZ7VY8oxnPZoJPGP57TBJavWGL7gRRaiPVGJxRGBJgxcACYxfHJIRLSrTHxGODHvGSgwcAEY=")) \ No newline at end of file +require("heatshrink").decompress(atob("kUw4MA///xP5gEH/AMBh//4AHBwF4gEDwEHgEB4fw8EAsf/jEAjPh80AhngjnAgcwAIMB5kA50A+cAmfAtnAhnYmc//8zhln/+c4YjBg0w440Bxk38EB/cP/0B//Dwf/+FxwEf8EGIAJGB2BkCnhiB4EPgF//EDFQIpB+HGgOMnkxwFjh8MsEY4YQHn/x//j//8n/wHYItBCAKFBhgKBKAIQBBgIQC4AQCmAQChkD/v8gcA/wCBBoMA7+39kAPwP/WIMP4aYBCAYhCCAkHAYOAA=")) diff --git a/apps/authentiwatch/ChangeLog b/apps/authentiwatch/ChangeLog index bb2945db4..655916170 100644 --- a/apps/authentiwatch/ChangeLog +++ b/apps/authentiwatch/ChangeLog @@ -4,3 +4,4 @@ 0.04: Fix tapping at very bottom of list, exit on inactivity 0.05: Add support for bulk importing and exporting tokens 0.06: Add spaces to codes for improved readability (thanks @BartS23) +0.07: Bangle 2: Improve drag responsiveness and exit on button press diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 73b8bdeea..05d94fc46 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -1,6 +1,10 @@ -const tokenextraheight = 16; -var tokendigitsheight = 30; -var tokenheight = tokendigitsheight + tokenextraheight; +const COUNTER_TRIANGLE_SIZE = 10; +const TOKEN_EXTRA_HEIGHT = 16; +var TOKEN_DIGITS_HEIGHT = 30; +var TOKEN_HEIGHT = TOKEN_DIGITS_HEIGHT + TOKEN_EXTRA_HEIGHT; +const PROGRESSBAR_HEIGHT = 3; +const IDLE_REPEATS = 1; // when idle, the number of extra timed periods to show before hiding +const SETTINGS = "authentiwatch.json"; // Hash functions const crypto = require("crypto"); const algos = { @@ -8,33 +12,24 @@ const algos = { "SHA256":{sha:crypto.SHA256,retsz:32,blksz:64 }, "SHA1" :{sha:crypto.SHA1 ,retsz:20,blksz:64 }, }; -const calculating = "Calculating"; -const notokens = "No tokens"; -const notsupported = "Not supported"; +const CALCULATING = /*LANG*/"Calculating"; +const NO_TOKENS = /*LANG*/"No tokens"; +const NOT_SUPPORTED = /*LANG*/"Not supported"; // sample settings: // {tokens:[{"algorithm":"SHA1","digits":6,"period":30,"issuer":"","account":"","secret":"Bbb","label":"Aaa"}],misc:{}} -var settings = require("Storage").readJSON("authentiwatch.json", true) || {tokens:[],misc:{}}; +var settings = require("Storage").readJSON(SETTINGS, true) || {tokens:[], misc:{}}; if (settings.data ) tokens = settings.data ; /* v0.02 settings */ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ -// QR Code Text -// -// Example: -// -// otpauth://totp/${url}:AA_${algorithm}_${digits}dig_${period}s@${url}?algorithm=${algorithm}&digits=${digits}&issuer=${url}&period=${period}&secret=${secret} -// -// ${algorithm} : one of SHA1 / SHA256 / SHA512 -// ${digits} : one of 6 / 8 -// ${period} : one of 30 / 60 -// ${url} : a domain name "example.com" -// ${secret} : the seed code - function b32decode(seedstr) { - // RFC4648 - var i, buf = 0, bitcount = 0, retstr = ""; - for (i in seedstr) { - var c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(seedstr.charAt(i).toUpperCase(), 0); + // RFC4648 Base16/32/64 Data Encodings + let buf = 0, bitcount = 0, retstr = ""; + for (let c of seedstr.toUpperCase()) { + if (c == '0') c = 'O'; + if (c == '1') c = 'I'; + if (c == '8') c = 'B'; + c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(c); if (c != -1) { buf <<= 5; buf |= c; @@ -46,195 +41,127 @@ function b32decode(seedstr) { } } } - var retbuf = new Uint8Array(retstr.length); - for (i in retstr) { + let retbuf = new Uint8Array(retstr.length); + for (let i in retstr) { retbuf[i] = retstr.charCodeAt(i); } return retbuf; } -function do_hmac(key, message, algo) { - var a = algos[algo]; - // RFC2104 + +function hmac(key, message, algo) { + let a = algos[algo.toUpperCase()]; + // RFC2104 HMAC if (key.length > a.blksz) { key = a.sha(key); } - var istr = new Uint8Array(a.blksz + message.length); - var ostr = new Uint8Array(a.blksz + a.retsz); - for (var i = 0; i < a.blksz; ++i) { - var c = (i < key.length) ? key[i] : 0; + let istr = new Uint8Array(a.blksz + message.length); + let ostr = new Uint8Array(a.blksz + a.retsz); + for (let i = 0; i < a.blksz; ++i) { + let c = (i < key.length) ? key[i] : 0; istr[i] = c ^ 0x36; ostr[i] = c ^ 0x5C; } istr.set(message, a.blksz); ostr.set(a.sha(istr), a.blksz); - var ret = a.sha(ostr); - // RFC4226 dynamic truncation - var v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); + let ret = a.sha(ostr); + // RFC4226 HOTP (dynamic truncation) + let v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); return v.getUint32(0) & 0x7FFFFFFF; } -function hotp(d, token, dohmac) { - var tick; + +function formatOtp(otp, digits) { + // add 0 padding + let ret = "" + otp % Math.pow(10, digits); + while (ret.length < digits) { + ret = "0" + ret; + } + // add a space after every 3rd or 4th digit + let re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; + return ret.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); +} + +function hotp(token) { + let d = Date.now(); + let tick, next; if (token.period > 0) { // RFC6238 - timed - var seconds = Math.floor(d.getTime() / 1000); - tick = Math.floor(seconds / token.period); + tick = Math.floor(Math.floor(d / 1000) / token.period); + next = (tick + 1) * token.period * 1000; } else { // RFC4226 - counter tick = -token.period; + next = d + 30000; } - var msg = new Uint8Array(8); - var v = new DataView(msg.buffer); + let msg = new Uint8Array(8); + let v = new DataView(msg.buffer); v.setUint32(0, tick >> 16 >> 16); v.setUint32(4, tick & 0xFFFFFFFF); - var ret = calculating; - if (dohmac) { - try { - var hash = do_hmac(b32decode(token.secret), msg, token.algorithm.toUpperCase()); - ret = "" + hash % Math.pow(10, token.digits); - while (ret.length < token.digits) { - ret = "0" + ret; - } - // add a space after every 3rd or 4th digit - var re = (token.digits % 3 == 0 || (token.digits % 3 >= token.digits % 4 && token.digits % 4 != 0)) ? "" : "."; - ret = ret.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); - } catch(err) { - ret = notsupported; - } + let ret; + try { + ret = hmac(b32decode(token.secret), msg, token.algorithm); + ret = formatOtp(ret, token.digits); + } catch(err) { + ret = NOT_SUPPORTED; } - return {hotp:ret, next:((token.period > 0) ? ((tick + 1) * token.period * 1000) : d.getTime() + 30000)}; + return {hotp:ret, next:next}; } +// Tokens are displayed in three states: +// 1. Unselected (state.id<0) +// 2. Selected, inactive (no code) (state.id>=0,state.hotp.hotp=="") +// 3. Selected, active (code showing) (state.id>=0,state.hotp.hotp!="") +var fontszCache = {}; var state = { - listy: 0, - prevcur:0, - curtoken:-1, - nextTime:0, - otp:"", - rem:0, - hide:0 + listy:0, // list scroll position + id:-1, // current token ID + hotp:{hotp:"",next:0} }; -function drawToken(id, r) { - var x1 = r.x; - var y1 = r.y; - var x2 = r.x + r.w - 1; - var y2 = r.y + r.h - 1; - var adj, lbl, sz; - g.setClipRect(Math.max(x1, Bangle.appRect.x ), Math.max(y1, Bangle.appRect.y ), - Math.min(x2, Bangle.appRect.x2), Math.min(y2, Bangle.appRect.y2)); - lbl = tokens[id].label.substr(0, 10); - if (id == state.curtoken) { - // current token - g.setColor(g.theme.fgH) - .setBgColor(g.theme.bgH) - .setFont("Vector", tokenextraheight) - // center just below top line - .setFontAlign(0, -1, 0); - adj = y1; - } else { - g.setColor(g.theme.fg) - .setBgColor(g.theme.bg); - sz = tokendigitsheight; +function sizeFont(id, txt, w) { + let sz = fontszCache[id]; + if (!sz) { + sz = TOKEN_DIGITS_HEIGHT; do { g.setFont("Vector", sz--); - } while (g.stringWidth(lbl) > r.w); - // center in box - g.setFontAlign(0, 0, 0); - adj = (y1 + y2) / 2; + } while (g.stringWidth(txt) > w); + fontszCache[id] = ++sz; } - g.clearRect(x1, y1, x2, y2) - .drawString(lbl, (x1 + x2) / 2, adj, false); - if (id == state.curtoken) { - if (tokens[id].period > 0) { - // timed - draw progress bar - let xr = Math.floor(Bangle.appRect.w * state.rem / tokens[id].period); - g.fillRect(x1, y2 - 4, xr, y2 - 1); - adj = 0; - } else { - // counter - draw triangle as swipe hint - let yc = (y1 + y2) / 2; - g.fillPoly([0, yc, 10, yc - 10, 10, yc + 10, 0, yc]); - adj = 12; - } - // digits just below label - sz = tokendigitsheight; - do { - g.setFont("Vector", sz--); - } while (g.stringWidth(state.otp) > (r.w - adj)); - g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + tokenextraheight, false); - } - // shaded lines top and bottom - g.setColor(0.5, 0.5, 0.5) - .drawLine(x1, y1, x2, y1) - .drawLine(x1, y2, x2, y2) - .setClipRect(0, 0, g.getWidth(), g.getHeight()); + g.setFont("Vector", sz); } -function draw() { - var timerfn = exitApp; - var timerdly = 10000; - var d = new Date(); - if (state.curtoken != -1) { - var t = tokens[state.curtoken]; - if (state.otp == calculating) { - state.otp = hotp(d, t, true).hotp; - } - if (d.getTime() > state.nextTime) { - if (state.hide == 0) { - // auto-hide the current token - if (state.curtoken != -1) { - state.prevcur = state.curtoken; - state.curtoken = -1; +tokenY = id => id * TOKEN_HEIGHT + AR.y - state.listy; +half = n => Math.floor(n / 2); + +function timerCalc() { + let timerfn = exitApp; + let timerdly = 10000; + if (state.id >= 0 && state.hotp.hotp != "") { + if (tokens[state.id].period > 0) { + // timed HOTP + if (state.hotp.next < Date.now()) { + if (state.cnt > 0) { + state.cnt--; + state.hotp = hotp(tokens[state.id]); + } else { + state.hotp.hotp = ""; } - state.nextTime = 0; + timerdly = 1; + timerfn = updateCurrentToken; } else { - // time to generate a new token - var r = hotp(d, t, state.otp != ""); - state.nextTime = r.next; - state.otp = r.hotp; - if (t.period <= 0) { - state.hide = 1; - } - state.hide--; - } - } - state.rem = Math.max(0, Math.floor((state.nextTime - d.getTime()) / 1000)); - } - if (tokens.length > 0) { - var drewcur = false; - var id = Math.floor(state.listy / tokenheight); - var y = id * tokenheight + Bangle.appRect.y - state.listy; - while (id < tokens.length && y < Bangle.appRect.y2) { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); - if (id == state.curtoken && (tokens[id].period <= 0 || state.nextTime != 0)) { - drewcur = true; - } - id += 1; - y += tokenheight; - } - if (drewcur) { - // the current token has been drawn - schedule a redraw - if (tokens[state.curtoken].period > 0) { - timerdly = (state.otp == calculating) ? 1 : 1000; // timed - } else { - timerdly = state.nexttime - d.getTime(); // counter - } - timerfn = draw; - if (tokens[state.curtoken].period <= 0) { - state.hide = 0; + timerdly = 1000; + timerfn = updateProgressBar; } } else { - // de-select the current token if it is scrolled out of view - if (state.curtoken != -1) { - state.prevcur = state.curtoken; - state.curtoken = -1; + // counter HOTP + if (state.cnt > 0) { + state.cnt--; + timerdly = 30000; + } else { + state.hotp.hotp = ""; + timerdly = 1; } - state.nexttime = 0; + timerfn = updateCurrentToken; } - } else { - g.setFont("Vector", tokendigitsheight) - .setFontAlign(0, 0, 0) - .drawString(notokens, Bangle.appRect.x + Bangle.appRect.w / 2, Bangle.appRect.y + Bangle.appRect.h / 2, false); } if (state.drawtimer) { clearTimeout(state.drawtimer); @@ -242,97 +169,236 @@ function draw() { state.drawtimer = setTimeout(timerfn, timerdly); } -function onTouch(zone, e) { - if (e) { - var id = Math.floor((state.listy + (e.y - Bangle.appRect.y)) / tokenheight); - if (id == state.curtoken || tokens.length == 0 || id >= tokens.length) { - id = -1; - } - if (state.curtoken != id) { - if (id != -1) { - var y = id * tokenheight - state.listy; - if (y < 0) { - state.listy += y; - y = 0; +function updateCurrentToken() { + drawToken(state.id); + timerCalc(); +} + +function updateProgressBar() { + drawProgressBar(); + timerCalc(); +} + +function drawProgressBar() { + let id = state.id; + if (id >= 0 && tokens[id].period > 0) { + let rem = Math.min(tokens[id].period, Math.floor((state.hotp.next - Date.now()) / 1000)); + if (rem >= 0) { + let y1 = tokenY(id); + let y2 = y1 + TOKEN_HEIGHT - 1; + if (y2 >= AR.y && y1 <= AR.y2) { + // token visible + y1 = y2 - PROGRESSBAR_HEIGHT; + if (y1 <= AR.y2) + { + // progress bar visible + y2 = Math.min(y2, AR.y2); + let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; + g.setColor(g.theme.fgH) + .setBgColor(g.theme.bgH) + .fillRect(AR.x, y1, xr, y2) + .clearRect(xr + 1, y1, AR.x2, y2); } - y += tokenheight; - if (y > Bangle.appRect.h) { - state.listy += (y - Bangle.appRect.h); - } - state.otp = ""; + } else { + // token not visible + state.id = -1; } - state.nextTime = 0; - state.curtoken = id; - state.hide = 2; } } - draw(); +} + +// id = token ID number (0...) +function drawToken(id) { + let x1 = AR.x; + let y1 = tokenY(id); + let x2 = AR.x2; + let y2 = y1 + TOKEN_HEIGHT - 1; + let lbl = (id >= 0 && id < tokens.length) ? tokens[id].label.substr(0, 10) : ""; + let adj; + g.setClipRect(x1, Math.max(y1, AR.y), x2, Math.min(y2, AR.y2)); + if (id === state.id) { + g.setColor(g.theme.fgH) + .setBgColor(g.theme.bgH); + } else { + g.setColor(g.theme.fg) + .setBgColor(g.theme.bg); + } + if (id == state.id && state.hotp.hotp != "") { + // small label centered just below top line + g.setFont("Vector", TOKEN_EXTRA_HEIGHT) + .setFontAlign(0, -1, 0); + adj = y1; + } else { + // large label centered in box + sizeFont("l" + id, lbl, AR.w); + g.setFontAlign(0, 0, 0); + adj = half(y1 + y2); + } + g.clearRect(x1, y1, x2, y2) + .drawString(lbl, half(x1 + x2), adj, false); + if (id == state.id && state.hotp.hotp != "") { + adj = 0; + if (tokens[id].period <= 0) { + // counter - draw triangle as swipe hint + let yc = half(y1 + y2); + adj = COUNTER_TRIANGLE_SIZE; + g.fillPoly([AR.x, yc, AR.x + adj, yc - adj, AR.x + adj, yc + adj]); + adj += 2; + } + // digits just below label + x1 = half(x1 + adj + x2); + y1 += TOKEN_EXTRA_HEIGHT; + if (state.hotp.hotp == CALCULATING) { + sizeFont("c", CALCULATING, AR.w - adj); + g.drawString(CALCULATING, x1, y1, false) + .flip(); + state.hotp = hotp(tokens[id]); + g.clearRect(AR.x + adj, y1, AR.x2, y2); + } + sizeFont("d" + id, state.hotp.hotp, AR.w - adj); + g.drawString(state.hotp.hotp, x1, y1, false); + if (tokens[id].period > 0) { + drawProgressBar(); + } + } + g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); +} + +function changeId(id) { + if (id != state.id) { + state.hotp.hotp = CALCULATING; + let pid = state.id; + state.id = id; + if (pid >= 0) { + drawToken(pid); + } + if (id >= 0) { + drawToken( id); + } + } } function onDrag(e) { - if (e.x > g.getWidth() || e.y > g.getHeight()) return; - if (e.dx == 0 && e.dy == 0) return; - var newy = Math.min(state.listy - e.dy, tokens.length * tokenheight - Bangle.appRect.h); - state.listy = Math.max(0, newy); - draw(); + state.cnt = IDLE_REPEATS; + if (e.b != 0 && e.dy != 0) { + let y = E.clip(state.listy - E.clip(e.dy, -AR.h, AR.h), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); + if (state.listy != y) { + let id, dy = state.listy - y; + state.listy = y; + g.setClipRect(AR.x, AR.y, AR.x2, AR.y2) + .scroll(0, dy); + if (dy > 0) { + id = Math.floor((state.listy + dy) / TOKEN_HEIGHT); + y = tokenY(id + 1); + do { + drawToken(id); + id--; + y -= TOKEN_HEIGHT; + } while (y > AR.y); + } + if (dy < 0) { + id = Math.floor((state.listy + dy + AR.h) / TOKEN_HEIGHT); + y = tokenY(id); + while (y < AR.y2) { + drawToken(id); + id++; + y += TOKEN_HEIGHT; + } + } + } + } + if (e.b == 0) { + timerCalc(); + } +} + +function onTouch(zone, e) { + state.cnt = IDLE_REPEATS; + if (e) { + let id = Math.floor((state.listy + e.y - AR.y) / TOKEN_HEIGHT); + if (id == state.id || tokens.length == 0 || id >= tokens.length) { + id = -1; + } + if (state.id != id) { + if (id >= 0) { + // scroll token into view if necessary + let dy = 0; + let y = id * TOKEN_HEIGHT - state.listy; + if (y < 0) { + dy -= y; + y = 0; + } + y += TOKEN_HEIGHT; + if (y > AR.h) { + dy -= (y - AR.h); + } + onDrag({b:1, dy:dy}); + } + changeId(id); + } + } + timerCalc(); } function onSwipe(e) { - if (e == 1) { + state.cnt = IDLE_REPEATS; + switch (e) { + case 1: exitApp(); + break; + case -1: + if (state.id >= 0 && tokens[state.id].period <= 0) { + tokens[state.id].period--; + require("Storage").writeJSON(SETTINGS, {tokens:tokens, misc:settings.misc}); + state.hotp.hotp = CALCULATING; + drawToken(state.id); + } + break; } - if (e == -1 && state.curtoken != -1 && tokens[state.curtoken].period <= 0) { - tokens[state.curtoken].period--; - let newsettings={tokens:tokens,misc:settings.misc}; - require("Storage").writeJSON("authentiwatch.json", newsettings); - state.nextTime = 0; - state.otp = ""; - state.hide = 2; - } - draw(); + timerCalc(); } -function bangle1Btn(e) { +function bangleBtn(e) { + state.cnt = IDLE_REPEATS; if (tokens.length > 0) { - if (state.curtoken == -1) { - state.curtoken = state.prevcur; - } else { - switch (e) { - case -1: state.curtoken--; break; - case 1: state.curtoken++; break; - } - } - state.curtoken = Math.max(state.curtoken, 0); - state.curtoken = Math.min(state.curtoken, tokens.length - 1); - state.listy = state.curtoken * tokenheight; - state.listy -= (Bangle.appRect.h - tokenheight) / 2; - state.listy = Math.min(state.listy, tokens.length * tokenheight - Bangle.appRect.h); - state.listy = Math.max(state.listy, 0); - var fakee = {}; - fakee.y = state.curtoken * tokenheight - state.listy + Bangle.appRect.y; - state.curtoken = -1; - state.nextTime = 0; - onTouch(0, fakee); - } else { - draw(); // resets idle timer + let id = E.clip(state.id + e, 0, tokens.length - 1); + onDrag({b:1, dy:state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h))}); + changeId(id); + drawProgressBar(); } + timerCalc(); } function exitApp() { + if (state.drawtimer) { + clearTimeout(state.drawtimer); + } Bangle.showLauncher(); } Bangle.on('touch', onTouch); Bangle.on('drag' , onDrag ); Bangle.on('swipe', onSwipe); -if (typeof BTN2 == 'number') { - setWatch(function(){bangle1Btn(-1);}, BTN1, {edge:"rising" , debounce:50, repeat:true}); - setWatch(function(){exitApp(); }, BTN2, {edge:"falling", debounce:50}); - setWatch(function(){bangle1Btn( 1);}, BTN3, {edge:"rising" , debounce:50, repeat:true}); +if (typeof BTN1 == 'number') { + if (typeof BTN2 == 'number' && typeof BTN3 == 'number') { + setWatch(()=>bangleBtn(-1), BTN1, {edge:"rising" , debounce:50, repeat:true}); + setWatch(()=>exitApp() , BTN2, {edge:"falling", debounce:50}); + setWatch(()=>bangleBtn( 1), BTN3, {edge:"rising" , debounce:50, repeat:true}); + } else { + setWatch(()=>exitApp() , BTN1, {edge:"falling", debounce:50}); + } } Bangle.loadWidgets(); - -// Clear the screen once, at startup +const AR = Bangle.appRect; +// draw the initial display g.clear(); -draw(); +if (tokens.length > 0) { + state.listy = AR.h; + onDrag({b:1, dy:AR.h}); +} else { + g.setFont("Vector", TOKEN_DIGITS_HEIGHT) + .setFontAlign(0, 0, 0) + .drawString(NO_TOKENS, AR.x + half(AR.w), AR.y + half(AR.h), false); +} +timerCalc(); Bangle.drawWidgets(); diff --git a/apps/authentiwatch/interface.html b/apps/authentiwatch/interface.html index 5ee32fd8e..d7cd59f1a 100644 --- a/apps/authentiwatch/interface.html +++ b/apps/authentiwatch/interface.html @@ -12,8 +12,8 @@ body.select div.select,body.export div.export{display:block} body.select div.export,body.export div.select{display:none} body.select div#tokens,body.editing div#edit,body.scanning div#scan,body.showqr div#showqr,body.export div#tokens{display:block} #tokens th,#tokens td{padding:5px} -#tokens tr:nth-child(odd){background-color:#ccc} -#tokens tr:nth-child(even){background-color:#eee} +#tokens tr:nth-child(odd){background-color:#f1f1fc} +#tokens tr:nth-child(even){background-color:#fff} #qr-canvas{margin:auto;width:calc(100%-20px);max-width:400px} #advbtn,#scan,#tokenqr table{text-align:center} #edittoken tbody#adv{display:none} @@ -54,9 +54,9 @@ var tokens = settings.tokens; */ function base32clean(val, nows) { var ret = val.replaceAll(/\s+/g, ' '); - ret = ret.replaceAll(/0/g, 'O'); - ret = ret.replaceAll(/1/g, 'I'); - ret = ret.replaceAll(/8/g, 'B'); + ret = ret.replaceAll('0', 'O'); + ret = ret.replaceAll('1', 'I'); + ret = ret.replaceAll('8', 'B'); ret = ret.replaceAll(/[^A-Za-z2-7 ]/g, ''); if (nows) { ret = ret.replaceAll(/\s+/g, ''); @@ -81,9 +81,9 @@ function b32encode(str) { function b32decode(seedstr) { // RFC4648 - var i, buf = 0, bitcount = 0, ret = ''; - for (i in seedstr) { - var c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(seedstr.charAt(i).toUpperCase(), 0); + var buf = 0, bitcount = 0, ret = ''; + for (var c of seedstr.toUpperCase()) { + c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(c); if (c != -1) { buf <<= 5; buf |= c; @@ -226,15 +226,18 @@ function editToken(id) { markup += selectMarkup('algorithm', otpAlgos, tokens[id].algorithm); markup += ''; markup += ''; - markup += ''; + markup += ''; markup += ''; - markup += ''; - markup += ''; + markup += ''; + markup += ' '; + markup += ''; + markup += ' '; if (tokens[id].isnew) { - markup += ''; + markup += ''; } else { - markup += ''; - markup += ''; + markup += ''; + markup += ' '; + markup += ''; } document.getElementById('edit').innerHTML = markup; document.body.className = 'editing'; @@ -304,9 +307,23 @@ function updateTokens() { return ''; }; const tokenButton = function(fn, id, label, dir) { - return ''; + return ''; }; - var markup = '
'; + var markup = ''; + markup += '
'; + markup += ''; + markup += ' '; + markup += ''; + markup += ' '; + markup += ''; + markup += ' '; + markup += ''; + markup += '
'; + markup += ''; + markup += ' '; + markup += ''; + markup += '
'; + markup += ''; /* any tokens marked new are cancelled new additions and must be removed */ @@ -331,15 +348,6 @@ function updateTokens() { markup += ''; } markup += '
'; markup += tokenSelect('all'); markup += 'TokenOrder
'; - markup += '
'; - markup += ''; - markup += ''; - markup += ''; - markup += ''; - markup += '
'; - markup += ''; - markup += ''; - markup += '
'; document.getElementById('tokens').innerHTML = markup; document.body.className = 'select'; } @@ -405,7 +413,7 @@ class proto3decoder { constructor(str) { this.buf = []; for (let i in str) { - this.buf = this.buf.concat(str.charCodeAt(i)); + this.buf.push(str.charCodeAt(i)); } } getVarint() { @@ -487,7 +495,7 @@ function startScan(handler,cancel) { document.body.className = 'scanning'; navigator.mediaDevices .getUserMedia({video:{facingMode:'environment'}}) - .then(function(stream){ + .then(stream => { scanning=true; video.setAttribute('playsinline',true); video.srcObject = stream; @@ -604,7 +612,7 @@ function qrBack() {
- +
@@ -613,7 +621,7 @@ function qrBack() {
- +
diff --git a/apps/authentiwatch/metadata.json b/apps/authentiwatch/metadata.json index b4ed34a12..36e1ea34d 100644 --- a/apps/authentiwatch/metadata.json +++ b/apps/authentiwatch/metadata.json @@ -4,7 +4,7 @@ "shortName": "AuthWatch", "icon": "app.png", "screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"}], - "version": "0.06", + "version": "0.07", "description": "Google Authenticator compatible tool.", "tags": "tool", "interface": "interface.html", diff --git a/apps/authentiwatch/screenshot1.png b/apps/authentiwatch/screenshot1.png index c7ca744b4..c3ac0e3b7 100644 Binary files a/apps/authentiwatch/screenshot1.png and b/apps/authentiwatch/screenshot1.png differ diff --git a/apps/authentiwatch/screenshot2.png b/apps/authentiwatch/screenshot2.png index 8156dd3e8..26bafdbb2 100644 Binary files a/apps/authentiwatch/screenshot2.png and b/apps/authentiwatch/screenshot2.png differ diff --git a/apps/authentiwatch/screenshot3.png b/apps/authentiwatch/screenshot3.png index 6d14e0b96..80f2fb172 100644 Binary files a/apps/authentiwatch/screenshot3.png and b/apps/authentiwatch/screenshot3.png differ diff --git a/apps/authentiwatch/screenshot4.png b/apps/authentiwatch/screenshot4.png index 7576e1aff..00756228d 100644 Binary files a/apps/authentiwatch/screenshot4.png and b/apps/authentiwatch/screenshot4.png differ diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index 316660fc6..0b8470b6a 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -7,3 +7,4 @@ 0.07: Update to use Bangle.setUI instead of setWatch 0.08: Use theme colors, Layout library 0.09: Fix time/date disappearing after fullscreen notification +0.10: Use ClockFace library diff --git a/apps/barclock/clock-bar.js b/apps/barclock/clock-bar.js index 5d46a1cb4..a465bb692 100644 --- a/apps/barclock/clock-bar.js +++ b/apps/barclock/clock-bar.js @@ -11,13 +11,9 @@ let locale = require("locale"); date.setMonth(1, 3); // februari: months are zero-indexed const localized = locale.date(date, true); locale.dayFirst = /3.*2/.test(localized); - - locale.hasMeridian = false; - if (typeof locale.meridian==="function") { // function does not exist if languages app is not installed - locale.hasMeridian = (locale.meridian(date)!==""); - } + locale.hasMeridian = (locale.meridian(date)!==""); } -Bangle.loadWidgets(); + function renderBar(l) { if (!this.fraction) { // zero-size fillRect stills draws one line of pixels, we don't want that @@ -27,32 +23,6 @@ function renderBar(l) { g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1); } -const Layout = require("Layout"); -const layout = new Layout({ - type: "v", c: [ - { - type: "h", c: [ - {id: "time", label: "88:88", type: "txt", font: "6x8:5", bgCol: g.theme.bg}, // size updated below - {id: "ampm", label: " ", type: "txt", font: "6x8:2", bgCol: g.theme.bg}, - ], - }, - {id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar}, - {height: 40}, - {id: "date", type: "txt", font: "10%", valign: 1}, - ], -}, {lazy: true}); -// adjustments based on screen size and whether we display am/pm -let thickness; // bar thickness, same as time font "pixel block" size -if (is12Hour) { - // Maximum font size = ( - ) / (5chars * 6px) - thickness = Math.floor((g.getWidth()-24)/(5*6)); -} else { - layout.ampm.label = ""; - thickness = Math.floor(g.getWidth()/(5*6)); -} -layout.bar.height = thickness+1; -layout.time.font = "6x8:"+thickness; -layout.update(); function timeText(date) { if (!is12Hour) { @@ -78,31 +48,48 @@ function dateText(date) { return `${dayName} ${dayMonth}`; } -draw = function draw(force) { - if (!Bangle.isLCDOn()) {return;} // no drawing, also no new update scheduled - const date = new Date(); - layout.time.label = timeText(date); - layout.ampm.label = ampmText(date); - layout.date.label = dateText(date); - const SECONDS_PER_MINUTE = 60; - layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE; - if (force) { - Bangle.drawWidgets(); - layout.forgetLazyState(); - } - layout.render(); - // schedule update at start of next second - const millis = date.getMilliseconds(); - setTimeout(draw, 1000-millis); -}; -// Show launcher when button pressed -Bangle.setUI("clock"); -Bangle.on("lcdPower", function(on) { - if (on) { - draw(true); - } -}); -g.reset().clear(); -Bangle.drawWidgets(); -draw(); +const ClockFace = require("ClockFace"), + clock = new ClockFace({ + precision:1, + init: function() { + const Layout = require("Layout"); + this.layout = new Layout({ + type: "v", c: [ + { + type: "h", c: [ + {id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // size updated below + {id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg}, + ], + }, + {id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar}, + {height: 40}, + {id: "date", type: "txt", font: "10%", valign: 1}, + ], + }, {lazy: true}); + // adjustments based on screen size and whether we display am/pm + let thickness; // bar thickness, same as time font "pixel block" size + if (is12Hour) { + // Maximum font size = ( - ) / (5chars * 6px) + thickness = Math.floor((Bangle.appRect.w-24)/(5*6)); + } else { + this.layout.ampm.label = ""; + thickness = Math.floor(Bangle.appRect.w/(5*6)); + } + this.layout.bar.height = thickness+1; + this.layout.time.font = "6x8:"+thickness; + this.layout.update(); + }, + update: function(date, c) { + if (c.m) this.layout.time.label = timeText(date); + if (c.h) this.layout.ampm.label = ampmText(date); + if (c.d) this.layout.date.label = dateText(date); + const SECONDS_PER_MINUTE = 60; + if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE; + this.layout.render(); + }, + resume: function() { + this.layout.forgetLazyState(); + }, + }); +clock.start(); diff --git a/apps/barclock/metadata.json b/apps/barclock/metadata.json index 2b7be355f..3ee7ccb3a 100644 --- a/apps/barclock/metadata.json +++ b/apps/barclock/metadata.json @@ -1,7 +1,7 @@ { "id": "barclock", "name": "Bar Clock", - "version": "0.09", + "version": "0.10", "description": "A simple digital clock showing seconds as a bar", "icon": "clock-bar.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], diff --git a/apps/bee/ChangeLog b/apps/bee/ChangeLog new file mode 100644 index 000000000..cfb44c8e9 --- /dev/null +++ b/apps/bee/ChangeLog @@ -0,0 +1,3 @@ +0.01: New app! +0.02: Fix bug with regenerating index, fix bug in word lookups +0.03: Improve word search performance diff --git a/apps/bee/README.md b/apps/bee/README.md new file mode 100644 index 000000000..3d0a4c14a --- /dev/null +++ b/apps/bee/README.md @@ -0,0 +1,33 @@ + +# Spelling bee game + +Word finding game inspired by the NYT spelling bee. Find as many words with 4 or more letters (must include the +letter at the center of the 'hive') as you can. + + +## Usage + + - tap on letters to type out word + - swipe left to delete last letter + - swipe right to enter; the word will turn blue while it is being checked against the internal dictionary; once + checked, it will turn red if the word is invalid, does not contain the central letter or has been guessed before or + will turn green if it is a valid word; in the latter case, points will be awarded + - swipe down to shuffle the 6 outer letters + - swipe up to view a list of already guessed words; tap on any of them to return to the regular game. + + +## Scoring + +The number of correctly guessed words is displayed on the bottom left, the score on the bottom right. A single point +is awarded for a 4 letter word, or the number of letters if longer. A pangram is a word that contains all 7 letters at +least once and yields an additional 7 points. Each game contains at least one pangram. + + +## Technical remarks +The game uses an internal dictionary consisting of a newline separated list of English words ('bee.words', using the '2of12inf' word list). +The dictionary is fairly large (~700kB of flash space) and thus requires appropriate space on the watch and will make installing the app somewhat +slow. Because of its size it cannot be compressed (heatshrink needs to hold the compressed/uncompressed data in memory). +This file can be replaced with a custom dictionary, an ASCII file containing a newline-separated (single "\n", not DOS-style "\r\n") alphabetically +sorted (sorting is important for the word lookup algorithm) list of words. + +![Screenshot](./bee_screenshot.png) diff --git a/apps/bee/app-icon.js b/apps/bee/app-icon.js new file mode 100644 index 000000000..f3bf5dbb2 --- /dev/null +++ b/apps/bee/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AE2JAAIKHnc7DyNPp4vRGAwuBGB4sBAAQvSGIovPFqYvHGAYvDGBYsGGhwvGGIQvEGBQnDMYhkNGBAvOvQABqyRTF5GJr4wLFwQACX6IwLsowJLYMrldVGAQvTsoADGBITD0YvDldPF6n+F4gyGGAdP5nMF4KKBGDJZDGI7EBcoOiGAK7DGAQvYRogxEr1Pp9VMAiSBBILBWeJIxCromBMAQwDAAZfTGBQyCxOCGAIvBGIV/F7AwMAAOIp95GAYACFqoyQMAIwGF7QADEQd5FgIADqvGF8DnEAAIvFGIWjF8CFE0QwHAAQudAAK0EGBQuecw3GqpemYIxiCGIa8cF4wwHdTwvJp9/F82jGA9VMQovf5jkHGIwvg4wvIAAgvg5miF9wwNF8QABF9QwF0YuoF4oxCqoulGBAAB42i0QvjGBPMF0gwIFswwHF1IA/AH4A/AH4AL")) diff --git a/apps/bee/app.png b/apps/bee/app.png new file mode 100644 index 000000000..ed16c44b1 Binary files /dev/null and b/apps/bee/app.png differ diff --git a/apps/bee/bee.app.js b/apps/bee/bee.app.js new file mode 100644 index 000000000..878e9763c --- /dev/null +++ b/apps/bee/bee.app.js @@ -0,0 +1,181 @@ + +const S = require("Storage"); +const words = S.read("bee.words"); +var letters = []; + +var centers = []; + +var word = ''; + +var foundWords = []; +var score = 0; + +var intervalID = -1; + +function biSearch(w, ws, start, end, count) { + "compile" + if (start>end-w.legnth || count--<=0) return ws.substr(start, end-start).indexOf("\n"+w+"\n"); + var mid = (end+start)>>1; + if (ws[mid-1]==="\n") --mid; + else while (midws[mid+i+1]) return biSearch(w, ws, mid+1, end, count); +} + +function isPangram(w) { + var ltrs = ''; + for (var i=0; i=0) return false; // already found + if (biSearch(w, words, 0, words.length, 20)>-1) { + foundWords.push(w); + foundWords.sort(); + if (w.length==4) score++; + else score += w.length; + if (isPangram(w)) score += 7; + return true; + } + return false; +} + +function getHexPoly(cx, cy, r, a) { + var p = []; + for (var i=0; i<6; ++i) p.push(cx+r*Math.sin((i+a)/3*Math.PI), cy+r*Math.cos((i+a)/3*Math.PI)); + return p; +} + +function drawHive() { + w = g.getWidth(); + h = g.getHeight(); + const R = w/3.3; + centers = getHexPoly(w/2, h/2+10, R, 0); + centers.push(w/2, h/2+10); + g.clear(); + g.setFont("Vector", w/7).setFontAlign(0, 0, 0); + g.setColor(g.theme.fg); + for (var i=0; i<6; ++i) { + g.drawPoly(getHexPoly(centers[2*i], centers[2*i+1], 0.9*R/Math.sqrt(3), 0.5), {closed:true}); + g.drawString(String.fromCharCode(65+letters[i+1]), centers[2*i]+2, centers[2*i+1]+2); + } + g.setColor(1, 1, 0).fillPoly(getHexPoly(w/2, h/2+10, 0.9*R/Math.sqrt(3), 0.5)); + g.setColor(0).drawString(String.fromCharCode(65+letters[0]), w/2+2, h/2+10+2); +} + +function shuffleLetters(qAll) { + for (var i=letters.length-1; i > 0; i--) { + var j = (1-qAll) + Math.floor(Math.random()*(i+qAll)); + var temp = letters[i]; + letters[i] = letters[j]; + letters[j] = temp; + } +} + +function pickLetters() { + var ltrs = ""; + while (ltrs.length!==7) { + ltrs = []; + var i = Math.floor((words.length-10)*Math.random()); + while (words[i]!="\n" && i0) { + word = word.slice(0, -1); + drawWord(g.theme.fg); + } + if (d==1 && word.length>=4) { + drawWord("#00f"); + drawWord((checkWord(word.toLowerCase()) ? "#0f0" : "#f00")); + if (intervalID===-1) intervalID = setInterval(wordFound, 800); + } + if (e===1) { + shuffleLetters(0); + drawHive(); + drawScore(); + drawWord(g.theme.fg); + } + if (e===-1 && foundWords.length>0) showWordList(); +} + +function showWordList() { + Bangle.removeListener("touch", touchHandler); + Bangle.removeListener("swipe", swipeHandler); + E.showScroller({ + h : 20, c : foundWords.length, + draw : (idx, r) => { + g.clearRect(r.x,r.y,r.x+r.w-1,r.y+r.h-1).setFont("6x8:2"); + g.setColor(isPangram(foundWords[idx])?'#0f0':g.theme.fg).drawString(foundWords[idx].toUpperCase(),r.x+10,r.y+4); + }, + select : (idx) => { + setInterval(()=> { + E.showScroller(); + drawHive(); + drawScore(); + drawWord(g.theme.fg); + Bangle.on("touch", touchHandler); + Bangle.on("swipe", swipeHandler); + clearInterval(); + }, 100); + } + }); +} + +pickLetters(); +drawHive(); +drawScore(); +Bangle.on("touch", touchHandler); +Bangle.on("swipe", swipeHandler); diff --git a/apps/bee/bee_screenshot.png b/apps/bee/bee_screenshot.png new file mode 100644 index 000000000..cd173b997 Binary files /dev/null and b/apps/bee/bee_screenshot.png differ diff --git a/apps/bee/bee_words_2of12 b/apps/bee/bee_words_2of12 new file mode 100644 index 000000000..3bab44251 --- /dev/null +++ b/apps/bee/bee_words_2of12 @@ -0,0 +1,74578 @@ +aardvark +aardvarks +abaci +aback +abacus +abacuses +abaft +abalone +abalones +abandon +abandoned +abandoning +abandonment +abandons +abase +abased +abasement +abases +abash +abashed +abashedly +abashes +abashing +abashment +abasing +abate +abated +abatement +abates +abating +abattoir +abattoirs +abbe +abbes +abbess +abbesses +abbey +abbeys +abbot +abbots +abbreviate +abbreviated +abbreviates +abbreviating +abbreviation +abbreviations +abdicate +abdicated +abdicates +abdicating +abdication +abdications +abdomen +abdomens +abdominal +abdominals +abduct +abducted +abducting +abduction +abductions +abductor +abductors +abducts +abeam +abed +aberrant +aberration +aberrational +aberrations +abet +abets +abetted +abetter +abetters +abetting +abettor +abettors +abeyance +abhor +abhorred +abhorrence +abhorrent +abhorrently +abhorring +abhors +abidance +abide +abided +abides +abiding +abidingly +abilities +ability +abject +abjection +abjectly +abjectness +abjuration +abjurations +abjuratory +abjure +abjured +abjurer +abjurers +abjures +abjuring +ablate +ablated +ablates +ablating +ablation +ablations +ablative +ablatives +ablaze +able +abler +ablest +abloom +ablution +ablutions +ably +abnegate +abnegated +abnegates +abnegating +abnegation +abnormal +abnormalities +abnormality +abnormally +aboard +abode +abodes +abolish +abolished +abolishes +abolishing +abolition +abolitionism +abolitionist +abolitionists +abominable +abominably +abominate +abominated +abominates +abominating +abomination +abominations +aboriginal +aboriginals +aborigine +aborigines +aborning +abort +aborted +aborting +abortion +abortionist +abortionists +abortions +abortive +abortively +aborts +abound +abounded +abounding +abounds +about +above +aboveboard +abracadabra +abrade +abraded +abrades +abrading +abrasion +abrasions +abrasive +abrasively +abrasiveness +abrasives +abreast +abridge +abridged +abridgement +abridgements +abridges +abridging +abridgment +abridgments +abroad +abrogate +abrogated +abrogates +abrogating +abrogation +abrogations +abrogator +abrogators +abrupt +abrupter +abruptest +abruptly +abruptness +abscess +abscessed +abscesses +abscessing +abscissa +abscissae +abscissas +abscission +abscond +absconded +absconder +absconders +absconding +absconds +abseil +abseiled +abseiling +abseils +absence +absences +absent +absented +absentee +absenteeism +absentees +absenting +absently +absentminded +absentmindedly +absentmindedness +absents +absinth +absinthe +absolute +absolutely +absoluteness +absolutes +absolutest +absolution +absolutism +absolutist +absolutists +absolve +absolved +absolves +absolving +absorb +absorbed +absorbency +absorbent +absorbents +absorbing +absorbingly +absorbs +absorption +absorptive +abstain +abstained +abstainer +abstainers +abstaining +abstains +abstemious +abstemiously +abstemiousness +abstention +abstentions +abstinence +abstinent +abstract +abstracted +abstractedly +abstractedness +abstracting +abstraction +abstractions +abstractly +abstractness +abstracts +abstruse +abstrusely +abstruseness +absurd +absurder +absurdest +absurdities +absurdity +absurdly +absurdness +abundance +abundances +abundant +abundantly +abuse +abused +abuser +abusers +abuses +abusing +abusive +abusively +abusiveness +abut +abutment +abutments +abuts +abutted +abutting +abuzz +abysmal +abysmally +abyss +abyssal +abysses +acacia +acacias +academe +academia +academic +academically +academician +academicians +academics +academies +academy +acanthi +acanthus +acanthuses +accede +acceded +accedes +acceding +accelerate +accelerated +accelerates +accelerating +acceleration +accelerator +accelerators +accent +accented +accenting +accents +accentual +accentuate +accentuated +accentuates +accentuating +accentuation +accept +acceptability +acceptable +acceptableness +acceptably +acceptance +acceptances +acceptation +acceptations +accepted +accepting +accepts +access +accessed +accesses +accessibility +accessible +accessibly +accessing +accession +accessions +accessories +accessory +accident +accidental +accidentally +accidentals +accidents +acclaim +acclaimed +acclaiming +acclaims +acclamation +acclimate +acclimated +acclimates +acclimating +acclimation +acclimatization +acclimatize +acclimatized +acclimatizes +acclimatizing +acclivities +acclivity +accolade +accolades +accommodate +accommodated +accommodates +accommodating +accommodatingly +accommodation +accommodations +accompanied +accompanies +accompaniment +accompaniments +accompanist +accompanists +accompany +accompanying +accomplice +accomplices +accomplish +accomplished +accomplishes +accomplishing +accomplishment +accomplishments +accord +accordance +accordant +accorded +according +accordingly +accordion +accordionist +accordionists +accordions +accords +accost +accosted +accosting +accosts +account +accountability +accountable +accountancy +accountant +accountants +accounted +accounting +accounts +accouter +accoutered +accoutering +accouterments +accouters +accoutre +accoutred +accoutrements +accoutres +accoutring +accredit +accreditation +accredited +accrediting +accredits +accretion +accretions +accrual +accruals +accrue +accrued +accrues +accruing +acculturate +acculturated +acculturates +acculturating +acculturation +accumulate +accumulated +accumulates +accumulating +accumulation +accumulations +accumulative +accumulator +accumulators +accuracy +accurate +accurately +accurateness +accursed +accursedness +accurst +accusation +accusations +accusative +accusatives +accusatory +accuse +accused +accuser +accusers +accuses +accusing +accusingly +accustom +accustomed +accustoming +accustoms +aced +acerbate +acerbated +acerbates +acerbating +acerbic +acerbically +acerbity +aces +acetaminophen +acetate +acetates +acetic +acetone +acetonic +acetylene +ache +ached +achene +achenes +aches +achier +achiest +achievable +achieve +achieved +achievement +achievements +achiever +achievers +achieves +achieving +aching +achoo +achoos +achromatic +achy +acid +acidic +acidified +acidifies +acidify +acidifying +acidity +acidly +acidosis +acids +acidulous +acing +acknowledge +acknowledged +acknowledgement +acknowledgements +acknowledges +acknowledging +acknowledgment +acknowledgments +acme +acmes +acne +acolyte +acolytes +aconite +aconites +acorn +acorns +acoustic +acoustical +acoustically +acoustics +acquaint +acquaintance +acquaintances +acquaintanceship +acquainted +acquainting +acquaints +acquiesce +acquiesced +acquiescence +acquiescent +acquiescently +acquiesces +acquiescing +acquirable +acquire +acquired +acquirement +acquires +acquiring +acquisition +acquisitions +acquisitive +acquisitively +acquisitiveness +acquit +acquits +acquittal +acquittals +acquitted +acquitting +acre +acreage +acreages +acres +acrid +acrider +acridest +acridity +acridly +acridness +acrimonious +acrimoniously +acrimoniousness +acrimony +acrobat +acrobatic +acrobatically +acrobatics +acrobats +acronym +acronyms +acrophobia +acropolis +acropolises +across +acrostic +acrostics +acrylic +acrylics +acted +acting +actinium +action +actionable +actions +activate +activated +activates +activating +activation +activator +activators +active +actively +activeness +actives +activism +activist +activists +activities +activity +actor +actors +actress +actresses +acts +actual +actualities +actuality +actualization +actualize +actualized +actualizes +actualizing +actually +actuarial +actuaries +actuary +actuate +actuated +actuates +actuating +actuation +actuator +actuators +acuity +acumen +acupressure +acupuncture +acupuncturist +acupuncturists +acute +acutely +acuteness +acuter +acutes +acutest +acyclovir +adage +adages +adagio +adagios +adamant +adamantly +adapt +adaptability +adaptable +adaptation +adaptations +adapted +adapter +adapters +adapting +adaptive +adaptor +adaptors +adapts +addable +added +addend +addenda +addends +addendum +adder +adders +addible +addict +addicted +addicting +addiction +addictions +addictive +addicts +adding +addition +additional +additionally +additions +additive +additives +addle +addled +addles +addling +address +addressed +addressee +addressees +addresses +addressing +adds +adduce +adduced +adduces +adducing +adenine +adenoid +adenoidal +adenoids +adept +adeptly +adeptness +adepts +adequacy +adequate +adequately +adequateness +adhere +adhered +adherence +adherent +adherents +adheres +adhering +adhesion +adhesive +adhesiveness +adhesives +adieu +adieus +adieux +adios +adipose +adjacency +adjacent +adjacently +adjectival +adjectivally +adjective +adjectives +adjoin +adjoined +adjoining +adjoins +adjourn +adjourned +adjourning +adjournment +adjournments +adjourns +adjudge +adjudged +adjudges +adjudging +adjudicate +adjudicated +adjudicates +adjudicating +adjudication +adjudicative +adjudicator +adjudicators +adjudicatory +adjunct +adjuncts +adjuration +adjurations +adjure +adjured +adjures +adjuring +adjust +adjustable +adjusted +adjuster +adjusters +adjusting +adjustment +adjustments +adjustor +adjustors +adjusts +adjutant +adjutants +adman +admen +administer +administered +administering +administers +administrate +administrated +administrates +administrating +administration +administrations +administrative +administratively +administrator +administrators +admirable +admirably +admiral +admirals +admiralty +admiration +admire +admired +admirer +admirers +admires +admiring +admiringly +admissibility +admissible +admissibly +admission +admissions +admit +admits +admittance +admitted +admittedly +admitting +admix +admixed +admixes +admixing +admixture +admixtures +admonish +admonished +admonishes +admonishing +admonishment +admonishments +admonition +admonitions +admonitory +adobe +adobes +adolescence +adolescences +adolescent +adolescents +adopt +adoptable +adopted +adopter +adopters +adopting +adoption +adoptions +adoptive +adopts +adorable +adorableness +adorably +adoration +adore +adored +adorer +adorers +adores +adoring +adoringly +adorn +adorned +adorning +adornment +adornments +adorns +adrenal +adrenalin +adrenaline +adrenals +adrift +adroit +adroitly +adroitness +adsorb +adsorbed +adsorbent +adsorbents +adsorbing +adsorbs +adsorption +adsorptions +adulate +adulated +adulates +adulating +adulation +adulator +adulators +adulatory +adult +adulterant +adulterants +adulterate +adulterated +adulterates +adulterating +adulteration +adulterer +adulterers +adulteress +adulteresses +adulteries +adulterous +adultery +adulthood +adults +adumbrate +adumbrated +adumbrates +adumbrating +adumbration +advance +advanced +advancement +advancements +advances +advancing +advantage +advantaged +advantageous +advantageously +advantages +advantaging +advent +adventitious +adventitiously +advents +adventure +adventured +adventurer +adventurers +adventures +adventuresome +adventuress +adventuresses +adventuring +adventurous +adventurously +adventurousness +adverb +adverbial +adverbially +adverbials +adverbs +adversarial +adversaries +adversary +adverse +adversely +adverseness +adverser +adversest +adversities +adversity +advert +adverted +adverting +advertise +advertised +advertisement +advertisements +advertiser +advertisers +advertises +advertising +advertize +advertized +advertizement +advertizements +advertizes +advertizing +advertorial +advertorials +adverts +advice +advisability +advisable +advisably +advise +advised +advisedly +advisement +adviser +advisers +advises +advising +advisor +advisories +advisors +advisory +advocacy +advocate +advocated +advocates +advocating +adze +adzes +aegis +aeon +aeons +aerate +aerated +aerates +aerating +aeration +aerator +aerators +aerial +aerialist +aerialists +aerially +aerials +aerie +aeries +aerobatic +aerobatics +aerobic +aerobically +aerobics +aerodrome +aerodromes +aerodynamic +aerodynamically +aerodynamics +aeronautic +aeronautical +aeronautics +aeroplane +aeroplanes +aerosol +aerosols +aerospace +aery +aesthete +aesthetes +aesthetic +aesthetically +aestheticism +aesthetics +afar +affability +affable +affably +affair +affairs +affect +affectation +affectations +affected +affectedly +affecting +affectingly +affection +affectionate +affectionately +affections +affects +afferent +affiance +affianced +affiances +affiancing +affidavit +affidavits +affiliate +affiliated +affiliates +affiliating +affiliation +affiliations +affinities +affinity +affirm +affirmation +affirmations +affirmative +affirmatively +affirmatives +affirmed +affirming +affirms +affix +affixed +affixes +affixing +afflatus +afflict +afflicted +afflicting +affliction +afflictions +afflicts +affluence +affluent +affluently +afford +affordable +afforded +affording +affords +afforest +afforestation +afforested +afforesting +afforests +affray +affrays +affront +affronted +affronting +affronts +afghan +afghans +aficionado +aficionados +afield +afire +aflame +afloat +aflutter +afoot +aforementioned +aforesaid +aforethought +afoul +afraid +afresh +after +afterbirth +afterbirths +afterburner +afterburners +aftercare +aftereffect +aftereffects +afterglow +afterglows +afterimage +afterimages +afterlife +afterlives +aftermarket +aftermarkets +aftermath +aftermaths +afternoon +afternoons +aftershave +aftershaves +aftershock +aftershocks +aftertaste +aftertastes +afterthought +afterthoughts +afterward +afterwards +afterword +afterwords +again +against +agape +agar +agate +agates +agave +aged +ageing +ageism +ageist +ageists +ageless +agelessly +agelessness +agencies +agency +agenda +agendas +agent +agents +ageratum +ageratums +ages +agglomerate +agglomerated +agglomerates +agglomerating +agglomeration +agglomerations +agglutinate +agglutinated +agglutinates +agglutinating +agglutination +agglutinations +aggrandize +aggrandized +aggrandizement +aggrandizes +aggrandizing +aggravate +aggravated +aggravates +aggravating +aggravatingly +aggravation +aggravations +aggregate +aggregated +aggregates +aggregating +aggregation +aggregations +aggression +aggressive +aggressively +aggressiveness +aggressor +aggressors +aggrieve +aggrieved +aggrieves +aggrieving +aghast +agile +agilely +agility +aging +agitate +agitated +agitates +agitating +agitation +agitations +agitator +agitators +agitprop +agleam +aglitter +aglow +agnostic +agnosticism +agnostics +agog +agonies +agonize +agonized +agonizes +agonizing +agonizingly +agony +agoraphobia +agoraphobic +agoraphobics +agrarian +agrarianism +agrarians +agree +agreeable +agreeableness +agreeably +agreed +agreeing +agreement +agreements +agrees +agribusiness +agribusinesses +agricultural +agriculturalist +agriculturalists +agriculturally +agriculture +agriculturist +agriculturists +agronomic +agronomics +agronomist +agronomists +agronomy +aground +ague +ahead +ahem +ahoy +aide +aided +aides +aiding +aids +aigrette +aigrettes +ailed +aileron +ailerons +ailing +ailment +ailments +ails +aimed +aiming +aimless +aimlessly +aimlessness +aims +airbag +airbags +airbase +airbases +airborne +airbrush +airbrushed +airbrushes +airbrushing +airbus +airbuses +airbusses +aircraft +airdrop +airdropped +airdropping +airdrops +aired +airfare +airfares +airfield +airfields +airflow +airfoil +airfoils +airfreight +airhead +airheads +airier +airiest +airily +airiness +airing +airings +airless +airlessness +airlift +airlifted +airlifting +airlifts +airline +airliner +airliners +airlines +airlock +airlocks +airmail +airmailed +airmailing +airmails +airman +airmen +airplane +airplanes +airplay +airport +airports +airs +airship +airships +airsick +airsickness +airspace +airstrike +airstrikes +airstrip +airstrips +airtight +airtime +airwaves +airway +airways +airworthier +airworthiest +airworthiness +airworthy +airy +aisle +aisles +aitch +aitches +ajar +akimbo +akin +alabaster +alack +alacrity +alarm +alarmed +alarming +alarmingly +alarmist +alarmists +alarms +alas +albacore +albacores +albatross +albatrosses +albeit +albinism +albino +albinos +albs +album +albumen +albumin +albuminous +albums +alchemist +alchemists +alchemy +alcohol +alcoholic +alcoholically +alcoholics +alcoholism +alcohols +alcove +alcoves +alder +alderman +aldermen +alders +alderwoman +alderwomen +aleatory +alehouse +alehouses +alembic +alembics +alert +alerted +alerting +alertly +alertness +alerts +ales +alewife +alewives +alfalfa +alfresco +alga +algae +algal +algebra +algebraic +algebraically +algorithm +algorithmic +algorithms +alias +aliased +aliases +aliasing +alibi +alibied +alibiing +alibis +alien +alienable +alienate +alienated +alienates +alienating +alienation +alienist +alienists +aliens +alight +alighted +alighting +alights +align +aligned +aligner +aligners +aligning +alignment +alignments +aligns +alike +aliment +alimentary +alimented +alimenting +aliments +alimony +aline +alined +alinement +alinements +alines +alining +alit +alive +aliveness +aliyah +aliyahs +alkali +alkalies +alkaline +alkalinity +alkalis +alkalize +alkalized +alkalizes +alkalizing +alkaloid +alkaloids +alkyd +alkyds +allay +allayed +allaying +allays +allegation +allegations +allege +alleged +allegedly +alleges +allegiance +allegiances +alleging +allegoric +allegorical +allegorically +allegories +allegorist +allegorists +allegory +allegretto +allegrettos +allegro +allegros +allele +alleles +alleluia +alleluias +allergen +allergenic +allergens +allergic +allergically +allergies +allergist +allergists +allergy +alleviate +alleviated +alleviates +alleviating +alleviation +alley +alleys +alleyway +alleyways +alliance +alliances +allied +allies +alligator +alligators +alliterate +alliterated +alliterates +alliterating +alliteration +alliterations +alliterative +alliteratively +allocate +allocated +allocates +allocating +allocation +allocations +allot +allotment +allotments +allots +allotted +allotting +allover +allow +allowable +allowably +allowance +allowances +allowed +allowing +allows +alloy +alloyed +alloying +alloys +allspice +allude +alluded +alludes +alluding +allure +allured +allurement +allurements +allures +alluring +alluringly +allusion +allusions +allusive +allusively +allusiveness +alluvia +alluvial +alluvium +alluviums +ally +allying +almanac +almanacs +almighty +almond +almonds +almoner +almoners +almost +alms +almshouse +almshouses +aloe +aloes +aloft +aloha +alohas +alone +along +alongshore +alongside +aloof +aloofly +aloofness +aloud +alpaca +alpacas +alpha +alphabet +alphabetic +alphabetical +alphabetically +alphabetization +alphabetizations +alphabetize +alphabetized +alphabetizer +alphabetizers +alphabetizes +alphabetizing +alphabets +alphanumeric +alphanumerical +alphanumerically +alphas +alpine +alps +already +alright +also +altar +altarpiece +altarpieces +altars +alter +alterable +alteration +alterations +altercation +altercations +altered +altering +alternate +alternated +alternately +alternates +alternating +alternation +alternations +alternative +alternatively +alternatives +alternator +alternators +alters +altho +although +altimeter +altimeters +altitude +altitudes +alto +altogether +altos +altruism +altruist +altruistic +altruistically +altruists +alum +alumina +aluminium +aluminum +alumna +alumnae +alumni +alumnus +alums +always +amalgam +amalgamate +amalgamated +amalgamates +amalgamating +amalgamation +amalgamations +amalgams +amanuenses +amanuensis +amaranth +amaranths +amaretto +amaryllis +amaryllises +amass +amassed +amasses +amassing +amateur +amateurish +amateurishly +amateurishness +amateurism +amateurs +amatory +amaze +amazed +amazement +amazes +amazing +amazingly +amazon +amazonian +amazons +ambassador +ambassadorial +ambassadors +ambassadorship +ambassadorships +ambassadress +ambassadresses +amber +ambergris +ambiance +ambiances +ambidexterity +ambidextrous +ambidextrously +ambience +ambiences +ambient +ambiguities +ambiguity +ambiguous +ambiguously +ambition +ambitions +ambitious +ambitiously +ambitiousness +ambivalence +ambivalent +ambivalently +amble +ambled +ambler +amblers +ambles +ambling +ambrosia +ambrosial +ambulance +ambulances +ambulant +ambulate +ambulated +ambulates +ambulating +ambulation +ambulations +ambulatories +ambulatory +ambuscade +ambuscaded +ambuscades +ambuscading +ambush +ambushed +ambushes +ambushing +ameba +amebae +amebas +amebic +ameboid +ameliorate +ameliorated +ameliorates +ameliorating +amelioration +ameliorative +amen +amenability +amenable +amenably +amend +amendable +amended +amending +amendment +amendments +amends +amenities +amenity +amerce +amerced +amercement +amercements +amerces +amercing +americium +amethyst +amethysts +amiability +amiable +amiably +amicability +amicable +amicably +amid +amide +amides +amidship +amidships +amidst +amigo +amigos +amir +amirs +amiss +amity +ammeter +ammeters +ammo +ammonia +ammunition +amnesia +amnesiac +amnesiacs +amnesic +amnesics +amnestied +amnesties +amnesty +amnestying +amnia +amniocenteses +amniocentesis +amnion +amnions +amniotic +amoeba +amoebae +amoebas +amoebic +amok +among +amongst +amontillado +amontillados +amoral +amorality +amorally +amorous +amorously +amorousness +amorphous +amorphously +amorphousness +amortization +amortizations +amortize +amortized +amortizes +amortizing +amount +amounted +amounting +amounts +amour +amours +amperage +ampere +amperes +ampersand +ampersands +amphetamine +amphetamines +amphibian +amphibians +amphibious +amphibiously +amphitheater +amphitheaters +amphitheatre +amphitheatres +amphora +amphorae +amphoras +ample +ampler +amplest +amplification +amplifications +amplified +amplifier +amplifiers +amplifies +amplify +amplifying +amplitude +amplitudes +amply +ampoule +ampoules +amps +ampul +ampule +ampules +ampuls +amputate +amputated +amputates +amputating +amputation +amputations +amputee +amputees +amuck +amulet +amulets +amuse +amused +amusement +amusements +amuses +amusing +amusingly +amylase +anabolism +anachronism +anachronisms +anachronistic +anachronistically +anaconda +anacondas +anaemia +anaemic +anaerobe +anaerobes +anaerobic +anaerobically +anaesthesia +anaesthetic +anaesthetics +anaesthetist +anaesthetists +anaesthetize +anaesthetized +anaesthetizes +anaesthetizing +anagram +anagrams +anal +analgesia +analgesic +analgesics +anally +analog +analogical +analogically +analogies +analogize +analogized +analogizes +analogizing +analogous +analogously +analogousness +analogs +analogue +analogues +analogy +analysand +analysands +analyse +analysed +analyses +analysing +analysis +analyst +analysts +analytic +analytical +analytically +analyzable +analyze +analyzed +analyzer +analyzers +analyzes +analyzing +anapaest +anapaests +anapest +anapestic +anapestics +anapests +anarchic +anarchically +anarchism +anarchist +anarchistic +anarchists +anarchy +anathema +anathemas +anathematize +anathematized +anathematizes +anathematizing +anatomic +anatomical +anatomically +anatomies +anatomist +anatomists +anatomize +anatomized +anatomizes +anatomizing +anatomy +ancestor +ancestors +ancestral +ancestrally +ancestress +ancestresses +ancestries +ancestry +anchor +anchorage +anchorages +anchored +anchoring +anchorite +anchorites +anchorman +anchormen +anchorperson +anchorpersons +anchors +anchorwoman +anchorwomen +anchovies +anchovy +ancient +ancienter +ancientest +anciently +ancientness +ancients +ancillaries +ancillary +andante +andantes +andiron +andirons +androgen +androgenic +androgynous +androgyny +android +androids +anecdota +anecdotal +anecdote +anecdotes +anemia +anemic +anemically +anemometer +anemometers +anemone +anemones +anent +anesthesia +anesthesiologist +anesthesiologists +anesthesiology +anesthetic +anesthetics +anesthetist +anesthetists +anesthetization +anesthetize +anesthetized +anesthetizes +anesthetizing +aneurism +aneurisms +aneurysm +aneurysms +anew +angel +angelfish +angelfishes +angelic +angelica +angelical +angelically +angels +anger +angered +angering +angers +angina +angioplasties +angioplasty +angiosperm +angiosperms +angle +angled +angler +anglers +angles +angleworm +angleworms +anglicize +anglicized +anglicizes +anglicizing +angling +angora +angoras +angrier +angriest +angrily +angry +angst +angstrom +angstroms +anguish +anguished +anguishes +anguishing +angular +angularities +angularity +anhydrous +aniline +animadversion +animadversions +animadvert +animadverted +animadverting +animadverts +animal +animalcule +animalcules +animals +animate +animated +animatedly +animates +animating +animation +animations +animator +animators +animism +animist +animistic +animists +animosities +animosity +animus +anion +anionic +anions +anise +aniseed +anisette +ankh +ankhs +ankle +anklebone +anklebones +ankles +anklet +anklets +annalist +annalists +annals +anneal +annealed +annealing +anneals +annelid +annelids +annex +annexation +annexations +annexe +annexed +annexes +annexing +annihilate +annihilated +annihilates +annihilating +annihilation +annihilator +annihilators +anniversaries +anniversary +annotate +annotated +annotates +annotating +annotation +annotations +annotative +annotator +annotators +announce +announced +announcement +announcements +announcer +announcers +announces +announcing +annoy +annoyance +annoyances +annoyed +annoying +annoyingly +annoys +annual +annually +annuals +annuitant +annuitants +annuities +annuity +annul +annular +annulled +annulling +annulment +annulments +annuls +annunciation +annunciations +anode +anodes +anodize +anodized +anodizes +anodizing +anodyne +anodynes +anoint +anointed +anointing +anointment +anoints +anomalies +anomalous +anomalously +anomaly +anon +anonymity +anonymous +anonymously +anopheles +anorak +anoraks +anorectic +anorectics +anorexia +anorexic +anorexics +another +answer +answerable +answered +answering +answers +antacid +antacids +antagonism +antagonisms +antagonist +antagonistic +antagonistically +antagonists +antagonize +antagonized +antagonizes +antagonizing +antarctic +ante +anteater +anteaters +antebellum +antecedence +antecedent +antecedents +antechamber +antechambers +anted +antedate +antedated +antedates +antedating +antediluvian +anteed +anteing +antelope +antelopes +antenatal +antenna +antennae +antennas +anterior +anteroom +anterooms +antes +anthem +anthems +anther +anthers +anthill +anthills +anthologies +anthologist +anthologists +anthologize +anthologized +anthologizes +anthologizing +anthology +anthracite +anthrax +anthropocentric +anthropoid +anthropoids +anthropological +anthropologically +anthropologist +anthropologists +anthropology +anthropomorphic +anthropomorphically +anthropomorphism +anthropomorphous +anti +antiabortion +antiabortionist +antiabortionists +antiaircraft +antibacterial +antibacterials +antibiotic +antibiotics +antibodies +antibody +antic +anticancer +antichrist +antichrists +anticipate +anticipated +anticipates +anticipating +anticipation +anticipations +anticipatory +anticlerical +anticlimactic +anticlimactically +anticlimax +anticlimaxes +anticline +anticlines +anticlockwise +anticoagulant +anticoagulants +anticommunism +anticommunist +anticommunists +antics +anticyclone +anticyclones +anticyclonic +antidemocratic +antidepressant +antidepressants +antidote +antidotes +antifascist +antifascists +antifreeze +antigen +antigenic +antigenicity +antigens +antihero +antiheroes +antihistamine +antihistamines +antiknock +antilabor +antilogarithm +antilogarithms +antimacassar +antimacassars +antimalarial +antimalarials +antimatter +antimicrobial +antimissile +antimony +antinuclear +antioxidant +antioxidants +antiparticle +antiparticles +antipasti +antipasto +antipastos +antipathetic +antipathies +antipathy +antipersonnel +antiperspirant +antiperspirants +antiphon +antiphonal +antiphonally +antiphonals +antiphons +antipodal +antipodean +antipodeans +antipodes +antipollution +antipoverty +antiquarian +antiquarianism +antiquarians +antiquaries +antiquary +antiquate +antiquated +antiquates +antiquating +antique +antiqued +antiques +antiquing +antiquities +antiquity +antis +antisemitic +antisemitism +antisepsis +antiseptic +antiseptically +antiseptics +antisera +antiserum +antiserums +antislavery +antisocial +antisocially +antispasmodic +antispasmodics +antisubmarine +antitank +antitheses +antithesis +antithetic +antithetical +antithetically +antitoxin +antitoxins +antitrust +antivenin +antivenins +antiviral +antivirals +antivivisectionist +antivivisectionists +antiwar +antler +antlered +antlers +antonym +antonymous +antonyms +ants +antsier +antsiest +antsy +anus +anuses +anvil +anvils +anxieties +anxiety +anxious +anxiously +anxiousness +anybodies +anybody +anyhow +anymore +anyone +anyplace +anything +anytime +anyway +anywhere +anywise +aorta +aortae +aortas +aortic +apace +apart +apartheid +apartment +apartments +apathetic +apathetically +apathy +apatite +aped +apelike +aperitif +aperitifs +aperture +apertures +apes +apex +apexes +aphasia +aphasic +aphasics +aphelia +aphelion +aphelions +aphid +aphids +aphorism +aphorisms +aphoristic +aphoristically +aphrodisiac +aphrodisiacs +apiaries +apiarist +apiarists +apiary +apical +apically +apices +apiece +aping +apish +apishly +aplenty +aplomb +apocalypse +apocalypses +apocalyptic +apocrypha +apocryphal +apocryphally +apogee +apogees +apolitical +apolitically +apologetic +apologetically +apologia +apologias +apologies +apologist +apologists +apologize +apologized +apologizes +apologizing +apology +apoplectic +apoplexies +apoplexy +apostasies +apostasy +apostate +apostates +apostatize +apostatized +apostatizes +apostatizing +apostle +apostles +apostleship +apostolic +apostrophe +apostrophes +apothecaries +apothecary +apothegm +apothegms +apotheoses +apotheosis +appal +appall +appalled +appalling +appallingly +appalls +appaloosa +appaloosas +appals +apparatus +apparatuses +apparel +appareled +appareling +apparelled +apparelling +apparels +apparent +apparently +apparition +apparitions +appeal +appealed +appealing +appealingly +appeals +appear +appearance +appearances +appeared +appearing +appears +appease +appeased +appeasement +appeasements +appeaser +appeasers +appeases +appeasing +appellant +appellants +appellate +appellation +appellations +append +appendage +appendages +appendectomies +appendectomy +appended +appendices +appendicitis +appending +appendix +appendixes +appends +appertain +appertained +appertaining +appertains +appetite +appetites +appetizer +appetizers +appetizing +appetizingly +applaud +applauded +applauder +applauders +applauding +applauds +applause +apple +applejack +apples +applesauce +applet +applets +appliance +appliances +applicability +applicable +applicably +applicant +applicants +application +applications +applicator +applicators +applied +applier +appliers +applies +applique +appliqued +appliqueing +appliques +apply +applying +appoint +appointed +appointee +appointees +appointing +appointive +appointment +appointments +appoints +apportion +apportioned +apportioning +apportionment +apportions +appose +apposed +apposes +apposing +apposite +appositely +appositeness +apposition +appositive +appositives +appraisal +appraisals +appraise +appraised +appraiser +appraisers +appraises +appraising +appreciable +appreciably +appreciate +appreciated +appreciates +appreciating +appreciation +appreciations +appreciative +appreciatively +appreciator +appreciators +appreciatory +apprehend +apprehended +apprehending +apprehends +apprehension +apprehensions +apprehensive +apprehensively +apprehensiveness +apprentice +apprenticed +apprentices +apprenticeship +apprenticeships +apprenticing +apprise +apprised +apprises +apprising +apprize +apprized +apprizes +apprizing +approach +approachable +approached +approaches +approaching +approbation +approbations +appropriate +appropriated +appropriately +appropriateness +appropriates +appropriating +appropriation +appropriations +appropriator +appropriators +approval +approvals +approve +approved +approves +approving +approvingly +approximate +approximated +approximately +approximates +approximating +approximation +approximations +appurtenance +appurtenances +appurtenant +apricot +apricots +apron +aprons +apropos +apse +apses +apter +aptest +aptitude +aptitudes +aptly +aptness +aqua +aquaculture +aquae +aqualung +aqualungs +aquamarine +aquamarines +aquanaut +aquanauts +aquaplane +aquaplaned +aquaplanes +aquaplaning +aquaria +aquarium +aquariums +aquas +aquatic +aquatically +aquatics +aquavit +aqueduct +aqueducts +aqueous +aquiculture +aquifer +aquifers +aquiline +arabesque +arabesques +arability +arable +arachnid +arachnids +arbiter +arbiters +arbitrage +arbitraged +arbitrager +arbitragers +arbitrages +arbitrageur +arbitrageurs +arbitraging +arbitrament +arbitraments +arbitrarily +arbitrariness +arbitrary +arbitrate +arbitrated +arbitrates +arbitrating +arbitration +arbitrator +arbitrators +arbor +arboreal +arboreta +arboretum +arboretums +arbors +arborvitae +arborvitaes +arbour +arbours +arbutus +arbutuses +arcade +arcades +arcane +arced +arch +archaeological +archaeologically +archaeologist +archaeologists +archaeology +archaic +archaically +archaism +archaisms +archaist +archaists +archangel +archangels +archbishop +archbishopric +archbishoprics +archbishops +archdeacon +archdeacons +archdiocesan +archdiocese +archdioceses +archduchess +archduchesses +archduke +archdukes +arched +archenemies +archenemy +archeological +archeologist +archeologists +archeology +archer +archers +archery +arches +archest +archetypal +archetype +archetypes +archetypical +archfiend +archfiends +archiepiscopal +arching +archipelago +archipelagoes +archipelagos +architect +architectonic +architectonics +architects +architectural +architecturally +architecture +architectures +architrave +architraves +archival +archive +archived +archives +archiving +archivist +archivists +archly +archness +archway +archways +arcing +arcked +arcking +arcs +arctic +arctics +ardent +ardently +ardor +ardors +ardour +ardours +arduous +arduously +arduousness +area +areal +areas +arena +arenas +ares +argent +argon +argosies +argosy +argot +argots +arguable +arguably +argue +argued +arguer +arguers +argues +arguing +argument +argumentation +argumentative +argumentatively +argumentativeness +arguments +argyle +argyles +aria +arias +arid +aridity +aridly +aright +arise +arisen +arises +arising +aristocracies +aristocracy +aristocrat +aristocratic +aristocratically +aristocrats +arithmetic +arithmetical +arithmetically +arithmetician +arithmeticians +arks +armada +armadas +armadillo +armadillos +armament +armaments +armature +armatures +armband +armbands +armchair +armchairs +armed +armful +armfuls +armhole +armholes +armies +arming +armistice +armistices +armlet +armlets +armor +armored +armorer +armorers +armorial +armories +armoring +armors +armory +armour +armoured +armouries +armouring +armours +armoury +armpit +armpits +armrest +armrests +arms +armsful +army +aroma +aromas +aromatherapist +aromatherapists +aromatherapy +aromatic +aromatically +aromatics +arose +around +arousal +arouse +aroused +arouses +arousing +arpeggio +arpeggios +arraign +arraigned +arraigning +arraignment +arraignments +arraigns +arrange +arranged +arrangement +arrangements +arranger +arrangers +arranges +arranging +arrant +arras +arrases +array +arrayed +arraying +arrays +arrears +arrest +arrested +arresting +arrests +arrhythmia +arrhythmic +arrhythmical +arrival +arrivals +arrive +arrived +arrives +arriving +arrogance +arrogant +arrogantly +arrogate +arrogated +arrogates +arrogating +arrogation +arrow +arrowhead +arrowheads +arrowroot +arrows +arroyo +arroyos +arsenal +arsenals +arsenic +arson +arsonist +arsonists +artefact +artefacts +arterial +arteries +arteriole +arterioles +arteriosclerosis +artery +artful +artfully +artfulness +arthritic +arthritics +arthritis +arthropod +arthropods +arthroscope +arthroscopes +arthroscopic +artichoke +artichokes +article +articles +articular +articulate +articulated +articulately +articulateness +articulates +articulating +articulation +articulations +artier +artiest +artifact +artifacts +artifice +artificer +artificers +artifices +artificial +artificiality +artificially +artillery +artilleryman +artillerymen +artiness +artisan +artisans +artist +artiste +artistes +artistic +artistically +artistry +artists +artless +artlessly +artlessness +arts +artsier +artsiest +artsy +artwork +artworks +arty +arum +arums +asbestos +ascend +ascendance +ascendancy +ascendant +ascendants +ascended +ascendency +ascendent +ascendents +ascending +ascends +ascension +ascensions +ascent +ascents +ascertain +ascertainable +ascertained +ascertaining +ascertainment +ascertains +ascetic +ascetically +asceticism +ascetics +ascot +ascots +ascribable +ascribe +ascribed +ascribes +ascribing +ascription +aseptic +aseptically +asexual +asexuality +asexually +ashamed +ashamedly +ashcan +ashcans +ashen +ashes +ashier +ashiest +ashlar +ashlars +ashore +ashram +ashrams +ashtray +ashtrays +ashy +aside +asides +asinine +asininely +asininities +asininity +askance +asked +askew +asking +asks +aslant +asleep +asocial +asparagus +aspartame +aspect +aspects +aspen +aspens +asperities +asperity +aspersion +aspersions +asphalt +asphalted +asphalting +asphalts +asphodel +asphodels +asphyxia +asphyxiate +asphyxiated +asphyxiates +asphyxiating +asphyxiation +asphyxiations +aspic +aspics +aspidistra +aspidistras +aspirant +aspirants +aspirate +aspirated +aspirates +aspirating +aspiration +aspirations +aspirator +aspirators +aspire +aspired +aspires +aspirin +aspiring +aspirins +asps +assail +assailable +assailant +assailants +assailed +assailing +assails +assassin +assassinate +assassinated +assassinates +assassinating +assassination +assassinations +assassins +assault +assaulted +assaulting +assaults +assay +assayed +assayer +assayers +assaying +assays +assemblage +assemblages +assemble +assembled +assembler +assemblers +assembles +assemblies +assembling +assembly +assemblyman +assemblymen +assemblywoman +assemblywomen +assent +assented +assenting +assents +assert +asserted +asserting +assertion +assertions +assertive +assertively +assertiveness +asserts +asses +assess +assessed +assesses +assessing +assessment +assessments +assessor +assessors +asset +assets +asseverate +asseverated +asseverates +asseverating +asseveration +asshole +assholes +assiduity +assiduous +assiduously +assiduousness +assign +assignable +assignation +assignations +assigned +assigner +assigners +assigning +assignment +assignments +assignor +assignors +assigns +assimilate +assimilated +assimilates +assimilating +assimilation +assist +assistance +assistant +assistants +assisted +assisting +assists +assize +assizes +associate +associated +associates +associating +association +associations +associative +assonance +assonant +assonants +assort +assorted +assorting +assortment +assortments +assorts +assuage +assuaged +assuages +assuaging +assumable +assume +assumed +assumes +assuming +assumption +assumptions +assumptive +assurance +assurances +assure +assured +assuredly +assureds +assures +assuring +astatine +aster +asterisk +asterisked +asterisking +asterisks +astern +asteroid +asteroids +asters +asthma +asthmatic +asthmatics +astigmatic +astigmatism +astigmatisms +astir +astonish +astonished +astonishes +astonishing +astonishingly +astonishment +astound +astounded +astounding +astoundingly +astounds +astraddle +astrakhan +astral +astray +astride +astringency +astringent +astringently +astringents +astrolabe +astrolabes +astrologer +astrologers +astrological +astrologically +astrologist +astrologists +astrology +astronaut +astronautic +astronautical +astronautics +astronauts +astronomer +astronomers +astronomic +astronomical +astronomically +astronomy +astrophysical +astrophysicist +astrophysicists +astrophysics +astute +astutely +astuteness +astuter +astutest +asunder +asylum +asylums +asymmetric +asymmetrical +asymmetrically +asymmetry +asymptomatic +atavism +atavist +atavistic +atavists +ataxia +ataxic +ataxics +atelier +ateliers +atheism +atheist +atheistic +atheists +atherosclerosis +athirst +athlete +athletes +athletic +athletically +athletics +athwart +atilt +atlas +atlases +atmosphere +atmospheres +atmospheric +atmospherically +atmospherics +atoll +atolls +atom +atomic +atomically +atomize +atomized +atomizer +atomizers +atomizes +atomizing +atoms +atonal +atonality +atonally +atone +atoned +atonement +atones +atoning +atop +atria +atrial +atrium +atriums +atrocious +atrociously +atrociousness +atrocities +atrocity +atrophied +atrophies +atrophy +atrophying +atropine +attach +attachable +attache +attached +attaches +attaching +attachment +attachments +attack +attacked +attacker +attackers +attacking +attacks +attain +attainability +attainable +attainder +attained +attaining +attainment +attainments +attains +attar +attempt +attempted +attempting +attempts +attend +attendance +attendances +attendant +attendants +attended +attendee +attendees +attending +attends +attention +attentions +attentive +attentively +attentiveness +attenuate +attenuated +attenuates +attenuating +attenuation +attest +attestation +attestations +attested +attesting +attests +attic +attics +attire +attired +attires +attiring +attitude +attitudes +attitudinal +attitudinize +attitudinized +attitudinizes +attitudinizing +attorney +attorneys +attract +attractable +attractant +attractants +attracted +attracting +attraction +attractions +attractive +attractively +attractiveness +attracts +attributable +attribute +attributed +attributes +attributing +attribution +attributions +attributive +attributively +attributives +attrition +attune +attuned +attunes +attuning +atwitter +atypical +atypically +auburn +auction +auctioned +auctioneer +auctioneers +auctioning +auctions +audacious +audaciously +audaciousness +audacity +audibility +audible +audibles +audibly +audience +audiences +audio +audiobook +audiobooks +audiological +audiologist +audiologists +audiology +audiometer +audiometers +audiophile +audiophiles +audios +audiotape +audiotapes +audiovisual +audiovisuals +audit +audited +auditing +audition +auditioned +auditioning +auditions +auditor +auditoria +auditorium +auditoriums +auditors +auditory +audits +auger +augers +aught +aughts +augment +augmentation +augmentations +augmentative +augmented +augmenter +augmenters +augmenting +augments +augur +augured +auguries +auguring +augurs +augury +august +auguster +augustest +augustly +augustness +auks +aunt +auntie +aunties +aunts +aunty +aura +aurae +aural +aurally +auras +aureola +aureolas +aureole +aureoles +auricle +auricles +auricular +aurora +aurorae +auroras +auscultate +auscultated +auscultates +auscultating +auscultation +auscultations +auspice +auspices +auspicious +auspiciously +auspiciousness +austere +austerely +austerer +austerest +austerities +austerity +austral +authentic +authentically +authenticate +authenticated +authenticates +authenticating +authentication +authentications +authenticity +author +authored +authoress +authoresses +authoring +authoritarian +authoritarianism +authoritarians +authoritative +authoritatively +authoritativeness +authorities +authority +authorization +authorizations +authorize +authorized +authorizes +authorizing +authors +authorship +autism +autistic +auto +autobahn +autobahns +autobiographer +autobiographers +autobiographic +autobiographical +autobiographically +autobiographies +autobiography +autoclave +autoclaves +autocracies +autocracy +autocrat +autocratic +autocratically +autocrats +autodidact +autodidacts +autograph +autographed +autographing +autographs +autoimmune +autoimmunity +automaker +automakers +automata +automate +automated +automates +automatic +automatically +automatics +automating +automation +automatism +automatize +automatized +automatizes +automatizing +automaton +automatons +automobile +automobiles +automotive +autonomic +autonomous +autonomously +autonomy +autopilot +autopilots +autopsied +autopsies +autopsy +autopsying +autos +autoworker +autoworkers +autumn +autumnal +autumns +auxiliaries +auxiliary +auxin +avail +availability +available +availed +availing +avails +avalanche +avalanches +avarice +avaricious +avariciously +avast +avatar +avatars +avaunt +avenge +avenged +avenger +avengers +avenges +avenging +avenue +avenues +aver +average +averaged +averages +averaging +averred +averring +avers +averse +aversion +aversions +avert +averted +averting +averts +avian +aviaries +aviary +aviation +aviator +aviators +aviatrices +aviatrix +aviatrixes +avid +avidity +avidly +avionic +avionics +avitaminosis +avocado +avocadoes +avocados +avocation +avocational +avocations +avoid +avoidable +avoidably +avoidance +avoided +avoiding +avoids +avoirdupois +avouch +avouched +avouches +avouching +avow +avowal +avowals +avowed +avowedly +avowing +avows +avuncular +await +awaited +awaiting +awaits +awake +awaked +awaken +awakened +awakening +awakenings +awakens +awakes +awaking +award +awarded +awarding +awards +aware +awareness +awash +away +awed +aweigh +awes +awesome +awesomely +awesomeness +awestricken +awestruck +awful +awfuller +awfullest +awfully +awfulness +awhile +awing +awkward +awkwarder +awkwardest +awkwardly +awkwardness +awls +awning +awnings +awns +awoke +awoken +awol +awry +axed +axes +axial +axially +axing +axiom +axiomatic +axiomatically +axioms +axis +axle +axles +axletree +axletrees +axolotl +axolotls +axon +axons +ayah +ayahs +ayatollah +ayatollahs +ayes +azalea +azaleas +azimuth +azimuths +azure +baaed +baaing +baas +babble +babbled +babbler +babblers +babbles +babbling +babe +babel +babels +babes +babied +babier +babies +babiest +baboon +baboons +babushka +babushkas +baby +babyhood +babying +babyish +babysat +babysit +babysits +babysitter +babysitters +babysitting +baccalaureate +baccalaureates +baccarat +bacchanal +bacchanalia +bacchanalian +bacchanalians +bacchanalias +bacchanals +bachelor +bachelorhood +bachelors +bacillary +bacilli +bacillus +back +backache +backaches +backbencher +backbenchers +backbit +backbite +backbiter +backbiters +backbites +backbiting +backbitten +backboard +backboards +backbone +backbones +backbreaking +backdate +backdated +backdates +backdating +backdrop +backdrops +backed +backer +backers +backfield +backfields +backfire +backfired +backfires +backfiring +backgammon +background +backgrounder +backgrounders +backgrounds +backhand +backhanded +backhandedly +backhander +backhanders +backhanding +backhands +backhoe +backhoes +backing +backings +backlash +backlashes +backless +backlog +backlogged +backlogging +backlogs +backpack +backpacked +backpacker +backpackers +backpacking +backpacks +backpedal +backpedaled +backpedaling +backpedalled +backpedalling +backpedals +backrest +backrests +backroom +backs +backscratching +backseat +backseats +backside +backsides +backslapper +backslappers +backslapping +backslash +backslashes +backslid +backslidden +backslide +backslider +backsliders +backslides +backsliding +backspace +backspaced +backspaces +backspacing +backspin +backstabber +backstabbers +backstage +backstair +backstairs +backstop +backstopped +backstopping +backstops +backstretch +backstretches +backstroke +backstroked +backstrokes +backstroking +backtalk +backtrack +backtracked +backtracking +backtracks +backup +backups +backward +backwardly +backwardness +backwards +backwash +backwater +backwaters +backwoods +backwoodsman +backwoodsmen +backyard +backyards +bacon +bacteria +bacterial +bactericidal +bactericide +bactericides +bacteriologic +bacteriological +bacteriologist +bacteriologists +bacteriology +bacterium +badder +baddest +baddie +baddies +baddy +bade +badge +badger +badgered +badgering +badgers +badges +badinage +badlands +badly +badman +badmen +badminton +badmouth +badmouthed +badmouthing +badmouths +badness +baffle +baffled +bafflement +baffler +bafflers +baffles +baffling +bagatelle +bagatelles +bagel +bagels +bagful +bagfuls +baggage +bagged +baggie +baggier +baggies +baggiest +baggily +bagginess +bagging +baggy +bagpipe +bagpiper +bagpipers +bagpipes +bags +bagsful +baguette +baguettes +baht +bahts +bail +bailable +bailed +bailiff +bailiffs +bailing +bailiwick +bailiwicks +bailout +bailouts +bails +bailsman +bailsmen +bairn +bairns +bait +baited +baiting +baits +baize +bake +baked +baker +bakeries +bakers +bakery +bakes +bakeshop +bakeshops +baking +baklava +baksheesh +balaclava +balaclavas +balalaika +balalaikas +balance +balanced +balances +balancing +balboa +balboas +balconies +balcony +bald +balded +balder +balderdash +baldest +baldfaced +balding +baldly +baldness +baldric +baldrics +balds +bale +baled +baleen +baleful +balefully +balefulness +baler +balers +bales +baling +balk +balked +balkier +balkiest +balking +balks +balky +ball +ballad +balladeer +balladeers +balladry +ballads +ballast +ballasted +ballasting +ballasts +ballcock +ballcocks +balled +ballerina +ballerinas +ballet +balletic +ballets +ballgame +ballgames +balling +ballistic +ballistics +balloon +ballooned +ballooning +balloonist +balloonists +balloons +ballot +balloted +balloting +ballots +ballpark +ballparks +ballplayer +ballplayers +ballpoint +ballpoints +ballroom +ballrooms +balls +ballsier +ballsiest +ballsy +ballyhoo +ballyhooed +ballyhooing +ballyhoos +balm +balmier +balmiest +balminess +balms +balmy +baloney +balsa +balsam +balsamic +balsams +balsas +baluster +balusters +balustrade +balustrades +bamboo +bamboos +bamboozle +bamboozled +bamboozles +bamboozling +banal +banalities +banality +banally +banana +bananas +band +bandage +bandaged +bandages +bandaging +bandana +bandanas +bandanna +bandannas +bandbox +bandboxes +bandeau +bandeaux +banded +bandied +bandier +bandies +bandiest +banding +bandit +banditry +bandits +banditti +bandmaster +bandmasters +bandoleer +bandoleers +bandolier +bandoliers +bands +bandsman +bandsmen +bandstand +bandstands +bandwagon +bandwagons +bandy +bandying +bane +baneful +banes +bang +banged +banging +bangle +bangles +bangs +bani +banish +banished +banishes +banishing +banishment +banister +banisters +banjo +banjoes +banjoist +banjoists +banjos +bank +bankable +bankbook +bankbooks +bankcard +bankcards +banked +banker +bankers +banking +banknote +banknotes +bankroll +bankrolled +bankrolling +bankrolls +bankrupt +bankruptcies +bankruptcy +bankrupted +bankrupting +bankrupts +banks +banned +banner +banners +banning +bannister +bannisters +bannock +bannocks +banns +banquet +banqueted +banqueter +banqueters +banqueting +banquets +banquette +banquettes +bans +banshee +banshees +banshie +banshies +bantam +bantams +bantamweight +bantamweights +banter +bantered +bantering +banteringly +banters +banyan +banyans +banzai +banzais +baobab +baobabs +baptism +baptismal +baptisms +baptisteries +baptistery +baptistries +baptistry +baptize +baptized +baptizer +baptizers +baptizes +baptizing +barb +barbarian +barbarianism +barbarianisms +barbarians +barbaric +barbarically +barbarism +barbarisms +barbarities +barbarity +barbarize +barbarized +barbarizes +barbarizing +barbarous +barbarously +barbecue +barbecued +barbecues +barbecuing +barbed +barbel +barbell +barbells +barbels +barbeque +barbequed +barbeques +barbequing +barber +barbered +barbering +barberries +barberry +barbers +barbershop +barbershops +barbing +barbiturate +barbiturates +barbs +barbwire +barcarole +barcaroles +barcarolle +barcarolles +bard +bardic +bards +bare +bareback +barebacked +bared +barefaced +barefacedly +barefoot +barefooted +barehanded +bareheaded +barelegged +barely +bareness +barer +bares +barest +barf +barfed +barfing +barflies +barfly +barfs +bargain +bargained +bargainer +bargainers +bargaining +bargains +barge +barged +bargeman +bargemen +barges +barging +barhop +barhopped +barhopping +barhops +baring +baritone +baritones +barium +bark +barked +barkeep +barkeeper +barkeepers +barkeeps +barker +barkers +barking +barks +barley +barmaid +barmaids +barman +barmen +barn +barnacle +barnacled +barnacles +barns +barnstorm +barnstormed +barnstormer +barnstormers +barnstorming +barnstorms +barnyard +barnyards +barometer +barometers +barometric +barometrically +baron +baronage +baronages +baroness +baronesses +baronet +baronetcies +baronetcy +baronets +baronial +baronies +barons +barony +baroque +barque +barques +barrack +barracked +barracking +barracks +barracuda +barracudas +barrage +barraged +barrages +barraging +barre +barred +barrel +barreled +barreling +barrelled +barrelling +barrels +barren +barrener +barrenest +barrenness +barrens +barres +barrette +barrettes +barricade +barricaded +barricades +barricading +barrier +barriers +barring +barrio +barrios +barrister +barristers +barroom +barrooms +barrow +barrows +bars +bartender +bartenders +barter +bartered +barterer +barterers +bartering +barters +baryon +baryons +basal +basally +basalt +basaltic +base +baseball +baseballs +baseboard +baseboards +based +baseless +baseline +baselines +basely +baseman +basemen +basement +basements +baseness +baser +bases +basest +bash +bashed +bashes +bashful +bashfully +bashfulness +bashing +basic +basically +basics +basil +basilica +basilicas +basilisk +basilisks +basin +basinful +basinfuls +basing +basins +basis +bask +basked +basket +basketball +basketballs +basketry +baskets +basketwork +basking +basks +bass +basses +basset +bassets +bassi +bassinet +bassinets +bassist +bassists +basso +bassoon +bassoonist +bassoonists +bassoons +bassos +basswood +basswoods +bast +bastard +bastardization +bastardizations +bastardize +bastardized +bastardizes +bastardizing +bastards +bastardy +baste +basted +baster +basters +bastes +basting +bastion +bastions +batch +batched +batches +batching +bate +bated +bates +bath +bathe +bathed +bather +bathers +bathes +bathetic +bathhouse +bathhouses +bathing +bathmat +bathmats +bathos +bathrobe +bathrobes +bathroom +bathrooms +baths +bathtub +bathtubs +bathyscaph +bathyscaphe +bathyscaphes +bathyscaphs +bathysphere +bathyspheres +batik +batiks +bating +batiste +batman +batmen +baton +batons +bats +batsman +batsmen +battalion +battalions +batted +batten +battened +battening +battens +batter +battered +batterer +batterers +batteries +battering +batters +battery +battier +battiest +batting +battle +battleax +battleaxe +battleaxes +battled +battledore +battledores +battlefield +battlefields +battlefront +battlefronts +battleground +battlegrounds +battlement +battlements +battler +battlers +battles +battleship +battleships +battling +batty +bauble +baubles +baud +bauds +baulk +baulked +baulking +baulks +bauxite +bawd +bawdier +bawdiest +bawdily +bawdiness +bawds +bawdy +bawl +bawled +bawling +bawls +bayberries +bayberry +bayed +baying +bayonet +bayoneted +bayoneting +bayonets +bayonetted +bayonetting +bayou +bayous +bays +bazaar +bazaars +bazooka +bazookas +beach +beachcomber +beachcombers +beached +beaches +beachhead +beachheads +beaching +beachwear +beacon +beacons +bead +beaded +beadier +beadiest +beading +beadle +beadles +beads +beady +beagle +beagles +beak +beaked +beaker +beakers +beaks +beam +beamed +beaming +beams +bean +beanbag +beanbags +beaned +beanie +beanies +beaning +beanpole +beanpoles +beans +beanstalk +beanstalks +bear +bearable +bearably +beard +bearded +bearding +beardless +beards +bearer +bearers +bearing +bearings +bearish +bearishly +bearishness +bearlike +bears +bearskin +bearskins +beast +beastlier +beastliest +beastliness +beastly +beasts +beat +beatable +beaten +beater +beaters +beatific +beatifically +beatification +beatifications +beatified +beatifies +beatify +beatifying +beating +beatings +beatitude +beatitudes +beatnik +beatniks +beats +beau +beaus +beaut +beauteous +beauteously +beautician +beauticians +beauties +beautification +beautified +beautifier +beautifiers +beautifies +beautiful +beautifully +beautify +beautifying +beauts +beauty +beaux +beaver +beavered +beavering +beavers +bebop +becalm +becalmed +becalming +becalms +became +because +beck +beckon +beckoned +beckoning +beckons +becks +becloud +beclouded +beclouding +beclouds +become +becomes +becoming +becomingly +bedaub +bedaubed +bedaubing +bedaubs +bedazzle +bedazzled +bedazzlement +bedazzles +bedazzling +bedbug +bedbugs +bedclothes +bedded +bedding +bedeck +bedecked +bedecking +bedecks +bedevil +bedeviled +bedeviling +bedevilled +bedevilling +bedevilment +bedevils +bedfellow +bedfellows +bedim +bedimmed +bedimming +bedims +bedizen +bedizened +bedizening +bedizens +bedlam +bedlams +bedpan +bedpans +bedpost +bedposts +bedraggle +bedraggled +bedraggles +bedraggling +bedridden +bedrock +bedrocks +bedroll +bedrolls +bedroom +bedrooms +beds +bedside +bedsides +bedsore +bedsores +bedspread +bedspreads +bedstead +bedsteads +bedtime +bedtimes +beebread +beech +beeches +beechnut +beechnuts +beef +beefburger +beefburgers +beefcake +beefcakes +beefed +beefier +beefiest +beefiness +beefing +beefs +beefsteak +beefsteaks +beefy +beehive +beehives +beekeeper +beekeepers +beekeeping +beeline +beelines +been +beep +beeped +beeper +beepers +beeping +beeps +beer +beerier +beeriest +beers +beery +bees +beeswax +beet +beetle +beetled +beetles +beetling +beets +beeves +befall +befallen +befalling +befalls +befell +befit +befits +befitted +befitting +befittingly +befog +befogged +befogging +befogs +before +beforehand +befoul +befouled +befouling +befouls +befriend +befriended +befriending +befriends +befuddle +befuddled +befuddlement +befuddles +befuddling +began +begat +beget +begets +begetting +beggar +beggared +beggaring +beggarly +beggars +beggary +begged +begging +begin +beginner +beginners +beginning +beginnings +begins +begone +begonia +begonias +begot +begotten +begrime +begrimed +begrimes +begriming +begrudge +begrudged +begrudges +begrudging +begrudgingly +begs +beguile +beguiled +beguilement +beguiler +beguilers +beguiles +beguiling +beguilingly +beguine +beguines +begum +begums +begun +behalf +behalves +behave +behaved +behaves +behaving +behavior +behavioral +behaviorally +behaviorism +behaviorist +behaviorists +behaviour +behead +beheaded +beheading +beheads +beheld +behemoth +behemoths +behest +behests +behind +behindhand +behinds +behold +beholden +beholder +beholders +beholding +beholds +behoove +behooved +behooves +behooving +beige +being +beings +bejewel +bejeweled +bejeweling +bejewelled +bejewelling +bejewels +belabor +belabored +belaboring +belabors +belabour +belaboured +belabouring +belabours +belated +belatedly +belay +belayed +belaying +belays +belch +belched +belches +belching +beleaguer +beleaguered +beleaguering +beleaguers +belfries +belfry +belie +belied +belief +beliefs +belies +believable +believably +believe +believed +believer +believers +believes +believing +belittle +belittled +belittlement +belittles +belittling +bell +belladonna +bellboy +bellboys +belle +belled +belles +belletrist +belletristic +belletrists +bellhop +bellhops +bellicose +bellicosity +bellied +bellies +belligerence +belligerency +belligerent +belligerently +belligerents +belling +bellman +bellmen +bellow +bellowed +bellowing +bellows +bells +bellwether +bellwethers +belly +bellyache +bellyached +bellyaches +bellyaching +bellybutton +bellybuttons +bellyful +bellyfuls +bellying +belong +belonged +belonging +belongings +belongs +beloved +beloveds +below +belt +belted +belting +belts +beltway +beltways +beluga +belugas +belying +bemire +bemired +bemires +bemiring +bemoan +bemoaned +bemoaning +bemoans +bemuse +bemused +bemusedly +bemusement +bemuses +bemusing +bench +benched +benches +benching +benchmark +benchmarks +bend +bendable +bender +benders +bending +bends +beneath +benediction +benedictions +benedictory +benefaction +benefactions +benefactor +benefactors +benefactress +benefactresses +benefice +beneficence +beneficent +beneficently +benefices +beneficial +beneficially +beneficiaries +beneficiary +benefit +benefited +benefiting +benefits +benefitted +benefitting +benevolence +benevolences +benevolent +benevolently +benighted +benightedly +benign +benignant +benigner +benignest +benignity +benignly +bent +bents +bentwood +benumb +benumbed +benumbing +benumbs +benzene +benzine +bequeath +bequeathed +bequeathing +bequeaths +bequest +bequests +berate +berated +berates +berating +bereave +bereaved +bereavement +bereavements +bereaves +bereaving +bereft +beret +berets +berg +bergs +beriberi +berkelium +berm +berms +berried +berries +berry +berrying +berrylike +berserk +berth +berthed +berthing +berths +beryl +beryllium +beryls +beseech +beseeched +beseecher +beseechers +beseeches +beseeching +beseechingly +beseem +beseemed +beseeming +beseems +beset +besets +besetting +beside +besides +besiege +besieged +besieger +besiegers +besieges +besieging +besmear +besmeared +besmearing +besmears +besmirch +besmirched +besmirches +besmirching +besom +besoms +besot +besots +besotted +besotting +besought +bespangle +bespangled +bespangles +bespangling +bespatter +bespattered +bespattering +bespatters +bespeak +bespeaking +bespeaks +bespectacled +bespoke +bespoken +best +bested +bestial +bestiality +bestially +bestiaries +bestiary +besting +bestir +bestirred +bestirring +bestirs +bestow +bestowal +bestowals +bestowed +bestowing +bestows +bestrew +bestrewed +bestrewing +bestrewn +bestrews +bestrid +bestridden +bestride +bestrides +bestriding +bestrode +bests +bestseller +bestsellers +beta +betake +betaken +betakes +betaking +betas +betcha +betel +bethink +bethinking +bethinks +bethought +betide +betided +betides +betiding +betimes +betoken +betokened +betokening +betokens +betook +betray +betrayal +betrayals +betrayed +betrayer +betrayers +betraying +betrays +betroth +betrothal +betrothals +betrothed +betrothing +betroths +bets +betted +better +bettered +bettering +betterment +betters +betting +bettor +bettors +between +betwixt +bevel +beveled +beveling +bevelled +bevelling +bevels +beverage +beverages +bevies +bevy +bewail +bewailed +bewailing +bewails +beware +bewared +bewares +bewaring +bewhiskered +bewigged +bewilder +bewildered +bewildering +bewilderingly +bewilderment +bewilders +bewitch +bewitched +bewitches +bewitching +bewitchingly +bewitchment +beyond +beys +bezel +bezels +biannual +biannually +bias +biased +biases +biasing +biassed +biassing +biathlon +biathlons +bible +bibles +biblical +bibliographer +bibliographers +bibliographic +bibliographical +bibliographically +bibliographies +bibliography +bibliophile +bibliophiles +bibs +bibulous +bicameral +bicameralism +bicarb +bicarbonate +bicarbonates +bicarbs +bicentenaries +bicentenary +bicentennial +bicentennials +bicep +biceps +bicepses +bicker +bickered +bickerer +bickerers +bickering +bickers +biconcave +biconvex +bicuspid +bicuspids +bicycle +bicycled +bicycler +bicyclers +bicycles +bicycling +bicyclist +bicyclists +biddable +bidden +bidder +bidders +biddies +bidding +biddy +bide +bided +bides +bidet +bidets +biding +bidirectional +bidirectionally +bids +biennia +biennial +biennially +biennials +biennium +bienniums +bier +biers +bifocal +bifocals +bifurcate +bifurcated +bifurcates +bifurcating +bifurcation +bifurcations +bigamist +bigamists +bigamous +bigamy +bigger +biggest +biggie +biggies +biggish +bighead +bigheads +bighearted +bigheartedness +bighorn +bighorns +bight +bights +bigmouth +bigmouths +bigness +bigot +bigoted +bigotries +bigotry +bigots +bigwig +bigwigs +bijou +bijoux +bike +biked +biker +bikers +bikes +biking +bikini +bikinis +bilabial +bilabials +bilateral +bilaterally +bile +bilge +bilges +bilingual +bilingualism +bilingually +bilinguals +bilious +biliousness +bilk +bilked +bilker +bilkers +bilking +bilks +bill +billable +billboard +billboards +billed +billet +billeted +billeting +billets +billfold +billfolds +billiard +billiards +billies +billing +billings +billingsgate +billion +billionaire +billionaires +billions +billionth +billionths +billow +billowed +billowing +billows +billowy +bills +billy +bimbo +bimboes +bimbos +bimetallic +bimetallics +bimetallism +bimonthlies +bimonthly +binaries +binary +bind +binder +binderies +binders +bindery +binding +bindings +binds +bindweed +binge +binged +bingeing +binges +binging +bingo +binnacle +binnacles +binned +binning +binocular +binoculars +binomial +binomials +bins +biochemical +biochemically +biochemicals +biochemist +biochemistry +biochemists +biodegradability +biodegradable +biodegrade +biodegraded +biodegrades +biodegrading +biodiversity +bioethics +biofeedback +biographer +biographers +biographic +biographical +biographically +biographies +biography +biologic +biological +biologically +biologist +biologists +biology +biomass +bionic +bionically +bionics +biophysical +biophysicist +biophysicists +biophysics +biopic +biopics +biopsied +biopsies +biopsy +biopsying +biorhythm +biorhythms +bios +biosphere +biospheres +biotechnological +biotechnology +biotin +bipartisan +bipartisanship +bipartite +biped +bipedal +bipeds +biplane +biplanes +bipolar +bipolarity +biracial +birch +birched +birches +birching +bird +birdbath +birdbaths +birdbrain +birdbrained +birdbrains +birded +birder +birders +birdhouse +birdhouses +birdie +birdied +birdieing +birdies +birding +birdlime +birds +birdseed +birdwatcher +birdwatchers +biretta +birettas +birth +birthday +birthdays +birthed +birthing +birthmark +birthmarks +birthplace +birthplaces +birthrate +birthrates +birthright +birthrights +births +birthstone +birthstones +biscuit +biscuits +bisect +bisected +bisecting +bisection +bisections +bisector +bisectors +bisects +bisexual +bisexuality +bisexually +bisexuals +bishop +bishopric +bishoprics +bishops +bismuth +bison +bisons +bisque +bistro +bistros +bitch +bitched +bitches +bitchier +bitchiest +bitchily +bitchiness +bitching +bitchy +bite +biter +biters +bites +biting +bitingly +bits +bitten +bitter +bitterer +bitterest +bitterly +bittern +bitterness +bitterns +bitters +bittersweet +bittersweets +bittier +bittiest +bitty +bitumen +bituminous +bivalent +bivalve +bivalves +bivouac +bivouacked +bivouacking +bivouacs +biweeklies +biweekly +biyearly +bizarre +bizarrely +blab +blabbed +blabber +blabbered +blabbering +blabbermouth +blabbermouths +blabbers +blabbing +blabs +black +blackamoor +blackamoors +blackball +blackballed +blackballing +blackballs +blackberries +blackberry +blackbird +blackbirds +blackboard +blackboards +blacked +blacken +blackened +blackening +blackens +blacker +blackest +blackguard +blackguards +blackhead +blackheads +blacking +blackish +blackjack +blackjacked +blackjacking +blackjacks +blacklist +blacklisted +blacklisting +blacklists +blackly +blackmail +blackmailed +blackmailer +blackmailers +blackmailing +blackmails +blackness +blackout +blackouts +blacks +blacksmith +blacksmiths +blacksnake +blacksnakes +blackthorn +blackthorns +blacktop +blacktopped +blacktopping +blacktops +bladder +bladders +blade +bladed +blades +blah +blahs +blamable +blame +blameable +blamed +blameless +blamelessly +blamelessness +blames +blameworthiness +blameworthy +blaming +blanch +blanched +blanches +blanching +blancmange +blancmanges +bland +blander +blandest +blandish +blandished +blandishes +blandishing +blandishment +blandishments +blandly +blandness +blank +blanked +blanker +blankest +blanket +blanketed +blanketing +blankets +blanking +blankly +blankness +blanks +blare +blared +blares +blaring +blarney +blarneyed +blarneying +blarneys +blase +blaspheme +blasphemed +blasphemer +blasphemers +blasphemes +blasphemies +blaspheming +blasphemous +blasphemously +blasphemy +blast +blasted +blaster +blasters +blasting +blastoff +blastoffs +blasts +blatancies +blatancy +blatant +blatantly +blather +blathered +blathering +blathers +blaze +blazed +blazer +blazers +blazes +blazing +blazon +blazoned +blazoning +blazons +bleach +bleached +bleacher +bleachers +bleaches +bleaching +bleak +bleaker +bleakest +bleakly +bleakness +blear +blearier +bleariest +blearily +bleariness +bleary +bleat +bleated +bleating +bleats +bled +bleed +bleeder +bleeders +bleeding +bleeds +bleep +bleeped +bleeper +bleepers +bleeping +bleeps +blemish +blemished +blemishes +blemishing +blench +blenched +blenches +blenching +blend +blended +blender +blenders +blending +blends +blent +bless +blessed +blessedly +blessedness +blesses +blessing +blessings +blest +blew +blight +blighted +blighting +blights +blimey +blimp +blimps +blind +blinded +blinder +blinders +blindest +blindfold +blindfolded +blindfolding +blindfolds +blinding +blindingly +blindly +blindness +blinds +blindside +blindsided +blindsides +blindsiding +blini +blinis +blink +blinked +blinker +blinkered +blinkering +blinkers +blinking +blinks +blintz +blintze +blintzes +blip +blips +bliss +blissful +blissfully +blissfulness +blister +blistered +blistering +blisteringly +blisters +blistery +blithe +blithely +blitheness +blither +blithering +blithesome +blithest +blitz +blitzed +blitzes +blitzing +blitzkrieg +blitzkriegs +blizzard +blizzards +bloat +bloated +bloating +bloats +blob +blobbed +blobbing +blobs +bloc +block +blockade +blockaded +blockader +blockaders +blockades +blockading +blockage +blockages +blockbuster +blockbusters +blockbusting +blocked +blocker +blockers +blockhead +blockheads +blockhouse +blockhouses +blocking +blocks +blocs +bloke +blokes +blond +blonde +blonder +blondes +blondest +blondish +blondness +blonds +blood +bloodbath +bloodbaths +bloodcurdling +blooded +bloodhound +bloodhounds +bloodied +bloodier +bloodies +bloodiest +bloodiness +blooding +bloodless +bloodlessly +bloodlessness +bloodletting +bloodline +bloodlines +bloodmobile +bloodmobiles +bloods +bloodshed +bloodshot +bloodstain +bloodstained +bloodstains +bloodstock +bloodstream +bloodstreams +bloodsucker +bloodsuckers +bloodsucking +bloodthirstier +bloodthirstiest +bloodthirstily +bloodthirstiness +bloodthirsty +bloody +bloodying +bloom +bloomed +bloomer +bloomers +blooming +blooms +bloop +blooped +blooper +bloopers +blooping +bloops +blossom +blossomed +blossoming +blossoms +blossomy +blot +blotch +blotched +blotches +blotchier +blotchiest +blotching +blotchy +blots +blotted +blotter +blotters +blotting +blotto +blouse +bloused +blouses +blousing +blow +blower +blowers +blowflies +blowfly +blowgun +blowguns +blowhard +blowhards +blowier +blowiest +blowing +blown +blowout +blowouts +blowpipe +blowpipes +blows +blowsier +blowsiest +blowsy +blowtorch +blowtorched +blowtorches +blowtorching +blowup +blowups +blowy +blowzier +blowziest +blowzy +blubber +blubbered +blubbering +blubbers +blubbery +bludgeon +bludgeoned +bludgeoning +bludgeons +blue +bluebell +bluebells +blueberries +blueberry +bluebird +bluebirds +bluebonnet +bluebonnets +bluebottle +bluebottles +blued +bluefish +bluefishes +bluegill +bluegills +bluegrass +blueing +blueish +bluejacket +bluejackets +bluejay +bluejays +bluejeans +blueness +bluenose +bluenoses +bluepoint +bluepoints +blueprint +blueprinted +blueprinting +blueprints +bluer +blues +bluesier +bluesiest +bluest +bluestocking +bluestockings +bluesy +bluet +bluets +bluff +bluffed +bluffer +bluffers +bluffest +bluffing +bluffly +bluffness +bluffs +bluing +bluish +blunder +blunderbuss +blunderbusses +blundered +blunderer +blunderers +blundering +blunders +blunt +blunted +blunter +bluntest +blunting +bluntly +bluntness +blunts +blur +blurb +blurbs +blurred +blurrier +blurriest +blurriness +blurring +blurry +blurs +blurt +blurted +blurting +blurts +blush +blushed +blusher +blushers +blushes +blushing +bluster +blustered +blusterer +blusterers +blustering +blusterous +blusters +blustery +boar +board +boarded +boarder +boarders +boarding +boardinghouse +boardinghouses +boardroom +boardrooms +boards +boardwalk +boardwalks +boars +boas +boast +boasted +boaster +boasters +boastful +boastfully +boastfulness +boasting +boasts +boat +boated +boater +boaters +boathouse +boathouses +boating +boatman +boatmen +boats +boatswain +boatswains +bobbed +bobbies +bobbin +bobbing +bobbins +bobble +bobbled +bobbles +bobbling +bobby +bobbysoxer +bobbysoxers +bobcat +bobcats +bobolink +bobolinks +bobs +bobsled +bobsledded +bobsledder +bobsledders +bobsledding +bobsleds +bobsleigh +bobsleighed +bobsleighing +bobsleighs +bobtail +bobtails +bobwhite +bobwhites +bocce +bocci +boccie +bock +bodacious +bode +boded +bodega +bodegas +bodes +bodice +bodices +bodied +bodies +bodily +boding +bodkin +bodkins +bods +body +bodybuilder +bodybuilders +bodybuilding +bodyguard +bodyguards +bodysuit +bodysuits +bodywork +boffo +bogey +bogeyed +bogeying +bogeyman +bogeymen +bogeys +bogged +boggier +boggiest +bogging +boggle +boggled +boggles +boggling +boggy +bogie +bogied +bogieing +bogies +bogs +bogus +bogy +bogyman +bogymen +bohemian +bohemianism +bohemians +boil +boiled +boiler +boilermaker +boilermakers +boilerplate +boilers +boiling +boils +boisterous +boisterously +boisterousness +bola +bolas +bold +bolder +boldest +boldface +boldfaced +boldly +boldness +bole +bolero +boleros +boles +bolivar +bolivares +bolivars +boll +bollix +bollixed +bollixes +bollixing +bolls +bologna +boloney +bolshevik +bolsheviki +bolsheviks +bolster +bolstered +bolstering +bolsters +bolt +bolted +bolting +bolts +bolus +boluses +bomb +bombard +bombarded +bombardier +bombardiers +bombarding +bombardment +bombardments +bombards +bombast +bombastic +bombastically +bombed +bomber +bombers +bombing +bombproof +bombproofed +bombproofing +bombproofs +bombs +bombshell +bombshells +bonanza +bonanzas +bonbon +bonbons +bond +bondage +bonded +bondholder +bondholders +bonding +bondman +bondmen +bonds +bondsman +bondsmen +bondwoman +bondwomen +bone +boned +bonehead +boneheaded +boneheads +boneless +boner +boners +bones +boney +boneyer +boneyest +bonfire +bonfires +bong +bonged +bonging +bongo +bongoes +bongos +bongs +bonhomie +bonier +boniest +boniness +boning +bonito +bonitoes +bonitos +bonkers +bonnet +bonnets +bonnie +bonnier +bonniest +bonny +bonsai +bonus +bonuses +bony +boob +boobed +boobies +boobing +booboo +booboos +boobs +booby +boodle +boodles +booed +boogeyman +boogeymen +boogie +boogied +boogieing +boogieman +boogiemen +boogies +boohoo +boohooed +boohooing +boohoos +booing +book +bookbinder +bookbinderies +bookbinders +bookbindery +bookbinding +bookcase +bookcases +booked +bookend +bookended +bookending +bookends +bookie +bookies +booking +bookings +bookish +bookkeeper +bookkeepers +bookkeeping +booklet +booklets +bookmaker +bookmakers +bookmaking +bookmark +bookmarked +bookmarking +bookmarks +bookmobile +bookmobiles +bookplate +bookplates +books +bookseller +booksellers +bookshelf +bookshelves +bookshop +bookshops +bookstore +bookstores +bookworm +bookworms +boom +boombox +boomboxes +boomed +boomerang +boomeranged +boomeranging +boomerangs +booming +booms +boon +boondocks +boondoggle +boondoggled +boondoggler +boondogglers +boondoggles +boondoggling +boonies +boons +boor +boorish +boorishly +boorishness +boorishnesses +boors +boos +boost +boosted +booster +boosters +boosting +boosts +boot +bootblack +bootblacks +booted +bootee +bootees +booth +booths +bootie +booties +booting +bootleg +bootlegged +bootlegger +bootleggers +bootlegging +bootlegs +bootless +boots +bootstrap +bootstrapped +bootstrapping +bootstraps +booty +booze +boozed +boozer +boozers +boozes +boozier +booziest +boozing +boozy +bopped +bopping +bops +borax +bordello +bordellos +border +bordered +bordering +borderland +borderlands +borderline +borderlines +borders +bore +bored +boredom +borer +borers +bores +boring +born +borne +boron +borough +boroughs +borrow +borrowed +borrower +borrowers +borrowing +borrowings +borrows +borsch +borscht +borzoi +borzois +bosh +bosom +bosoms +bosomy +boss +bossed +bosses +bossier +bossiest +bossily +bossiness +bossing +bossism +bossy +bosun +bosuns +botanic +botanical +botanically +botanist +botanists +botany +botch +botched +botcher +botchers +botches +botching +both +bother +bothered +bothering +bothers +bothersome +bottle +bottled +bottleneck +bottlenecks +bottler +bottlers +bottles +bottling +bottom +bottomed +bottoming +bottomless +bottoms +botulism +boudoir +boudoirs +bouffant +bouffants +bougainvillea +bougainvilleas +bough +boughs +bought +bouillabaisse +bouillabaisses +bouillon +bouillons +boulder +boulders +boulevard +boulevards +bounce +bounced +bouncer +bouncers +bounces +bouncier +bounciest +bouncily +bounciness +bouncing +bouncy +bound +boundaries +boundary +bounded +bounden +bounder +bounders +bounding +boundless +boundlessly +boundlessness +bounds +bounteous +bounteously +bounteousness +bounties +bountiful +bountifully +bountifulness +bounty +bouquet +bouquets +bourbon +bourgeois +bourgeoisie +bout +boutique +boutiques +boutonniere +boutonnieres +bouts +bouzouki +bouzoukis +bovine +bovines +bowdlerization +bowdlerizations +bowdlerize +bowdlerized +bowdlerizes +bowdlerizing +bowed +bowel +bowels +bower +bowers +bowing +bowl +bowlder +bowlders +bowled +bowleg +bowlegged +bowlegs +bowler +bowlers +bowlful +bowlfuls +bowline +bowlines +bowling +bowls +bowman +bowmen +bows +bowsprit +bowsprits +bowstring +bowstrings +bowwow +bowwows +boxcar +boxcars +boxed +boxer +boxers +boxes +boxier +boxiest +boxing +boxlike +boxwood +boxy +boycott +boycotted +boycotting +boycotts +boyfriend +boyfriends +boyhood +boyhoods +boyish +boyishly +boyishness +boys +boysenberries +boysenberry +bozo +bozos +brace +braced +bracelet +bracelets +bracer +bracero +braceros +bracers +braces +bracing +bracken +bracket +bracketed +bracketing +brackets +brackish +brackishness +bract +bracts +brad +brads +brae +braes +brag +braggadocio +braggadocios +braggart +braggarts +bragged +bragger +braggers +bragging +brags +braid +braided +braiding +braids +braille +brain +brainchild +brainchildren +brained +brainier +brainiest +braininess +braining +brainless +brainlessly +brains +brainstorm +brainstormed +brainstorming +brainstorms +brainteaser +brainteasers +brainwash +brainwashed +brainwashes +brainwashing +brainy +braise +braised +braises +braising +brake +braked +brakeman +brakemen +brakes +braking +bramble +brambles +bramblier +brambliest +brambly +bran +branch +branched +branches +branching +branchlike +brand +branded +brander +branders +brandied +brandies +branding +brandish +brandished +brandishes +brandishing +brands +brandy +brandying +bras +brash +brasher +brashest +brashly +brashness +brass +brasserie +brasseries +brasses +brassier +brassiere +brassieres +brassiest +brassily +brassiness +brassy +brat +brats +brattier +brattiest +bratty +bratwurst +bratwursts +bravado +brave +braved +bravely +braveness +braver +bravery +braves +bravest +braving +bravo +bravos +bravura +bravuras +brawl +brawled +brawler +brawlers +brawling +brawls +brawn +brawnier +brawniest +brawniness +brawny +bray +brayed +braying +brays +braze +brazed +brazen +brazened +brazening +brazenly +brazenness +brazens +brazer +brazers +brazes +brazier +braziers +brazing +breach +breached +breaches +breaching +bread +breadbasket +breadbaskets +breadboard +breadboards +breadbox +breadboxes +breadcrumb +breadcrumbs +breaded +breadfruit +breadfruits +breading +breadline +breadlines +breads +breadth +breadths +breadwinner +breadwinners +break +breakable +breakables +breakage +breakages +breakaway +breakaways +breakdown +breakdowns +breaker +breakers +breakeven +breakfast +breakfasted +breakfasting +breakfasts +breakfront +breakfronts +breaking +breakneck +breakout +breakouts +breaks +breakthrough +breakthroughs +breakup +breakups +breakwater +breakwaters +bream +breams +breast +breastbone +breastbones +breasted +breasting +breastplate +breastplates +breasts +breaststroke +breaststrokes +breastwork +breastworks +breath +breathable +breathalyze +breathalyzed +breathalyzes +breathalyzing +breathe +breathed +breather +breathers +breathes +breathier +breathiest +breathing +breathless +breathlessly +breathlessness +breaths +breathtaking +breathtakingly +breathy +bred +breech +breeches +breed +breeder +breeders +breeding +breeds +breeze +breezed +breezes +breezeway +breezeways +breezier +breeziest +breezily +breeziness +breezing +breezy +brethren +breve +breves +brevet +breveted +breveting +brevets +brevetted +brevetting +breviaries +breviary +brevity +brew +brewed +brewer +breweries +brewers +brewery +brewing +brewpub +brewpubs +brews +briar +briars +bribe +bribed +briber +bribers +bribery +bribes +bribing +brick +brickbat +brickbats +bricked +bricking +bricklayer +bricklayers +bricklaying +bricks +brickwork +bridal +bridals +bride +bridegroom +bridegrooms +brides +bridesmaid +bridesmaids +bridge +bridgeable +bridged +bridgehead +bridgeheads +bridges +bridgework +bridging +bridle +bridled +bridles +bridling +brie +brief +briefcase +briefcases +briefed +briefer +briefest +briefing +briefings +briefly +briefness +briefs +brier +briers +brig +brigade +brigades +brigadier +brigadiers +brigand +brigandage +brigands +brigantine +brigantines +bright +brighten +brightened +brightener +brighteners +brightening +brightens +brighter +brightest +brightly +brightness +brights +brigs +brilliance +brilliancy +brilliant +brilliantine +brilliantly +brilliants +brim +brimful +brimfull +brimless +brimmed +brimming +brims +brimstone +brindle +brindled +brine +bring +bringer +bringers +bringing +brings +brinier +briniest +brininess +brink +brinkmanship +brinks +brinksmanship +briny +brioche +brioches +briquet +briquets +briquette +briquettes +brisk +brisked +brisker +briskest +brisket +briskets +brisking +briskly +briskness +brisks +bristle +bristled +bristles +bristlier +bristliest +bristling +bristly +britches +brittle +brittleness +brittler +brittlest +broach +broached +broaches +broaching +broad +broadband +broadcast +broadcasted +broadcaster +broadcasters +broadcasting +broadcasts +broadcloth +broaden +broadened +broadening +broadens +broader +broadest +broadloom +broadly +broadminded +broadness +broads +broadsheet +broadsheets +broadside +broadsided +broadsides +broadsiding +broadsword +broadswords +brocade +brocaded +brocades +brocading +broccoli +brochette +brochettes +brochure +brochures +brogan +brogans +brogue +brogues +broil +broiled +broiler +broilers +broiling +broils +broke +broken +brokenhearted +brokenheartedly +brokenly +brokenness +broker +brokerage +brokerages +brokered +brokering +brokers +bromide +bromides +bromidic +bromine +bronc +bronchi +bronchial +bronchitic +bronchitis +broncho +bronchos +bronchus +bronco +broncobuster +broncobusters +broncos +broncs +brontosaur +brontosauri +brontosaurs +brontosaurus +brontosauruses +bronze +bronzed +bronzes +bronzing +brooch +brooches +brood +brooded +brooder +brooders +broodier +broodiest +brooding +broodingly +broodmare +broodmares +broods +broody +brook +brooked +brooking +brooklet +brooklets +brooks +broom +brooms +broomstick +broomsticks +bros +broth +brothel +brothels +brother +brotherhood +brotherhoods +brotherliness +brotherly +brothers +broths +brougham +broughams +brought +brouhaha +brouhahas +brow +browbeat +browbeaten +browbeating +browbeats +brown +browned +browner +brownest +brownie +brownies +browning +brownish +brownness +brownout +brownouts +browns +brownstone +brownstones +brows +browse +browsed +browser +browsers +browses +browsing +bruin +bruins +bruise +bruised +bruiser +bruisers +bruises +bruising +bruit +bruited +bruiting +bruits +brunch +brunched +brunches +brunching +brunet +brunets +brunette +brunettes +brunt +brush +brushed +brushes +brushing +brushoff +brushoffs +brushwood +brushwork +brusk +brusker +bruskest +brusque +brusquely +brusqueness +brusquer +brusquest +brutal +brutalities +brutality +brutalization +brutalize +brutalized +brutalizes +brutalizing +brutally +brute +brutes +brutish +brutishly +brutishness +bubble +bubbled +bubblegum +bubbles +bubblier +bubbliest +bubbling +bubbly +bubo +buboes +bubs +buccaneer +buccaneers +buck +buckaroo +buckaroos +buckboard +buckboards +bucked +bucket +bucketed +bucketful +bucketfuls +bucketing +buckets +buckeye +buckeyes +bucking +buckle +buckled +buckler +bucklers +buckles +buckling +buckram +bucks +bucksaw +bucksaws +buckshot +buckskin +buckskins +buckteeth +bucktooth +bucktoothed +buckwheat +bucolic +bucolically +bucolics +budded +buddies +budding +buddy +budge +budged +budgerigar +budgerigars +budges +budget +budgetary +budgeted +budgeting +budgets +budgie +budgies +budging +buds +buff +buffalo +buffaloed +buffaloes +buffaloing +buffalos +buffed +buffer +buffered +buffering +buffers +buffet +buffeted +buffeting +buffets +buffing +buffoon +buffoonery +buffoonish +buffoons +buffs +bugaboo +bugaboos +bugbear +bugbears +bugged +bugger +buggered +buggering +buggers +buggier +buggies +buggiest +bugging +buggy +bugle +bugled +bugler +buglers +bugles +bugling +bugs +build +builder +builders +building +buildings +builds +buildup +buildups +built +bulb +bulbous +bulbs +bulge +bulged +bulges +bulgier +bulgiest +bulging +bulgy +bulimarexia +bulimia +bulimic +bulimics +bulk +bulked +bulkhead +bulkheads +bulkier +bulkiest +bulkiness +bulking +bulks +bulky +bull +bulldog +bulldogged +bulldogging +bulldogs +bulldoze +bulldozed +bulldozer +bulldozers +bulldozes +bulldozing +bulled +bullet +bulletin +bulletined +bulletining +bulletins +bulletproof +bulletproofed +bulletproofing +bulletproofs +bullets +bullfight +bullfighter +bullfighters +bullfighting +bullfights +bullfinch +bullfinches +bullfrog +bullfrogs +bullhead +bullheaded +bullheadedly +bullheadedness +bullheads +bullhorn +bullhorns +bullied +bullies +bulling +bullion +bullish +bullishly +bullishness +bullock +bullocks +bullpen +bullpens +bullring +bullrings +bulls +bullshit +bullshits +bullshitted +bullshitter +bullshitters +bullshitting +bully +bullying +bulrush +bulrushes +bulwark +bulwarks +bumble +bumblebee +bumblebees +bumbled +bumbler +bumblers +bumbles +bumbling +bummed +bummer +bummers +bummest +bumming +bump +bumped +bumper +bumpers +bumpier +bumpiest +bumpiness +bumping +bumpkin +bumpkins +bumps +bumptious +bumptiously +bumptiousness +bumpy +bums +bunch +bunched +bunches +bunchier +bunchiest +bunching +bunchy +bunco +buncoed +buncoing +buncombe +buncos +bundle +bundled +bundles +bundling +bung +bungalow +bungalows +bunged +bungee +bungees +bunghole +bungholes +bunging +bungle +bungled +bungler +bunglers +bungles +bungling +bungs +bunion +bunions +bunk +bunked +bunker +bunkers +bunkhouse +bunkhouses +bunking +bunko +bunkos +bunks +bunkum +bunnies +bunny +buns +bunt +bunted +bunting +buntings +bunts +buoy +buoyancy +buoyant +buoyantly +buoyed +buoying +buoys +burble +burbled +burbles +burbling +burbs +burden +burdened +burdening +burdens +burdensome +burdock +bureau +bureaucracies +bureaucracy +bureaucrat +bureaucratic +bureaucratically +bureaucratization +bureaucratize +bureaucratized +bureaucratizes +bureaucratizing +bureaucrats +bureaus +bureaux +burg +burgeon +burgeoned +burgeoning +burgeons +burger +burgers +burgh +burgher +burghers +burghs +burglar +burglaries +burglarize +burglarized +burglarizes +burglarizing +burglarproof +burglarproofed +burglarproofing +burglarproofs +burglars +burglary +burgle +burgled +burgles +burgling +burgomaster +burgomasters +burgs +burgundies +burgundy +burial +burials +buried +buries +burl +burlap +burled +burlesque +burlesqued +burlesques +burlesquing +burlier +burliest +burliness +burls +burly +burn +burnable +burnables +burned +burner +burners +burning +burnish +burnished +burnisher +burnishers +burnishes +burnishing +burnoose +burnooses +burnous +burnouses +burnout +burnouts +burns +burnt +burp +burped +burping +burps +burr +burred +burring +burrito +burritos +burro +burros +burrow +burrowed +burrower +burrowers +burrowing +burrows +burrs +burs +bursa +bursae +bursar +bursaries +bursars +bursary +bursas +bursitis +burst +bursted +bursting +bursts +bury +burying +busbies +busboy +busboys +busby +bused +buses +busgirl +busgirls +bush +bushed +bushel +busheled +busheling +bushelled +bushelling +bushels +bushes +bushier +bushiest +bushiness +bushing +bushings +bushman +bushmaster +bushmasters +bushmen +bushwhack +bushwhacked +bushwhacker +bushwhackers +bushwhacking +bushwhacks +bushy +busied +busier +busies +busiest +busily +business +businesses +businesslike +businessman +businessmen +businessperson +businesspersons +businesswoman +businesswomen +busing +buskin +buskins +buss +bussed +busses +bussing +bust +busted +buster +busters +bustier +bustiest +busting +bustle +bustled +bustles +bustling +busts +busty +busy +busybodies +busybody +busying +busyness +busywork +butane +butch +butcher +butchered +butcheries +butchering +butchers +butchery +butches +butler +butlers +buts +butt +butte +butted +butter +butterball +butterballs +buttercup +buttercups +buttered +butterfat +butterfingered +butterfingers +butterflied +butterflies +butterfly +butterflying +butterier +butteries +butteriest +buttering +buttermilk +butternut +butternuts +butters +butterscotch +buttery +buttes +butting +buttock +buttocks +button +buttoned +buttonhole +buttonholed +buttonholes +buttonholing +buttoning +buttons +buttonwood +buttonwoods +buttress +buttressed +buttresses +buttressing +butts +buxom +buyback +buybacks +buyer +buyers +buying +buyout +buyouts +buys +buzz +buzzard +buzzards +buzzed +buzzer +buzzers +buzzes +buzzing +buzzword +buzzwords +byelaw +byelaws +byes +bygone +bygones +bylaw +bylaws +byline +bylines +bypass +bypassed +bypasses +bypassing +bypast +bypath +bypaths +byplay +byproduct +byproducts +byroad +byroads +bystander +bystanders +byte +bytes +byway +byways +byword +bywords +byzantine +cabal +cabala +caballero +caballeros +cabals +cabana +cabanas +cabaret +cabarets +cabbage +cabbages +cabbed +cabbie +cabbies +cabbing +cabby +cabdriver +cabdrivers +cabin +cabinet +cabinetmaker +cabinetmakers +cabinetmaking +cabinetry +cabinets +cabinetwork +cabins +cable +cablecast +cablecasted +cablecasting +cablecasts +cabled +cablegram +cablegrams +cables +cabling +cabochon +cabochons +caboodle +caboose +cabooses +cabriolet +cabriolets +cabs +cabstand +cabstands +cacao +cacaos +cache +cached +cachepot +cachepots +caches +cachet +cachets +caching +cackle +cackled +cackler +cacklers +cackles +cackling +cacophonies +cacophonous +cacophony +cacti +cactus +cactuses +cadaver +cadaverous +cadavers +caddie +caddied +caddies +caddish +caddishly +caddishness +caddy +caddying +cadence +cadenced +cadences +cadenza +cadenzas +cadet +cadets +cadge +cadged +cadger +cadgers +cadges +cadging +cadmium +cadre +cadres +cads +caducei +caduceus +caesarean +caesareans +caesarian +caesarians +caesura +caesurae +caesuras +cafe +cafes +cafeteria +cafeterias +caffeine +caftan +caftans +cage +caged +cages +cagey +cagier +cagiest +cagily +caginess +caging +cagy +cahoot +cahoots +caiman +caimans +cairn +cairns +caisson +caissons +caitiff +caitiffs +cajole +cajoled +cajolement +cajoler +cajolers +cajolery +cajoles +cajoling +cake +caked +cakes +cakewalk +cakewalks +caking +calabash +calabashes +calaboose +calabooses +calamari +calamaris +calamine +calamities +calamitous +calamitously +calamity +calcareous +calciferous +calcification +calcified +calcifies +calcify +calcifying +calcimine +calcimined +calcimines +calcimining +calcine +calcined +calcines +calcining +calcite +calcium +calculable +calculate +calculated +calculatedly +calculates +calculating +calculatingly +calculation +calculations +calculative +calculator +calculators +calculi +calculus +calculuses +caldera +calderas +caldron +caldrons +calendar +calendared +calendaring +calendars +calender +calendered +calendering +calenders +calf +calfs +calfskin +caliber +calibers +calibrate +calibrated +calibrates +calibrating +calibration +calibrations +calibrator +calibrators +calibre +calibres +calico +calicoes +calicos +calif +californium +califs +caliper +calipered +calipering +calipers +caliph +caliphate +caliphates +caliphs +calisthenic +calisthenics +calk +calked +calking +calks +call +calla +callas +callback +callbacks +called +caller +callers +calligrapher +calligraphers +calligraphic +calligraphist +calligraphists +calligraphy +calling +callings +calliope +calliopes +calliper +callipered +callipering +callipers +callisthenics +callosities +callosity +callous +calloused +callouses +callousing +callously +callousness +callow +callower +callowest +callowness +calls +callus +callused +calluses +callusing +calm +calmed +calmer +calmest +calming +calmly +calmness +calms +caloric +calorie +calories +calorific +calumet +calumets +calumniate +calumniated +calumniates +calumniating +calumniation +calumniator +calumniators +calumnies +calumnious +calumny +calve +calved +calves +calving +calyces +calypso +calypsos +calyx +calyxes +camaraderie +camber +cambered +cambering +cambers +cambia +cambial +cambium +cambiums +cambric +camcorder +camcorders +came +camel +camelhair +camellia +camellias +camels +cameo +cameos +camera +cameraman +cameramen +cameras +camerawoman +camerawomen +camisole +camisoles +camomile +camomiles +camouflage +camouflaged +camouflager +camouflagers +camouflages +camouflaging +camp +campaign +campaigned +campaigner +campaigners +campaigning +campaigns +campanile +campaniles +campanili +campanologist +campanologists +campanology +camped +camper +campers +campfire +campfires +campground +campgrounds +camphor +campier +campiest +camping +camps +campsite +campsites +campus +campuses +campy +cams +camshaft +camshafts +canal +canalization +canalize +canalized +canalizes +canalizing +canals +canape +canapes +canard +canards +canaries +canary +canasta +cancan +cancans +cancel +canceled +canceler +cancelers +canceling +cancellation +cancellations +cancelled +canceller +cancellers +cancelling +cancels +cancer +cancerous +cancers +candelabra +candelabras +candelabrum +candelabrums +candid +candidacies +candidacy +candidate +candidates +candidature +candidatures +candidly +candidness +candied +candies +candle +candled +candlelight +candlepower +candler +candlers +candles +candlestick +candlesticks +candlewick +candlewicks +candling +candor +candour +candy +candying +cane +canebrake +canebrakes +caned +caner +caners +canes +canine +canines +caning +canister +canisters +canker +cankered +cankering +cankerous +cankers +cannabis +cannabises +canned +cannelloni +canneries +cannery +cannibal +cannibalism +cannibalistic +cannibalization +cannibalize +cannibalized +cannibalizes +cannibalizing +cannibals +cannier +canniest +cannily +canniness +canning +cannon +cannonade +cannonaded +cannonades +cannonading +cannonball +cannonballs +cannoned +cannoning +cannons +cannot +canny +canoe +canoed +canoeing +canoeist +canoeists +canoes +canola +canon +canonical +canonically +canonization +canonizations +canonize +canonized +canonizes +canonizing +canons +canopied +canopies +canopy +canopying +cans +canst +cant +cantabile +cantaloup +cantaloupe +cantaloupes +cantaloups +cantankerous +cantankerously +cantankerousness +cantata +cantatas +canted +canteen +canteens +canter +cantered +cantering +canters +canticle +canticles +cantilever +cantilevered +cantilevering +cantilevers +canting +canto +canton +cantonal +cantonment +cantonments +cantons +cantor +cantors +cantos +cants +canvas +canvasback +canvasbacks +canvased +canvases +canvasing +canvass +canvassed +canvasser +canvassers +canvasses +canvassing +canyon +canyons +capabilities +capability +capable +capably +capacious +capaciously +capaciousness +capacitance +capacities +capacitor +capacitors +capacity +caparison +caparisoned +caparisoning +caparisons +cape +caped +caper +capered +capering +capers +capes +capeskin +capillaries +capillarity +capillary +capital +capitalism +capitalist +capitalistic +capitalistically +capitalists +capitalization +capitalize +capitalized +capitalizes +capitalizing +capitally +capitals +capitation +capitations +capitol +capitols +capitulate +capitulated +capitulates +capitulating +capitulation +capitulations +caplet +caplets +capo +capon +capons +capos +capped +capping +cappuccino +cappuccinos +caprice +caprices +capricious +capriciously +capriciousness +caps +capsicum +capsicums +capsize +capsized +capsizes +capsizing +capstan +capstans +capstone +capstones +capsular +capsule +capsuled +capsules +capsuling +capsulize +capsulized +capsulizes +capsulizing +captain +captaincies +captaincy +captained +captaining +captains +caption +captioned +captioning +captions +captious +captiously +captiousness +captivate +captivated +captivates +captivating +captivation +captivator +captivators +captive +captives +captivities +captivity +captor +captors +capture +captured +captures +capturing +caracul +carafe +carafes +caramel +caramelize +caramelized +caramelizes +caramelizing +caramels +carapace +carapaces +carat +carats +caravan +caravans +caravansaries +caravansary +caravanserai +caravanserais +caravel +caravels +caraway +caraways +carbide +carbides +carbine +carbines +carbohydrate +carbohydrates +carbon +carbonaceous +carbonate +carbonated +carbonates +carbonating +carbonation +carboniferous +carbonize +carbonized +carbonizes +carbonizing +carbons +carborundum +carboy +carboys +carbuncle +carbuncles +carbuncular +carburetor +carburetors +carburetter +carburetters +carburettor +carburettors +carcass +carcasses +carcinogen +carcinogenic +carcinogenicity +carcinogenics +carcinogens +carcinoma +carcinomas +carcinomata +card +cardamom +cardamoms +cardboard +carded +carder +carders +cardiac +cardigan +cardigans +cardinal +cardinally +cardinals +carding +cardiogram +cardiograms +cardiograph +cardiographs +cardiologist +cardiologists +cardiology +cardiopulmonary +cardiovascular +cards +cardsharp +cardsharper +cardsharpers +cardsharps +care +cared +careen +careened +careening +careens +career +careered +careering +careerist +careerists +careers +carefree +careful +carefuller +carefullest +carefully +carefulness +caregiver +caregivers +careless +carelessly +carelessness +carer +carers +cares +caress +caressed +caresses +caressing +caret +caretaker +caretakers +carets +careworn +carfare +cargo +cargoes +cargos +carhop +carhops +caribou +caribous +caricature +caricatured +caricatures +caricaturing +caricaturist +caricaturists +caries +carillon +carillons +caring +carious +carjack +carjacked +carjacker +carjackers +carjacking +carjackings +carjacks +carload +carloads +carmine +carnage +carnal +carnality +carnally +carnation +carnations +carnelian +carnelians +carney +carneys +carnies +carnival +carnivals +carnivore +carnivores +carnivorous +carnivorously +carnivorousness +carny +carob +carol +caroled +caroler +carolers +caroling +carolled +caroller +carollers +carolling +carols +carom +caromed +caroming +caroms +carotene +carotid +carotids +carousal +carousals +carouse +caroused +carousel +carousels +carouser +carousers +carouses +carousing +carp +carpal +carpals +carped +carpel +carpels +carpenter +carpentered +carpentering +carpenters +carpentry +carper +carpers +carpet +carpetbag +carpetbagged +carpetbagger +carpetbaggers +carpetbagging +carpetbags +carpeted +carpeting +carpets +carpi +carping +carpool +carpooled +carpooling +carpools +carport +carports +carps +carpus +carrel +carrell +carrells +carrels +carriage +carriages +carried +carrier +carriers +carries +carrion +carrot +carrots +carroty +carrousel +carrousels +carry +carryall +carryalls +carrying +carryout +carryover +carryovers +cars +carsick +carsickness +cart +cartage +carted +cartel +cartels +carter +carters +carthorse +carthorses +cartilage +cartilages +cartilaginous +carting +cartload +cartloads +cartographer +cartographers +cartographic +cartography +carton +cartons +cartoon +cartooned +cartooning +cartoonist +cartoonists +cartoons +cartridge +cartridges +carts +cartwheel +cartwheeled +cartwheeling +cartwheels +carve +carved +carver +carvers +carves +carving +carvings +caryatid +caryatides +caryatids +casaba +casabas +cascade +cascaded +cascades +cascading +cascara +cascaras +case +cased +caseharden +casehardened +casehardening +casehardens +casein +caseload +caseloads +casement +casements +cases +casework +caseworker +caseworkers +cash +cashbook +cashbooks +cashed +cashes +cashew +cashews +cashier +cashiered +cashiering +cashiers +cashing +cashless +cashmere +casing +casings +casino +casinos +cask +casket +caskets +casks +cassava +cassavas +casserole +casseroled +casseroles +casseroling +cassette +cassettes +cassia +cassias +cassino +cassock +cassocks +cassowaries +cassowary +cast +castanet +castanets +castaway +castaways +caste +castellated +caster +casters +castes +castigate +castigated +castigates +castigating +castigation +castigator +castigators +casting +castings +castle +castled +castles +castling +castoff +castoffs +castor +castors +castrate +castrated +castrates +castrating +castration +castrations +casts +casual +casually +casualness +casuals +casualties +casualty +casuist +casuistic +casuistry +casuists +cataclysm +cataclysmal +cataclysmic +cataclysms +catacomb +catacombs +catafalque +catafalques +catalepsy +cataleptic +cataleptics +catalog +cataloged +cataloger +catalogers +cataloging +catalogs +catalogue +catalogued +cataloguer +cataloguers +catalogues +cataloguing +catalpa +catalpas +catalysis +catalyst +catalysts +catalytic +catalytics +catalyze +catalyzed +catalyzes +catalyzing +catamaran +catamarans +catapult +catapulted +catapulting +catapults +cataract +cataracts +catarrh +catastrophe +catastrophes +catastrophic +catastrophically +catatonia +catatonic +catatonics +catbird +catbirds +catboat +catboats +catcall +catcalled +catcalling +catcalls +catch +catchall +catchalls +catcher +catchers +catches +catchier +catchiest +catching +catchment +catchments +catchpenny +catchphrase +catchphrases +catchup +catchword +catchwords +catchy +catechism +catechisms +catechist +catechists +catechize +catechized +catechizes +catechizing +categorical +categorically +categories +categorization +categorizations +categorize +categorized +categorizes +categorizing +category +cater +catercorner +catered +caterer +caterers +catering +caterpillar +caterpillars +caters +caterwaul +caterwauled +caterwauling +caterwauls +catfish +catfishes +catgut +catharses +catharsis +cathartic +cathartics +cathedral +cathedrals +catheter +catheterize +catheterized +catheterizes +catheterizing +catheters +cathode +cathodes +cathodic +catholic +catholicity +cation +cations +catkin +catkins +catlike +catnap +catnapped +catnapping +catnaps +catnip +cats +catsup +cattail +cattails +cattier +cattiest +cattily +cattiness +cattle +cattleman +cattlemen +catty +catwalk +catwalks +caucus +caucused +caucuses +caucusing +caucussed +caucussing +caudal +caudally +caught +cauldron +cauldrons +cauliflower +cauliflowers +caulk +caulked +caulker +caulkers +caulking +caulks +causal +causalities +causality +causally +causation +causative +cause +caused +causeless +causer +causerie +causeries +causers +causes +causeway +causeways +causing +caustic +caustically +causticity +caustics +cauterization +cauterize +cauterized +cauterizes +cauterizing +caution +cautionary +cautioned +cautioning +cautions +cautious +cautiously +cautiousness +cavalcade +cavalcades +cavalier +cavalierly +cavaliers +cavalries +cavalry +cavalryman +cavalrymen +cave +caveat +caveats +caved +caveman +cavemen +cavern +cavernous +cavernously +caverns +caves +caviar +caviare +cavil +caviled +caviler +cavilers +caviling +cavilled +caviller +cavillers +cavilling +cavils +caving +cavities +cavity +cavort +cavorted +cavorting +cavorts +cawed +cawing +caws +cayenne +cayman +caymans +cays +cayuse +cayuses +cease +ceased +ceasefire +ceasefires +ceaseless +ceaselessly +ceaselessness +ceases +ceasing +ceca +cecal +cecum +cedar +cedars +cede +ceded +ceder +ceders +cedes +cedilla +cedillas +ceding +ceiling +ceilings +celandine +celebrant +celebrants +celebrate +celebrated +celebrates +celebrating +celebration +celebrations +celebrator +celebrators +celebratory +celebrities +celebrity +celerity +celery +celesta +celestas +celestial +celestially +celibacy +celibate +celibates +cell +cellar +cellars +celled +celli +cellist +cellists +cellmate +cellmates +cello +cellophane +cellos +cellphone +cellphones +cells +cellular +cellulite +celluloid +cellulose +cement +cemented +cementer +cementers +cementing +cements +cementum +cemeteries +cemetery +cenobite +cenobites +cenobitic +cenotaph +cenotaphs +censer +censers +censor +censored +censorial +censoring +censorious +censoriously +censoriousness +censors +censorship +censurable +censure +censured +censurer +censurers +censures +censuring +census +censused +censuses +censusing +cent +centaur +centaurs +centavo +centavos +centenarian +centenarians +centenaries +centenary +centennial +centennially +centennials +center +centerboard +centerboards +centered +centerfold +centerfolds +centering +centerpiece +centerpieces +centers +centigrade +centigram +centigramme +centigrammes +centigrams +centiliter +centiliters +centime +centimes +centimeter +centimeters +centimetre +centimetres +centipede +centipedes +central +centrality +centralization +centralize +centralized +centralizer +centralizers +centralizes +centralizing +centrally +centrals +centre +centred +centres +centrifugal +centrifugally +centrifuge +centrifuged +centrifuges +centrifuging +centring +centripetal +centripetally +centrism +centrist +centrists +cents +centuries +centurion +centurions +century +cephalic +ceramic +ceramicist +ceramicists +ceramics +ceramist +ceramists +cereal +cereals +cerebella +cerebellar +cerebellum +cerebellums +cerebra +cerebral +cerebrate +cerebrated +cerebrates +cerebrating +cerebration +cerebrum +cerebrums +cerement +cerements +ceremonial +ceremonially +ceremonials +ceremonies +ceremonious +ceremoniously +ceremoniousness +ceremony +cerise +cerium +cermet +certain +certainly +certainties +certainty +certifiable +certifiably +certificate +certificated +certificates +certificating +certification +certifications +certified +certifies +certify +certifying +certitude +cerulean +cervical +cervices +cervix +cervixes +cesarean +cesareans +cesium +cessation +cessations +cession +cessions +cesspool +cesspools +cetacean +cetaceans +chafe +chafed +chafes +chaff +chaffed +chaffinch +chaffinches +chaffing +chaffs +chafing +chagrin +chagrined +chagrining +chagrinned +chagrinning +chagrins +chain +chained +chaining +chains +chainsaw +chainsawed +chainsawing +chainsaws +chair +chaired +chairing +chairlift +chairlifts +chairman +chairmanship +chairmen +chairperson +chairpersons +chairs +chairwoman +chairwomen +chaise +chaises +chalcedony +chalet +chalets +chalice +chalices +chalk +chalkboard +chalkboards +chalked +chalkier +chalkiest +chalkiness +chalking +chalks +chalky +challenge +challenged +challenger +challengers +challenges +challenging +challis +chamber +chambered +chamberlain +chamberlains +chambermaid +chambermaids +chambers +chambray +chameleon +chameleons +chammies +chammy +chamois +chamoix +chamomile +chamomiles +champ +champagne +champagnes +champed +champing +champion +championed +championing +champions +championship +championships +champs +chance +chanced +chancel +chancelleries +chancellery +chancellor +chancellors +chancellorship +chancels +chanceries +chancery +chances +chancier +chanciest +chanciness +chancing +chancre +chancres +chancy +chandelier +chandeliers +chandler +chandlers +change +changeability +changeable +changeableness +changeably +changed +changeless +changelessly +changeling +changelings +changeover +changeovers +changer +changers +changes +changing +channel +channeled +channeling +channelization +channelize +channelized +channelizes +channelizing +channelled +channelling +channels +chanson +chansons +chant +chanted +chanter +chanters +chanteuse +chanteuses +chantey +chanteys +chanticleer +chanticleers +chanties +chanting +chants +chanty +chaos +chaotic +chaotically +chap +chaparral +chaparrals +chapbook +chapbooks +chapeau +chapeaus +chapeaux +chapel +chapels +chaperon +chaperonage +chaperone +chaperoned +chaperones +chaperoning +chaperons +chaplain +chaplaincies +chaplaincy +chaplains +chaplet +chaplets +chapped +chapping +chaps +chapt +chapter +chapters +char +charabanc +charabancs +character +characteristic +characteristically +characteristics +characterization +characterizations +characterize +characterized +characterizes +characterizing +characterless +characters +charade +charades +charbroil +charbroiled +charbroiling +charbroils +charcoal +charcoals +chard +chardonnay +chardonnays +charge +chargeable +charged +charger +chargers +charges +charging +charier +chariest +charily +chariness +chariot +charioteer +charioteers +chariots +charisma +charismatic +charismatics +charitable +charitableness +charitably +charities +charity +charlatan +charlatanism +charlatanry +charlatans +charm +charmed +charmer +charmers +charming +charmingly +charms +charred +charring +chars +chart +charted +charter +chartered +charterer +charterers +chartering +charters +charting +chartreuse +charts +charwoman +charwomen +chary +chase +chased +chaser +chasers +chases +chasing +chasm +chasms +chassis +chaste +chastely +chasten +chastened +chasteness +chastening +chastens +chaster +chastest +chastise +chastised +chastisement +chastisements +chastiser +chastisers +chastises +chastising +chastity +chasuble +chasubles +chat +chateau +chateaus +chateaux +chatelaine +chatelaines +chats +chatted +chattel +chattels +chatter +chatterbox +chatterboxes +chattered +chatterer +chatterers +chattering +chatters +chattier +chattiest +chattily +chattiness +chatting +chatty +chauffeur +chauffeured +chauffeuring +chauffeurs +chauvinism +chauvinist +chauvinistic +chauvinistically +chauvinists +cheap +cheapen +cheapened +cheapening +cheapens +cheaper +cheapest +cheaply +cheapness +cheapskate +cheapskates +cheat +cheated +cheater +cheaters +cheating +cheats +check +checkbook +checkbooks +checked +checker +checkerboard +checkerboards +checkered +checkering +checkers +checking +checklist +checklists +checkmate +checkmated +checkmates +checkmating +checkoff +checkoffs +checkout +checkouts +checkpoint +checkpoints +checkroom +checkrooms +checks +checkup +checkups +cheddar +cheek +cheekbone +cheekbones +cheeked +cheekier +cheekiest +cheekily +cheekiness +cheeking +cheeks +cheeky +cheep +cheeped +cheeping +cheeps +cheer +cheered +cheerer +cheerers +cheerful +cheerfuller +cheerfullest +cheerfully +cheerfulness +cheerier +cheeriest +cheerily +cheeriness +cheering +cheerio +cheerios +cheerleader +cheerleaders +cheerless +cheerlessly +cheerlessness +cheers +cheery +cheese +cheeseburger +cheeseburgers +cheesecake +cheesecakes +cheesecloth +cheeseparing +cheeses +cheesier +cheesiest +cheesiness +cheesy +cheetah +cheetahs +chef +chefs +chemical +chemically +chemicals +chemise +chemises +chemist +chemistry +chemists +chemo +chemotherapeutic +chemotherapy +chemurgy +chenille +cheque +chequer +chequers +cheques +cherish +cherished +cherishes +cherishing +cheroot +cheroots +cherries +cherry +chert +cherub +cherubic +cherubim +cherubims +cherubs +chervil +chess +chessboard +chessboards +chessman +chessmen +chest +chested +chesterfield +chesterfields +chestful +chestfuls +chestier +chestiest +chestnut +chestnuts +chests +chesty +chevalier +chevaliers +cheviot +chevron +chevrons +chew +chewed +chewer +chewers +chewier +chewiest +chewiness +chewing +chews +chewy +chiaroscuro +chic +chicane +chicaneries +chicanery +chicanes +chicer +chicest +chichi +chichis +chick +chickadee +chickadees +chicken +chickened +chickenfeed +chickenhearted +chickening +chickenpox +chickens +chickpea +chickpeas +chicks +chickweed +chicle +chicness +chicories +chicory +chid +chidden +chide +chided +chides +chiding +chidingly +chief +chiefdom +chiefer +chiefest +chiefly +chiefs +chieftain +chieftains +chieftainship +chieftainships +chiffon +chiffonier +chiffoniers +chigger +chiggers +chignon +chignons +chihuahua +chihuahuas +chilblain +chilblains +child +childbearing +childbirth +childbirths +childcare +childhood +childhoods +childish +childishly +childishness +childless +childlessness +childlike +childproof +childproofed +childproofing +childproofs +children +chile +chiles +chili +chilies +chill +chilled +chiller +chillers +chillest +chilli +chillier +chillies +chilliest +chilliness +chilling +chillingly +chillness +chills +chilly +chimaera +chimaeras +chime +chimed +chimer +chimera +chimeras +chimeric +chimerical +chimers +chimes +chiming +chimney +chimneys +chimp +chimpanzee +chimpanzees +chimps +chin +china +chinaware +chinchilla +chinchillas +chine +chines +chink +chinked +chinking +chinks +chinned +chinning +chino +chinos +chins +chinstrap +chinstraps +chintz +chintzier +chintziest +chintzy +chip +chipmunk +chipmunks +chipped +chipper +chippers +chipping +chips +chirography +chiropodist +chiropodists +chiropody +chiropractic +chiropractics +chiropractor +chiropractors +chirp +chirped +chirpier +chirpiest +chirping +chirps +chirpy +chirrup +chirruped +chirruping +chirrupped +chirrupping +chirrups +chis +chisel +chiseled +chiseler +chiselers +chiseling +chiselled +chiseller +chisellers +chiselling +chisels +chit +chitchat +chitchats +chitchatted +chitchatting +chitin +chitinous +chitlings +chitlins +chits +chitterlings +chivalrous +chivalrously +chivalrousness +chivalry +chive +chives +chlamydia +chlamydiae +chlamydias +chloral +chlordane +chloride +chlorides +chlorinate +chlorinated +chlorinates +chlorinating +chlorination +chlorine +chlorofluorocarbon +chlorofluorocarbons +chloroform +chloroformed +chloroforming +chloroforms +chlorophyl +chlorophyll +chloroplast +chloroplasts +chocaholic +chocaholics +chock +chockablock +chocked +chocking +chocks +chocoholic +chocoholics +chocolate +chocolates +chocolatey +chocolaty +choice +choicer +choices +choicest +choir +choirboy +choirboys +choirmaster +choirmasters +choirs +choke +chokecherries +chokecherry +choked +choker +chokers +chokes +choking +choler +cholera +choleric +cholesterol +chomp +chomped +chomping +chomps +choose +chooser +choosers +chooses +choosey +choosier +choosiest +choosiness +choosing +choosy +chop +chophouse +chophouses +chopped +chopper +choppered +choppering +choppers +choppier +choppiest +choppily +choppiness +chopping +choppy +chops +chopstick +chopsticks +choral +chorale +chorales +chorally +chorals +chord +chordal +chordate +chordates +chords +chore +chorea +choreograph +choreographed +choreographer +choreographers +choreographic +choreographically +choreographing +choreographs +choreography +chores +chorister +choristers +choroid +choroids +chortle +chortled +chortler +chortlers +chortles +chortling +chorus +chorused +choruses +chorusing +chorussed +chorussing +chose +chosen +chow +chowder +chowders +chowed +chowing +chows +chrism +christen +christened +christening +christenings +christens +chromatic +chromatically +chromatin +chrome +chromed +chromes +chroming +chromium +chromosomal +chromosome +chromosomes +chronic +chronically +chronicle +chronicled +chronicler +chroniclers +chronicles +chronicling +chronograph +chronographs +chronological +chronologically +chronologies +chronologist +chronologists +chronology +chronometer +chronometers +chrysalides +chrysalis +chrysalises +chrysanthemum +chrysanthemums +chub +chubbier +chubbiest +chubbiness +chubby +chubs +chuck +chucked +chuckhole +chuckholes +chucking +chuckle +chuckled +chuckles +chuckling +chucks +chug +chugged +chugging +chugs +chukka +chukkas +chum +chummed +chummier +chummiest +chummily +chumminess +chumming +chummy +chump +chumps +chums +chunk +chunkier +chunkiest +chunkiness +chunks +chunky +church +churches +churchgoer +churchgoers +churchgoing +churchman +churchmen +churchwarden +churchwardens +churchyard +churchyards +churl +churlish +churlishly +churlishness +churls +churn +churned +churner +churners +churning +churns +chute +chutes +chutney +chutzpa +chutzpah +chyme +ciao +cicada +cicadae +cicadas +cicatrice +cicatrices +cicatrix +cicatrixes +cicerone +cicerones +ciceroni +cider +ciders +cigar +cigaret +cigarets +cigarette +cigarettes +cigarillo +cigarillos +cigars +cilantro +cilia +cilium +cinch +cinched +cinches +cinching +cinchona +cinchonas +cincture +cinctures +cinder +cindered +cindering +cinders +cinema +cinemas +cinematic +cinematographer +cinematographers +cinematographic +cinematography +cinnabar +cinnamon +cipher +ciphered +ciphering +ciphers +circa +circadian +circle +circled +circles +circlet +circlets +circling +circuit +circuital +circuited +circuiting +circuitous +circuitously +circuitousness +circuitry +circuits +circuity +circular +circularity +circularize +circularized +circularizes +circularizing +circularly +circulars +circulate +circulated +circulates +circulating +circulation +circulations +circulatory +circumcise +circumcised +circumcises +circumcising +circumcision +circumcisions +circumference +circumferences +circumferential +circumflex +circumflexes +circumlocution +circumlocutions +circumnavigate +circumnavigated +circumnavigates +circumnavigating +circumnavigation +circumnavigations +circumpolar +circumscribe +circumscribed +circumscribes +circumscribing +circumscription +circumscriptions +circumspect +circumspection +circumspectly +circumstance +circumstanced +circumstances +circumstancing +circumstantial +circumstantially +circumvent +circumvented +circumventing +circumvention +circumvents +circus +circuses +cirque +cirques +cirrhosis +cirrhotic +cirrhotics +cirrus +cistern +cisterns +citadel +citadels +citation +citations +cite +cited +cites +cities +citified +citing +citizen +citizenry +citizens +citizenship +citric +citron +citronella +citrons +citrous +citrus +citruses +city +civet +civets +civic +civically +civics +civies +civil +civilian +civilians +civilisation +civilisations +civilise +civilised +civilises +civilising +civilities +civility +civilization +civilizations +civilize +civilized +civilizes +civilizing +civilly +civvies +clack +clacked +clacking +clacks +clad +cladding +claim +claimable +claimant +claimants +claimed +claimer +claimers +claiming +claims +clairvoyance +clairvoyant +clairvoyants +clam +clambake +clambakes +clamber +clambered +clamberer +clamberers +clambering +clambers +clammed +clammier +clammiest +clammily +clamminess +clamming +clammy +clamor +clamored +clamoring +clamorous +clamors +clamour +clamoured +clamouring +clamours +clamp +clampdown +clampdowns +clamped +clamping +clamps +clams +clan +clandestine +clandestinely +clang +clanged +clanging +clangor +clangorous +clangorously +clangour +clangs +clank +clanked +clanking +clanks +clannish +clannishness +clans +clansman +clansmen +clap +clapboard +clapboarded +clapboarding +clapboards +clapped +clapper +clappers +clapping +claps +claptrap +claque +claques +claret +clarets +clarification +clarifications +clarified +clarifies +clarify +clarifying +clarinet +clarinetist +clarinetists +clarinets +clarinettist +clarinettists +clarion +clarions +clarity +clash +clashed +clashes +clashing +clasp +clasped +clasping +clasps +class +classed +classes +classic +classical +classically +classicism +classicist +classicists +classics +classier +classiest +classifiable +classification +classifications +classified +classifieds +classifier +classifiers +classifies +classify +classifying +classiness +classing +classless +classmate +classmates +classroom +classrooms +classwork +classy +clatter +clattered +clattering +clatters +clausal +clause +clauses +claustrophobia +claustrophobic +clavichord +clavichords +clavicle +clavicles +clavier +claviers +claw +clawed +clawing +claws +clay +clayey +clayier +clayiest +clean +cleanable +cleaned +cleaner +cleaners +cleanest +cleaning +cleanings +cleanlier +cleanliest +cleanliness +cleanly +cleanness +cleans +cleanse +cleansed +cleanser +cleansers +cleanses +cleansing +cleanup +cleanups +clear +clearance +clearances +cleared +clearer +clearest +clearheaded +clearing +clearinghouse +clearinghouses +clearings +clearly +clearness +clears +cleat +cleats +cleavage +cleavages +cleave +cleaved +cleaver +cleavers +cleaves +cleaving +clef +clefs +cleft +clefts +clematis +clematises +clemency +clement +clemently +clench +clenched +clenches +clenching +clerestories +clerestory +clergies +clergy +clergyman +clergymen +clergywoman +clergywomen +cleric +clerical +clericalism +clerically +clerics +clerk +clerked +clerking +clerks +clerkship +clever +cleverer +cleverest +cleverly +cleverness +clevis +clevises +clew +clewed +clewing +clews +cliche +cliched +cliches +click +clicked +clicker +clickers +clicking +clicks +client +clientele +clienteles +clients +cliff +cliffhanger +cliffhangers +cliffs +climacteric +climactic +climate +climates +climatic +climatically +climatologist +climatologists +climatology +climax +climaxed +climaxes +climaxing +climb +climbable +climbed +climber +climbers +climbing +climbs +clime +climes +clinch +clinched +clincher +clinchers +clinches +clinching +cling +clinger +clingers +clingier +clingiest +clinging +clings +clingy +clinic +clinical +clinically +clinician +clinicians +clinics +clink +clinked +clinker +clinkers +clinking +clinks +cliometric +cliometrician +cliometricians +cliometrics +clip +clipboard +clipboards +clipped +clipper +clippers +clipping +clippings +clips +clipt +clique +cliques +cliquey +cliquier +cliquiest +cliquish +cliquishly +cliquishness +clitoral +clitorides +clitoris +clitorises +cloaca +cloacae +cloacas +cloak +cloaked +cloaking +cloakroom +cloakrooms +cloaks +clobber +clobbered +clobbering +clobbers +cloche +cloches +clock +clocked +clocking +clocks +clockwise +clockwork +clockworks +clod +cloddish +clodhopper +clodhoppers +clods +clog +clogged +clogging +clogs +cloisonne +cloister +cloistered +cloistering +cloisters +cloistral +clomp +clomped +clomping +clomps +clonal +clone +cloned +clones +cloning +clonk +clonked +clonking +clonks +clop +clopped +clopping +clops +close +closed +closefisted +closely +closemouthed +closeness +closeout +closeouts +closer +closes +closest +closet +closeted +closeting +closets +closeup +closeups +closing +closings +closure +closures +clot +cloth +clothe +clothed +clothes +clotheshorse +clotheshorses +clothesline +clotheslines +clothespin +clothespins +clothier +clothiers +clothing +cloths +clots +clotted +clotting +cloture +clotures +cloud +cloudburst +cloudbursts +clouded +cloudier +cloudiest +cloudiness +clouding +cloudless +clouds +cloudy +clout +clouted +clouting +clouts +clove +cloven +clover +cloverleaf +cloverleafs +cloverleaves +clovers +cloves +clown +clowned +clowning +clownish +clownishly +clownishness +clowns +cloy +cloyed +cloying +cloys +club +clubbed +clubbing +clubfeet +clubfoot +clubfooted +clubhouse +clubhouses +clubs +cluck +clucked +clucking +clucks +clue +clued +clueing +clueless +clues +cluing +clump +clumped +clumpier +clumpiest +clumping +clumps +clumpy +clumsier +clumsiest +clumsily +clumsiness +clumsy +clung +clunk +clunked +clunker +clunkers +clunkier +clunkiest +clunking +clunks +clunky +cluster +clustered +clustering +clusters +clutch +clutched +clutches +clutching +clutter +cluttered +cluttering +clutters +cnidarian +cnidarians +coach +coached +coaches +coaching +coachman +coachmen +coadjutor +coadjutors +coagulant +coagulants +coagulate +coagulated +coagulates +coagulating +coagulation +coagulator +coagulators +coal +coaled +coalesce +coalesced +coalescence +coalescent +coalesces +coalescing +coalface +coalfaces +coaling +coalition +coalitionist +coalitionists +coalitions +coals +coarse +coarsely +coarsen +coarsened +coarseness +coarsening +coarsens +coarser +coarsest +coast +coastal +coasted +coaster +coasters +coasting +coastline +coastlines +coasts +coat +coated +coating +coatings +coats +coattail +coattails +coauthor +coauthored +coauthoring +coauthors +coax +coaxed +coaxer +coaxers +coaxes +coaxial +coaxing +coaxingly +cobalt +cobble +cobbled +cobbler +cobblers +cobbles +cobblestone +cobblestones +cobbling +cobra +cobras +cobs +cobweb +cobwebbier +cobwebbiest +cobwebby +cobwebs +coca +cocain +cocaine +cocci +coccus +coccyges +coccyx +coccyxes +cochineal +cochlea +cochleae +cochlear +cochleas +cock +cockade +cockades +cockamamie +cockatoo +cockatoos +cockatrice +cockatrices +cockcrow +cockcrows +cocked +cockerel +cockerels +cockeyed +cockfight +cockfighting +cockfights +cockier +cockiest +cockily +cockiness +cocking +cockle +cockles +cockleshell +cockleshells +cockney +cockneys +cockpit +cockpits +cockroach +cockroaches +cocks +cockscomb +cockscombs +cocksucker +cocksuckers +cocksure +cocktail +cocktails +cocky +coco +cocoa +cocoanut +cocoanuts +cocoas +coconut +coconuts +cocoon +cocooned +cocooning +cocoons +cocos +coda +codas +coddle +coddled +coddles +coddling +code +coded +codeine +codependency +codependent +codependents +coder +coders +codes +codex +codfish +codfishes +codger +codgers +codices +codicil +codicils +codification +codifications +codified +codifier +codifiers +codifies +codify +codifying +coding +codpiece +codpieces +cods +coed +coeds +coeducation +coeducational +coefficient +coefficients +coelenterate +coelenterates +coequal +coequally +coequals +coerce +coerced +coercer +coercers +coerces +coercing +coercion +coercive +coeval +coevally +coevals +coexist +coexisted +coexistence +coexistent +coexisting +coexists +coextensive +coffee +coffeecake +coffeecakes +coffeehouse +coffeehouses +coffeemaker +coffeemakers +coffeepot +coffeepots +coffees +coffer +cofferdam +cofferdams +coffers +coffin +coffined +coffining +coffins +cogency +cogent +cogently +cogitate +cogitated +cogitates +cogitating +cogitation +cogitative +cogitator +cogitators +cognac +cognacs +cognate +cognates +cognition +cognitional +cognitive +cognitively +cognizable +cognizance +cognizant +cognomen +cognomens +cognomina +cognoscente +cognoscenti +cogs +cogwheel +cogwheels +cohabit +cohabitant +cohabitants +cohabitation +cohabited +cohabiting +cohabits +coheir +coheirs +cohere +cohered +coherence +coherency +coherent +coherently +coheres +cohering +cohesion +cohesive +cohesively +cohesiveness +coho +cohort +cohorts +cohos +coif +coifed +coiffed +coiffing +coiffure +coiffured +coiffures +coiffuring +coifing +coifs +coil +coiled +coiling +coils +coin +coinage +coinages +coincide +coincided +coincidence +coincidences +coincident +coincidental +coincidentally +coincides +coinciding +coined +coiner +coiners +coining +coins +coinsurance +coital +coitus +coke +coked +cokes +coking +cola +colander +colanders +colas +cold +coldblooded +colder +coldest +coldly +coldness +colds +coleslaw +coleus +coleuses +colic +colicky +coliseum +coliseums +colitis +collaborate +collaborated +collaborates +collaborating +collaboration +collaborations +collaborative +collaborator +collaborators +collage +collages +collapse +collapsed +collapses +collapsible +collapsing +collar +collarbone +collarbones +collard +collards +collared +collaring +collarless +collars +collate +collated +collateral +collateralize +collateralized +collateralizes +collateralizing +collaterally +collates +collating +collation +collations +collator +collators +colleague +colleagues +collect +collectable +collectables +collected +collectedly +collectible +collectibles +collecting +collection +collections +collective +collectively +collectives +collectivism +collectivist +collectivists +collectivization +collectivize +collectivized +collectivizes +collectivizing +collector +collectors +collects +colleen +colleens +college +colleges +collegiality +collegian +collegians +collegiate +collide +collided +collides +colliding +collie +collier +collieries +colliers +colliery +collies +collision +collisions +collocate +collocated +collocates +collocating +collocation +collocations +colloid +colloidal +colloids +colloquia +colloquial +colloquialism +colloquialisms +colloquially +colloquies +colloquium +colloquiums +colloquy +collude +colluded +colludes +colluding +collusion +collusive +cologne +colognes +colon +colonel +colonelcy +colonels +colones +colonial +colonialism +colonialist +colonialists +colonially +colonials +colonies +colonist +colonists +colonization +colonize +colonized +colonizer +colonizers +colonizes +colonizing +colonnade +colonnaded +colonnades +colons +colony +colophon +colophons +color +colorant +colorants +coloration +coloratura +coloraturas +colorblind +colorblindness +colored +coloreds +colorfast +colorfastness +colorful +colorfully +colorfulness +coloring +colorization +colorize +colorized +colorizes +colorizing +colorless +colorlessly +colorlessness +colors +colossal +colossally +colossi +colossus +colossuses +colostomies +colostomy +colostrum +colour +coloured +colouring +colours +colt +coltish +colts +columbine +columbines +column +columnar +columned +columnist +columnists +columns +coma +comaker +comakers +comas +comatose +comb +combat +combatant +combatants +combated +combating +combative +combativeness +combats +combatted +combatting +combed +comber +combers +combination +combinations +combine +combined +combiner +combiners +combines +combing +combings +combining +combo +combos +combs +combustibility +combustible +combustibles +combustion +combustive +come +comeback +comebacks +comedian +comedians +comedic +comedienne +comediennes +comedies +comedown +comedowns +comedy +comelier +comeliest +comeliness +comely +comer +comers +comes +comestible +comestibles +comet +comets +comeuppance +comeuppances +comfier +comfiest +comfit +comfits +comfort +comfortable +comfortableness +comfortably +comforted +comforter +comforters +comforting +comfortingly +comforts +comfy +comic +comical +comicality +comically +comics +coming +comings +comity +comma +command +commandant +commandants +commanded +commandeer +commandeered +commandeering +commandeers +commander +commanders +commanding +commandment +commandments +commando +commandoes +commandos +commands +commas +commemorate +commemorated +commemorates +commemorating +commemoration +commemorations +commemorative +commemorator +commemorators +commence +commenced +commencement +commencements +commences +commencing +commend +commendable +commendably +commendation +commendations +commendatory +commended +commending +commends +commensurable +commensurate +commensurately +comment +commentaries +commentary +commentate +commentated +commentates +commentating +commentator +commentators +commented +commenting +comments +commerce +commercial +commercialism +commercialization +commercialize +commercialized +commercializes +commercializing +commercially +commercials +commie +commies +commingle +commingled +commingles +commingling +commiserate +commiserated +commiserates +commiserating +commiseration +commiserations +commiserative +commissar +commissariat +commissariats +commissaries +commissars +commissary +commission +commissioned +commissioner +commissioners +commissioning +commissions +commit +commitment +commitments +commits +committal +committals +committed +committee +committeeman +committeemen +committees +committeewoman +committeewomen +committing +commode +commodes +commodious +commodiously +commodities +commodity +commodore +commodores +common +commonalty +commoner +commoners +commonest +commonly +commonness +commonplace +commonplaces +commons +commonsense +commonweal +commonwealth +commonwealths +commotion +commotions +communal +communally +commune +communed +communes +communicability +communicable +communicably +communicant +communicants +communicate +communicated +communicates +communicating +communication +communications +communicative +communicator +communicators +communing +communion +communions +communique +communiques +communism +communist +communistic +communists +communities +community +commutable +commutation +commutations +commutative +commutator +commutators +commute +commuted +commuter +commuters +commutes +commuting +comp +compact +compacted +compacter +compactest +compacting +compactly +compactness +compactor +compactors +compacts +companies +companion +companionable +companionably +companions +companionship +companionway +companionways +company +comparability +comparable +comparably +comparative +comparatively +comparatives +compare +compared +compares +comparing +comparison +comparisons +compartment +compartmental +compartmentalization +compartmentalize +compartmentalized +compartmentalizes +compartmentalizing +compartments +compass +compassed +compasses +compassing +compassion +compassionate +compassionately +compatibility +compatible +compatibles +compatibly +compatriot +compatriots +comped +compeer +compeers +compel +compelled +compelling +compellingly +compels +compendia +compendious +compendium +compendiums +compensate +compensated +compensates +compensating +compensation +compensations +compensatory +compete +competed +competence +competences +competencies +competency +competent +competently +competes +competing +competition +competitions +competitive +competitively +competitiveness +competitor +competitors +compilation +compilations +compile +compiled +compiler +compilers +compiles +compiling +comping +complacence +complacency +complacent +complacently +complain +complainant +complainants +complained +complainer +complainers +complaining +complains +complaint +complaints +complaisance +complaisant +complaisantly +complected +complement +complementary +complemented +complementing +complements +complete +completed +completely +completeness +completer +completes +completest +completing +completion +complex +complexes +complexion +complexional +complexioned +complexions +complexities +complexity +complexly +compliance +compliant +compliantly +complicate +complicated +complicatedly +complicates +complicating +complication +complications +complicit +complicity +complied +complies +compliment +complimentary +complimented +complimenting +compliments +comply +complying +component +components +comport +comported +comporting +comportment +comports +compose +composed +composedly +composer +composers +composes +composing +composite +compositely +composites +composition +compositions +compositor +compositors +compost +composted +composting +composts +composure +compote +compotes +compound +compoundable +compounded +compounding +compounds +comprehend +comprehended +comprehending +comprehends +comprehensibility +comprehensible +comprehensibly +comprehension +comprehensions +comprehensive +comprehensively +comprehensiveness +comprehensives +compress +compressed +compresses +compressible +compressing +compression +compressor +compressors +comprise +comprised +comprises +comprising +compromise +compromised +compromises +compromising +comps +comptroller +comptrollers +compulsion +compulsions +compulsive +compulsively +compulsiveness +compulsories +compulsorily +compulsory +compunction +compunctions +computation +computational +computations +compute +computed +computer +computerization +computerize +computerized +computerizes +computerizing +computers +computes +computing +comrade +comradely +comrades +comradeship +concatenate +concatenated +concatenates +concatenating +concatenation +concatenations +concave +concavely +concaveness +concavities +concavity +conceal +concealable +concealed +concealer +concealers +concealing +concealment +conceals +concede +conceded +concedes +conceding +conceit +conceited +conceitedly +conceitedness +conceits +conceivable +conceivably +conceive +conceived +conceives +conceiving +concentrate +concentrated +concentrates +concentrating +concentration +concentrations +concentric +concentrically +concept +conception +conceptional +conceptions +concepts +conceptual +conceptualization +conceptualizations +conceptualize +conceptualized +conceptualizes +conceptualizing +conceptually +concern +concerned +concerning +concerns +concert +concerted +concertedly +concerti +concertina +concertinaed +concertinaing +concertinas +concerting +concertize +concertized +concertizes +concertizing +concertmaster +concertmasters +concerto +concertos +concerts +concession +concessionaire +concessionaires +concessional +concessionary +concessions +conch +conches +conchs +concierge +concierges +conciliate +conciliated +conciliates +conciliating +conciliation +conciliator +conciliators +conciliatory +concise +concisely +conciseness +conciser +concisest +concision +conclave +conclaves +conclude +concluded +concludes +concluding +conclusion +conclusions +conclusive +conclusively +conclusiveness +concoct +concocted +concocting +concoction +concoctions +concocts +concomitant +concomitantly +concomitants +concord +concordance +concordances +concordant +concordat +concordats +concourse +concourses +concrete +concreted +concretely +concreteness +concretes +concreting +concretion +concretions +concubinage +concubine +concubines +concupiscence +concupiscent +concur +concurred +concurrence +concurrences +concurrent +concurrently +concurring +concurs +concuss +concussed +concusses +concussing +concussion +concussions +concussive +condemn +condemnation +condemnations +condemnatory +condemned +condemner +condemners +condemning +condemns +condensate +condensates +condensation +condensations +condense +condensed +condenser +condensers +condenses +condensing +condescend +condescended +condescending +condescendingly +condescends +condescension +condign +condiment +condiments +condition +conditional +conditionally +conditionals +conditioned +conditioner +conditioners +conditioning +conditions +condo +condoes +condole +condoled +condolence +condolences +condoles +condoling +condom +condominium +condominiums +condoms +condone +condoned +condones +condoning +condor +condors +condos +conduce +conduced +conduces +conducing +conducive +conduct +conductance +conducted +conductibility +conductible +conducting +conduction +conductive +conductivity +conductor +conductors +conductress +conductresses +conducts +conduit +conduits +cone +cones +coney +coneys +confab +confabbed +confabbing +confabs +confabulate +confabulated +confabulates +confabulating +confabulation +confabulations +confection +confectioner +confectioneries +confectioners +confectionery +confections +confederacies +confederacy +confederate +confederated +confederates +confederating +confederation +confederations +confer +conferee +conferees +conference +conferences +conferment +conferments +conferrable +conferral +conferred +conferrer +conferrers +conferring +confers +confess +confessed +confessedly +confesses +confessing +confession +confessional +confessionals +confessions +confessor +confessors +confetti +confidant +confidante +confidantes +confidants +confide +confided +confidence +confidences +confident +confidential +confidentiality +confidentially +confidently +confider +confiders +confides +confiding +configuration +configurations +configure +configured +configures +configuring +confine +confined +confinement +confinements +confines +confining +confirm +confirmation +confirmations +confirmed +confirming +confirms +confiscate +confiscated +confiscates +confiscating +confiscation +confiscations +confiscator +confiscators +confiscatory +conflagration +conflagrations +conflate +conflated +conflates +conflating +conflation +conflations +conflict +conflicted +conflicting +conflicts +confluence +confluences +confluent +conform +conformable +conformance +conformation +conformations +conformed +conformer +conformers +conforming +conformism +conformist +conformists +conformity +conforms +confound +confounded +confounding +confounds +confraternities +confraternity +confrere +confreres +confront +confrontation +confrontational +confrontations +confronted +confronting +confronts +confuse +confused +confusedly +confuses +confusing +confusingly +confusion +confutation +confute +confuted +confutes +confuting +conga +congaed +congaing +congas +congeal +congealed +congealing +congealment +congeals +congenial +congeniality +congenially +congenital +congenitally +conger +congeries +congers +congest +congested +congesting +congestion +congestive +congests +conglomerate +conglomerated +conglomerates +conglomerating +conglomeration +conglomerations +congrats +congratulate +congratulated +congratulates +congratulating +congratulation +congratulations +congratulatory +congregant +congregants +congregate +congregated +congregates +congregating +congregation +congregational +congregationalism +congregationalist +congregationalists +congregations +congress +congresses +congressional +congressman +congressmen +congressperson +congresspersons +congresswoman +congresswomen +congruence +congruent +congruently +congruities +congruity +congruous +conic +conical +conically +conics +conies +conifer +coniferous +conifers +conjectural +conjecture +conjectured +conjectures +conjecturing +conjoin +conjoined +conjoiner +conjoiners +conjoining +conjoins +conjoint +conjointly +conjugal +conjugally +conjugate +conjugated +conjugates +conjugating +conjugation +conjugations +conjunct +conjunction +conjunctions +conjunctiva +conjunctivae +conjunctivas +conjunctive +conjunctives +conjunctivitis +conjuncts +conjuncture +conjunctures +conjuration +conjurations +conjure +conjured +conjurer +conjurers +conjures +conjuring +conjuror +conjurors +conk +conked +conking +conks +conman +conmen +connect +connectable +connected +connecter +connecters +connectible +connecting +connection +connections +connective +connectives +connectivity +connector +connectors +connects +conned +connexion +connexions +conning +conniption +conniptions +connivance +connive +connived +conniver +connivers +connives +conniving +connoisseur +connoisseurs +connotation +connotations +connotative +connote +connoted +connotes +connoting +connubial +conquer +conquerable +conquered +conquering +conqueror +conquerors +conquers +conquest +conquests +conquistador +conquistadores +conquistadors +cons +consanguineous +consanguinity +conscience +conscienceless +consciences +conscientious +conscientiously +conscientiousness +conscious +consciously +consciousness +consciousnesses +conscript +conscripted +conscripting +conscription +conscripts +consecrate +consecrated +consecrates +consecrating +consecration +consecrations +consecutive +consecutively +consensual +consensus +consensuses +consent +consented +consenting +consents +consequence +consequences +consequent +consequential +consequentially +consequently +conservancies +conservancy +conservation +conservationism +conservationist +conservationists +conservatism +conservative +conservatively +conservatives +conservator +conservatories +conservators +conservatory +conserve +conserved +conserves +conserving +consider +considerable +considerably +considerate +considerately +considerateness +consideration +considerations +considered +considering +considers +consign +consigned +consignee +consignees +consigning +consignment +consignments +consignor +consignors +consigns +consist +consisted +consistence +consistences +consistencies +consistency +consistent +consistently +consisting +consistories +consistory +consists +consolable +consolation +consolations +consolatory +console +consoled +consoles +consolidate +consolidated +consolidates +consolidating +consolidation +consolidations +consolidator +consolidators +consoling +consolingly +consomme +consonance +consonances +consonant +consonantly +consonants +consort +consorted +consortia +consorting +consortium +consortiums +consorts +conspectus +conspectuses +conspicuous +conspicuously +conspicuousness +conspiracies +conspiracy +conspirator +conspiratorial +conspiratorially +conspirators +conspire +conspired +conspires +conspiring +constable +constables +constabularies +constabulary +constancy +constant +constantly +constants +constellation +constellations +consternation +constipate +constipated +constipates +constipating +constipation +constituencies +constituency +constituent +constituents +constitute +constituted +constitutes +constituting +constitution +constitutional +constitutionality +constitutionally +constitutionals +constitutions +constitutive +constrain +constrained +constraining +constrains +constraint +constraints +constrict +constricted +constricting +constriction +constrictions +constrictive +constrictor +constrictors +constricts +construable +construct +constructed +constructing +construction +constructional +constructionist +constructionists +constructions +constructive +constructively +constructiveness +constructor +constructors +constructs +construe +construed +construes +construing +consubstantiation +consul +consular +consulate +consulates +consuls +consulship +consult +consultancies +consultancy +consultant +consultants +consultation +consultations +consultative +consulted +consulting +consults +consumable +consumables +consume +consumed +consumer +consumerism +consumerist +consumerists +consumers +consumes +consuming +consummate +consummated +consummately +consummates +consummating +consummation +consummations +consumption +consumptive +consumptives +contact +contacted +contacting +contacts +contagion +contagions +contagious +contagiously +contagiousness +contain +containable +contained +container +containerization +containerize +containerized +containerizes +containerizing +containers +containing +containment +contains +contaminant +contaminants +contaminate +contaminated +contaminates +contaminating +contamination +contaminator +contaminators +contemn +contemned +contemning +contemns +contemplate +contemplated +contemplates +contemplating +contemplation +contemplative +contemplatives +contemporaneity +contemporaneous +contemporaneously +contemporaries +contemporary +contempt +contemptible +contemptibly +contemptuous +contemptuously +contemptuousness +contend +contended +contender +contenders +contending +contends +content +contented +contentedly +contentedness +contenting +contention +contentions +contentious +contentiously +contentiousness +contently +contentment +contents +conterminous +conterminously +contest +contestable +contestant +contestants +contested +contesting +contests +context +contexts +contextual +contextualize +contextualized +contextualizes +contextualizing +contextually +contiguity +contiguous +contiguously +continence +continent +continental +continentals +continents +contingencies +contingency +contingent +contingently +contingents +continua +continual +continually +continuance +continuances +continuation +continuations +continue +continued +continues +continuing +continuity +continuous +continuously +continuum +continuums +contort +contorted +contorting +contortion +contortionist +contortionists +contortions +contorts +contour +contoured +contouring +contours +contraband +contraception +contraceptive +contraceptives +contract +contracted +contractible +contractile +contracting +contraction +contractions +contractor +contractors +contracts +contractual +contractually +contradict +contradicted +contradicting +contradiction +contradictions +contradictory +contradicts +contradistinction +contradistinctions +contrail +contrails +contraindicate +contraindicated +contraindicates +contraindicating +contraindication +contraindications +contralto +contraltos +contraption +contraptions +contrapuntal +contrapuntally +contraries +contrariety +contrarily +contrariness +contrariwise +contrary +contrast +contrasted +contrasting +contrasts +contravene +contravened +contravenes +contravening +contravention +contraventions +contretemps +contribute +contributed +contributes +contributing +contribution +contributions +contributor +contributors +contributory +contrite +contritely +contriteness +contrition +contrivance +contrivances +contrive +contrived +contriver +contrivers +contrives +contriving +control +controllable +controlled +controller +controllers +controlling +controls +controversial +controversially +controversies +controversy +controvert +controverted +controvertible +controverting +controverts +contumacious +contumaciously +contumacy +contumelies +contumelious +contumely +contuse +contused +contuses +contusing +contusion +contusions +conundrum +conundrums +conurbation +conurbations +convalesce +convalesced +convalescence +convalescences +convalescent +convalescents +convalesces +convalescing +convection +convectional +convective +convene +convened +convener +conveners +convenes +convenience +conveniences +convenient +conveniently +convening +convent +conventicle +conventicles +convention +conventional +conventionality +conventionalize +conventionalized +conventionalizes +conventionalizing +conventionally +conventions +convents +converge +converged +convergence +convergences +convergent +converges +converging +conversant +conversation +conversational +conversationalist +conversationalists +conversationally +conversations +converse +conversed +conversely +converses +conversing +conversion +conversions +convert +converted +converter +converters +convertibility +convertible +convertibles +converting +convertor +convertors +converts +convex +convexity +convexly +convey +conveyable +conveyance +conveyances +conveyed +conveyer +conveyers +conveying +conveyor +conveyors +conveys +convict +convicted +convicting +conviction +convictions +convicts +convince +convinced +convinces +convincing +convincingly +convivial +conviviality +convivially +convocation +convocations +convoke +convoked +convokes +convoking +convoluted +convolution +convolutions +convoy +convoyed +convoying +convoys +convulse +convulsed +convulses +convulsing +convulsion +convulsions +convulsive +convulsively +cony +cooed +cooing +cook +cookbook +cookbooks +cooked +cooker +cookeries +cookers +cookery +cookie +cookies +cooking +cookout +cookouts +cooks +cookware +cookwares +cooky +cool +coolant +coolants +cooled +cooler +coolers +coolest +coolie +coolies +cooling +coolly +coolness +cools +coon +coons +coonskin +coonskins +coop +cooped +cooper +cooperage +cooperate +cooperated +cooperates +cooperating +cooperation +cooperative +cooperatively +cooperativeness +cooperatives +cooperator +cooperators +coopered +coopering +coopers +cooping +coops +coordinate +coordinated +coordinately +coordinates +coordinating +coordination +coordinator +coordinators +coos +coot +cootie +cooties +coots +copacetic +copay +cope +coped +copes +copied +copier +copiers +copies +copilot +copilots +coping +copings +copious +copiously +copiousness +copped +copper +copperhead +copperheads +copperplate +coppers +coppery +coppice +coppices +copping +copra +cops +copse +copses +copter +copters +copula +copulae +copulas +copulate +copulated +copulates +copulating +copulation +copulative +copulatives +copy +copybook +copybooks +copycat +copycats +copycatted +copycatting +copying +copyist +copyists +copyright +copyrighted +copyrighting +copyrights +copywriter +copywriters +coquetries +coquetry +coquette +coquetted +coquettes +coquetting +coquettish +coquettishly +coracle +coracles +coral +corals +corbel +corbels +cord +cordage +corded +cordial +cordiality +cordially +cordials +cordillera +cordilleras +cording +cordite +cordless +cordon +cordoned +cordoning +cordons +cordovan +cords +corduroy +corduroys +core +cored +corer +corers +cores +corespondent +corespondents +corgi +corgis +coriander +coring +cork +corked +corker +corkers +corking +corks +corkscrew +corkscrewed +corkscrewing +corkscrews +corm +cormorant +cormorants +corms +corn +cornball +cornballs +cornbread +corncob +corncobs +cornea +corneal +corneas +corned +corner +cornered +cornering +corners +cornerstone +cornerstones +cornet +cornets +cornflakes +cornflower +cornflowers +cornice +cornices +cornier +corniest +cornily +corniness +corning +cornmeal +cornrow +cornrowed +cornrowing +cornrows +corns +cornstalk +cornstalks +cornstarch +cornucopia +cornucopias +corny +corolla +corollaries +corollary +corollas +corona +coronae +coronal +coronals +coronaries +coronary +coronas +coronation +coronations +coroner +coroners +coronet +coronets +corpora +corporal +corporals +corporate +corporately +corporation +corporations +corporeal +corporeality +corporeally +corps +corpse +corpses +corpsman +corpsmen +corpulence +corpulent +corpus +corpuscle +corpuscles +corpuscular +corpuses +corral +corralled +corralling +corrals +correct +correctable +corrected +correcter +correctest +correcting +correction +correctional +corrections +corrective +correctives +correctly +correctness +corrects +correlate +correlated +correlates +correlating +correlation +correlations +correlative +correlatives +correspond +corresponded +correspondence +correspondences +correspondent +correspondents +corresponding +correspondingly +corresponds +corridor +corridors +corroborate +corroborated +corroborates +corroborating +corroboration +corroborations +corroborative +corroborator +corroborators +corroboratory +corrode +corroded +corrodes +corroding +corrosion +corrosive +corrosively +corrosives +corrugate +corrugated +corrugates +corrugating +corrugation +corrugations +corrupt +corrupted +corrupter +corruptest +corruptibility +corruptible +corrupting +corruption +corruptions +corruptly +corruptness +corrupts +corsage +corsages +corsair +corsairs +corset +corseted +corseting +corsets +cortege +corteges +cortex +cortexes +cortical +cortices +cortisone +corundum +coruscate +coruscated +coruscates +coruscating +coruscation +corvette +corvettes +cosier +cosies +cosiest +cosign +cosignatories +cosignatory +cosigned +cosigner +cosigners +cosigning +cosigns +cosily +cosine +cosines +cosiness +cosmetic +cosmetically +cosmetician +cosmeticians +cosmetics +cosmetologist +cosmetologists +cosmetology +cosmic +cosmically +cosmogonies +cosmogonist +cosmogonists +cosmogony +cosmological +cosmologies +cosmologist +cosmologists +cosmology +cosmonaut +cosmonauts +cosmopolitan +cosmopolitanism +cosmopolitans +cosmos +cosmoses +cosponsor +cosponsored +cosponsoring +cosponsors +cosset +cosseted +cosseting +cossets +cost +costar +costarred +costarring +costars +costed +costing +costlier +costliest +costliness +costly +costs +costume +costumed +costumer +costumers +costumes +costuming +cosy +cotangent +cotangents +cote +coterie +coteries +coterminous +cotes +cotillion +cotillions +cots +cottage +cottager +cottagers +cottages +cottar +cottars +cotter +cotters +cotton +cottoned +cottoning +cottonmouth +cottonmouths +cottons +cottonseed +cottonseeds +cottontail +cottontails +cottonwood +cottonwoods +cottony +cotyledon +cotyledons +couch +couched +couches +couching +cougar +cougars +cough +coughed +coughing +coughs +could +coulee +coulees +coulomb +coulombs +council +councillor +councillors +councilman +councilmen +councilor +councilors +councilperson +councilpersons +councils +councilwoman +councilwomen +counsel +counseled +counseling +counselled +counselling +counsellor +counsellors +counselor +counselors +counsels +count +countable +countdown +countdowns +counted +countenance +countenanced +countenances +countenancing +counter +counteract +counteracted +counteracting +counteraction +counteractions +counteractive +counteracts +counterattack +counterattacked +counterattacking +counterattacks +counterbalance +counterbalanced +counterbalances +counterbalancing +counterclaim +counterclaimed +counterclaiming +counterclaims +counterclockwise +counterculture +countered +counterespionage +counterfeit +counterfeited +counterfeiter +counterfeiters +counterfeiting +counterfeits +counterfoil +counterfoils +countering +counterinsurgencies +counterinsurgency +counterintelligence +counterman +countermand +countermanded +countermanding +countermands +countermeasure +countermeasures +countermen +counteroffensive +counteroffensives +counteroffer +counteroffered +counteroffering +counteroffers +counterpane +counterpanes +counterpart +counterparts +counterpoint +counterpoints +counterpoise +counterpoised +counterpoises +counterpoising +counterproductive +counterrevolution +counterrevolutionaries +counterrevolutionary +counterrevolutions +counters +countersank +countersign +countersignature +countersignatures +countersigned +countersigning +countersigns +countersink +countersinking +countersinks +counterspies +counterspy +countersunk +countertenor +countertenors +countervail +countervailed +countervailing +countervails +counterweight +counterweights +countess +countesses +counties +counting +countless +countries +countrified +country +countryman +countrymen +countryside +countrysides +countrywoman +countrywomen +counts +county +coup +coupe +coupes +couple +coupled +couples +couplet +couplets +coupling +couplings +coupon +coupons +coups +courage +courageous +courageously +courageousness +courier +couriered +couriering +couriers +course +coursed +courser +coursers +courses +coursing +court +courted +courteous +courteously +courteousness +courtesan +courtesans +courtesies +courtesy +courthouse +courthouses +courtier +courtiers +courting +courtlier +courtliest +courtliness +courtly +courtroom +courtrooms +courts +courtship +courtships +courtyard +courtyards +couscous +cousin +cousins +couture +couturier +couturiers +cove +coven +covenant +covenanted +covenanting +covenants +covens +cover +coverage +coverall +coveralls +covered +covering +coverings +coverlet +coverlets +covers +covert +covertly +covertness +coverts +coverup +coverups +coves +covet +coveted +coveting +covetous +covetously +covetousness +covets +covey +coveys +coward +cowardice +cowardliness +cowardly +cowards +cowbell +cowbells +cowbird +cowbirds +cowboy +cowboys +cowcatcher +cowcatchers +cowed +cower +cowered +cowering +cowers +cowgirl +cowgirls +cowhand +cowhands +cowherd +cowherds +cowhide +cowhides +cowing +cowl +cowlick +cowlicks +cowling +cowlings +cowls +cowman +cowmen +coworker +coworkers +cowpoke +cowpokes +cowpox +cowpuncher +cowpunchers +cowrie +cowries +cows +cowslip +cowslips +coxcomb +coxcombs +coxswain +coxswains +coyer +coyest +coyly +coyness +coyote +coyotes +coypu +coypus +cozen +cozenage +cozened +cozening +cozens +cozier +cozies +coziest +cozily +coziness +cozy +crab +crabbed +crabber +crabbers +crabbier +crabbiest +crabbily +crabbiness +crabbing +crabby +crabgrass +crablike +crabs +crack +crackdown +crackdowns +cracked +cracker +crackerjack +crackerjacks +crackers +crackhead +crackheads +cracking +crackle +crackled +crackles +crackling +cracklings +crackly +crackpot +crackpots +cracks +crackup +crackups +cradle +cradled +cradles +cradling +craft +crafted +craftier +craftiest +craftily +craftiness +crafting +crafts +craftsman +craftsmanship +craftsmen +craftswoman +craftswomen +crafty +crag +craggier +craggiest +cragginess +craggy +crags +cram +crammed +cramming +cramp +cramped +cramping +crampon +crampons +cramps +crams +cranberries +cranberry +crane +craned +cranes +crania +cranial +craning +cranium +craniums +crank +crankcase +crankcases +cranked +crankier +crankiest +crankily +crankiness +cranking +cranks +crankshaft +crankshafts +cranky +crannied +crannies +cranny +crap +crape +crapes +crapped +crappie +crappier +crappies +crappiest +crapping +crappy +craps +crapshooter +crapshooters +crash +crashed +crashes +crashing +crass +crasser +crassest +crassly +crassness +crate +crated +crater +cratered +cratering +craters +crates +crating +cravat +cravats +crave +craved +craven +cravenly +cravenness +cravens +craves +craving +cravings +craw +crawdad +crawdads +crawfish +crawfishes +crawl +crawled +crawler +crawlers +crawlier +crawlies +crawliest +crawling +crawls +crawlspace +crawlspaces +crawly +craws +crayfish +crayfishes +crayon +crayoned +crayoning +crayons +craze +crazed +crazes +crazier +crazies +craziest +crazily +craziness +crazing +crazy +creak +creaked +creakier +creakiest +creakily +creakiness +creaking +creaks +creaky +cream +creamed +creamer +creameries +creamers +creamery +creamier +creamiest +creamily +creaminess +creaming +creams +creamy +crease +creased +creases +creasing +create +created +creates +creating +creation +creationism +creationist +creationists +creations +creative +creatively +creativeness +creatives +creativity +creator +creators +creature +creatures +creche +creches +credence +credential +credentials +credenza +credenzas +credibility +credible +credibly +credit +creditable +creditably +credited +crediting +creditor +creditors +credits +credo +credos +credulity +credulous +credulously +credulousness +creed +creeds +creek +creeks +creel +creels +creep +creeped +creeper +creepers +creepier +creepiest +creepily +creepiness +creeping +creeps +creepy +cremains +cremate +cremated +cremates +cremating +cremation +cremations +crematoria +crematories +crematorium +crematoriums +crematory +creme +cremes +crenelate +crenelated +crenelates +crenelating +crenelation +crenelations +crenellate +crenellated +crenellates +crenellating +crenellation +crenellations +creole +creoles +creosote +creosoted +creosotes +creosoting +crepe +crepes +crept +crescendi +crescendo +crescendos +crescent +crescents +cress +crest +crested +crestfallen +cresting +crestless +crests +cretaceous +cretin +cretinism +cretinous +cretins +cretonne +crevasse +crevasses +crevice +crevices +crew +crewed +crewel +crewelwork +crewing +crewman +crewmen +crews +crib +cribbage +cribbed +cribber +cribbers +cribbing +cribs +crick +cricked +cricket +cricketer +cricketers +crickets +cricking +cricks +cried +crier +criers +cries +crime +crimes +criminal +criminality +criminally +criminals +criminologist +criminologists +criminology +crimp +crimped +crimping +crimps +crimson +crimsoned +crimsoning +crimsons +cringe +cringed +cringes +cringing +crinkle +crinkled +crinkles +crinklier +crinkliest +crinkling +crinkly +crinoline +crinolines +cripple +crippled +crippler +cripplers +cripples +crippling +crises +crisis +crisp +crisped +crisper +crispest +crispier +crispiest +crispiness +crisping +crisply +crispness +crisps +crispy +crisscross +crisscrossed +crisscrosses +crisscrossing +criteria +criterion +criterions +critic +critical +critically +criticism +criticisms +criticize +criticized +criticizer +criticizers +criticizes +criticizing +critics +critique +critiqued +critiques +critiquing +critter +critters +croak +croaked +croakier +croakiest +croaking +croaks +croaky +crochet +crocheted +crocheter +crocheters +crocheting +crochets +croci +crock +crocked +crockery +crocks +crocodile +crocodiles +crocus +crocuses +croissant +croissants +crone +crones +cronies +crony +cronyism +crook +crooked +crookeder +crookedest +crookedly +crookedness +crooking +crookneck +crooknecks +crooks +croon +crooned +crooner +crooners +crooning +croons +crop +cropland +croplands +cropped +cropper +croppers +cropping +crops +croquet +croquette +croquettes +crosier +crosiers +cross +crossbar +crossbars +crossbeam +crossbeams +crossbones +crossbow +crossbowman +crossbowmen +crossbows +crossbred +crossbreed +crossbreeding +crossbreeds +crosscheck +crosschecked +crosschecking +crosschecks +crosscurrent +crosscurrents +crosscut +crosscuts +crosscutting +crossed +crosser +crosses +crossest +crossfire +crossfires +crosshatch +crosshatched +crosshatches +crosshatching +crossing +crossings +crossly +crossness +crossover +crossovers +crosspatch +crosspatches +crosspiece +crosspieces +crossroad +crossroads +crosstown +crosswalk +crosswalks +crossways +crosswind +crosswinds +crosswise +crossword +crosswords +crotch +crotches +crotchet +crotchets +crotchety +crouch +crouched +crouches +crouching +croup +croupier +croupiers +croupiest +croupy +crouton +croutons +crow +crowbar +crowbars +crowd +crowded +crowding +crowds +crowed +crowfeet +crowfoot +crowfoots +crowing +crown +crowned +crowning +crowns +crows +crozier +croziers +cruces +crucial +crucially +crucible +crucibles +crucified +crucifies +crucifix +crucifixes +crucifixion +crucifixions +cruciform +cruciforms +crucify +crucifying +crud +cruddier +cruddiest +cruddy +crude +crudely +crudeness +cruder +crudest +crudites +crudities +crudity +cruel +crueler +cruelest +crueller +cruellest +cruelly +cruelness +cruelties +cruelty +cruet +cruets +cruise +cruised +cruiser +cruisers +cruises +cruising +cruller +crullers +crumb +crumbed +crumbier +crumbiest +crumbing +crumble +crumbled +crumbles +crumblier +crumbliest +crumbliness +crumbling +crumbly +crumbs +crumby +crummier +crummiest +crumminess +crummy +crumpet +crumpets +crumple +crumpled +crumples +crumpling +crunch +crunched +crunches +crunchier +crunchiest +crunchiness +crunching +crunchy +crupper +cruppers +crusade +crusaded +crusader +crusaders +crusades +crusading +cruse +cruses +crush +crushed +crusher +crushers +crushes +crushing +crust +crustacean +crustaceans +crustal +crusted +crustier +crustiest +crustily +crustiness +crusting +crusts +crusty +crutch +crutches +crux +cruxes +crybabies +crybaby +crying +cryogenic +cryogenics +cryosurgery +crypt +cryptic +cryptically +cryptogram +cryptograms +cryptographer +cryptographers +cryptography +crypts +crystal +crystalline +crystallization +crystallize +crystallized +crystallizes +crystallizing +crystals +cubbyhole +cubbyholes +cube +cubed +cuber +cubers +cubes +cubic +cubical +cubicle +cubicles +cubing +cubism +cubist +cubists +cubit +cubits +cubs +cuckold +cuckolded +cuckolding +cuckoldry +cuckolds +cuckoo +cuckoos +cucumber +cucumbers +cuddle +cuddled +cuddles +cuddlier +cuddliest +cuddling +cuddly +cudgel +cudgeled +cudgeling +cudgelled +cudgelling +cudgels +cuds +cued +cueing +cues +cuff +cuffed +cuffing +cufflink +cufflinks +cuffs +cuing +cuisine +cuisines +culinary +cull +culled +cullender +cullenders +culling +culls +culminate +culminated +culminates +culminating +culmination +culminations +culotte +culottes +culpability +culpable +culpably +culprit +culprits +cult +cultism +cultist +cultists +cultivable +cultivatable +cultivate +cultivated +cultivates +cultivating +cultivation +cultivator +cultivators +cults +cultural +culturally +culture +cultured +cultures +culturing +culvert +culverts +cumber +cumbered +cumbering +cumbers +cumbersome +cumbersomeness +cumbrous +cumin +cummed +cummerbund +cummerbunds +cumming +cumquat +cumquats +cums +cumulative +cumulatively +cumuli +cumulonimbi +cumulonimbus +cumulonimbuses +cumulus +cuneiform +cunnilingus +cunning +cunninger +cunningest +cunningly +cunt +cunts +cupboard +cupboards +cupcake +cupcakes +cupful +cupfuls +cupid +cupidity +cupids +cupola +cupolaed +cupolas +cupped +cupping +cupric +cups +cupsful +curability +curable +curacies +curacy +curare +curate +curated +curates +curating +curative +curatives +curator +curatorial +curators +curb +curbed +curbing +curbs +curbstone +curbstones +curd +curdle +curdled +curdles +curdling +curds +cure +cured +curer +curers +cures +curettage +curfew +curfews +curia +curiae +curie +curies +curing +curio +curios +curiosities +curiosity +curious +curiously +curiousness +curium +curl +curled +curler +curlers +curlew +curlews +curlicue +curlicued +curlicues +curlicuing +curlier +curliest +curliness +curling +curls +curly +curlycue +curlycues +curmudgeon +curmudgeonly +curmudgeons +currant +currants +currencies +currency +current +currently +currents +curricula +curricular +curriculum +curriculums +curried +curries +curry +currycomb +currycombed +currycombing +currycombs +currying +curs +curse +cursed +cursedly +curses +cursing +cursive +cursively +cursor +cursorily +cursoriness +cursors +cursory +curst +curt +curtail +curtailed +curtailing +curtailment +curtailments +curtails +curtain +curtained +curtaining +curtains +curter +curtest +curtly +curtness +curtsey +curtseyed +curtseying +curtseys +curtsied +curtsies +curtsy +curtsying +curvaceous +curvaceousness +curvacious +curvature +curvatures +curve +curved +curves +curvier +curviest +curving +curvy +cushier +cushiest +cushion +cushioned +cushioning +cushions +cushy +cusp +cuspid +cuspidor +cuspidors +cuspids +cusps +cuss +cussed +cusses +cussing +custard +custards +custodial +custodian +custodians +custodianship +custody +custom +customarily +customary +customer +customers +customhouse +customhouses +customization +customize +customized +customizes +customizing +customs +cutaneous +cutaway +cutaways +cutback +cutbacks +cute +cutely +cuteness +cuter +cutesie +cutesier +cutesiest +cutest +cutesy +cuticle +cuticles +cutie +cuties +cutlas +cutlases +cutlass +cutlasses +cutler +cutlers +cutlery +cutlet +cutlets +cutoff +cutoffs +cutout +cutouts +cuts +cutter +cutters +cutthroat +cutthroats +cutting +cuttingly +cuttings +cuttlefish +cuttlefishes +cutup +cutups +cutworm +cutworms +cyan +cyanide +cybernetic +cybernetics +cyberpunk +cyberpunks +cyberspace +cyborg +cyborgs +cyclamen +cyclamens +cycle +cycled +cycles +cyclic +cyclical +cyclically +cycling +cyclist +cyclists +cyclometer +cyclometers +cyclone +cyclones +cyclonic +cyclopaedia +cyclopaedias +cyclopedia +cyclopedias +cyclopes +cyclops +cyclotron +cyclotrons +cyder +cyders +cygnet +cygnets +cylinder +cylinders +cylindrical +cymbal +cymbalist +cymbalists +cymbals +cynic +cynical +cynically +cynicism +cynics +cynosure +cynosures +cypher +cyphered +cyphering +cyphers +cypress +cypresses +cyst +cystic +cysts +cytologist +cytologists +cytology +cytoplasm +cytoplasmic +cytosine +czar +czarina +czarinas +czarist +czarists +czars +dabbed +dabber +dabbers +dabbing +dabble +dabbled +dabbler +dabblers +dabbles +dabbling +dabs +dace +daces +dacha +dachas +dachshund +dachshunds +dactyl +dactylic +dactylics +dactyls +dadaism +dadaist +dadaists +daddies +daddy +dado +dadoes +dados +dads +daemon +daemonic +daemons +daffier +daffiest +daffiness +daffodil +daffodils +daffy +daft +dafter +daftest +daftly +daftness +dagger +daggers +daguerreotype +daguerreotyped +daguerreotypes +daguerreotyping +dahlia +dahlias +dailies +dailiness +daily +daintier +dainties +daintiest +daintily +daintiness +dainty +daiquiri +daiquiris +dairies +dairy +dairying +dairymaid +dairymaids +dairyman +dairymen +dairywoman +dairywomen +dais +daises +daisies +daisy +dale +dales +dalliance +dalliances +dallied +dallier +dalliers +dallies +dally +dallying +dalmatian +dalmatians +damage +damageable +damaged +damages +damaging +damask +damasked +damasking +damasks +dame +dames +dammed +damming +dammit +damn +damnable +damnably +damnation +damndest +damned +damnedest +damning +damns +damp +damped +dampen +dampened +dampener +dampeners +dampening +dampens +damper +dampers +dampest +damping +damply +dampness +damps +dams +damsel +damselflies +damselfly +damsels +damson +damsons +dance +danced +dancer +dancers +dances +dancing +dandelion +dandelions +dander +dandier +dandies +dandiest +dandified +dandifies +dandify +dandifying +dandle +dandled +dandles +dandling +dandruff +dandy +dang +danged +danger +dangerous +dangerously +dangers +danging +dangle +dangled +dangler +danglers +dangles +dangling +dangs +danish +danishes +dank +danker +dankest +dankly +dankness +danseuse +danseuses +dapper +dapperer +dapperest +dapple +dappled +dapples +dappling +dare +dared +daredevil +daredevilry +daredevils +darer +darers +dares +daresay +daring +daringly +dark +darken +darkened +darkener +darkeners +darkening +darkens +darker +darkest +darkly +darkness +darkroom +darkrooms +darling +darlingest +darlings +darn +darned +darneder +darnedest +darner +darners +darning +darns +dart +dartboard +dartboards +darted +darter +darters +darting +darts +dash +dashboard +dashboards +dashed +dasher +dashers +dashes +dashiki +dashikis +dashing +dashingly +dastard +dastardliness +dastardly +dastards +data +databank +databanks +database +databases +date +dated +dateless +dateline +datelined +datelines +datelining +dater +daters +dates +dating +dative +datives +datum +daub +daubed +dauber +daubers +daubing +daubs +daughter +daughterly +daughters +daunt +daunted +daunting +dauntingly +dauntless +dauntlessly +dauntlessness +daunts +dauphin +dauphins +davenport +davenports +davit +davits +dawdle +dawdled +dawdler +dawdlers +dawdles +dawdling +dawn +dawned +dawning +dawns +daybed +daybeds +daybreak +daycare +daydream +daydreamed +daydreamer +daydreamers +daydreaming +daydreams +daydreamt +daylight +daylights +days +daytime +daze +dazed +dazedly +dazes +dazing +dazzle +dazzled +dazzler +dazzlers +dazzles +dazzling +dazzlingly +deacon +deaconess +deaconesses +deacons +deactivate +deactivated +deactivates +deactivating +deactivation +dead +deadbeat +deadbeats +deadbolt +deadbolts +deaden +deadened +deadening +deadens +deader +deadest +deadlier +deadliest +deadline +deadlines +deadliness +deadlock +deadlocked +deadlocking +deadlocks +deadly +deadpan +deadpanned +deadpanning +deadpans +deadweight +deadweights +deadwood +deaf +deafen +deafened +deafening +deafeningly +deafens +deafer +deafest +deafness +deal +dealer +dealers +dealership +dealerships +dealing +dealings +deals +dealt +dean +deaneries +deanery +deans +deanship +dear +dearer +dearest +dearie +dearies +dearly +dearness +dears +dearth +dearths +deary +death +deathbed +deathbeds +deathblow +deathblows +deathless +deathlessly +deathlike +deathly +deaths +deathtrap +deathtraps +deathwatch +deathwatches +debacle +debacles +debar +debark +debarkation +debarked +debarking +debarks +debarment +debarred +debarring +debars +debase +debased +debasement +debasements +debases +debasing +debatable +debate +debated +debater +debaters +debates +debating +debauch +debauched +debauchee +debauchees +debaucheries +debauchery +debauches +debauching +debenture +debentures +debilitate +debilitated +debilitates +debilitating +debilitation +debilities +debility +debit +debited +debiting +debits +debonair +debonaire +debonairly +debonairness +debouch +debouched +debouches +debouching +debrief +debriefed +debriefing +debriefings +debriefs +debris +debs +debt +debtor +debtors +debts +debug +debugged +debugging +debugs +debunk +debunked +debunking +debunks +debut +debutante +debutantes +debuted +debuting +debuts +decade +decadence +decadency +decadent +decadently +decadents +decades +decaf +decaffeinate +decaffeinated +decaffeinates +decaffeinating +decagon +decagons +decal +decals +decamp +decamped +decamping +decampment +decamps +decant +decanted +decanter +decanters +decanting +decants +decapitate +decapitated +decapitates +decapitating +decapitation +decapitations +decapitator +decapitators +decathlon +decathlons +decay +decayed +decaying +decays +decease +deceased +deceases +deceasing +decedent +decedents +deceit +deceitful +deceitfully +deceitfulness +deceits +deceive +deceived +deceiver +deceivers +deceives +deceiving +deceivingly +decelerate +decelerated +decelerates +decelerating +deceleration +decelerator +decelerators +decencies +decency +decennial +decennials +decent +decently +decentralization +decentralize +decentralized +decentralizes +decentralizing +deception +deceptions +deceptive +deceptively +deceptiveness +decibel +decibels +decidable +decide +decided +decidedly +decides +deciding +deciduous +deciliter +deciliters +decimal +decimals +decimate +decimated +decimates +decimating +decimation +decimeter +decimeters +decipher +decipherable +deciphered +deciphering +deciphers +decision +decisions +decisive +decisively +decisiveness +deck +decked +deckhand +deckhands +decking +decks +declaim +declaimed +declaimer +declaimers +declaiming +declaims +declamation +declamations +declamatory +declarable +declaration +declarations +declarative +declaratory +declare +declared +declarer +declarers +declares +declaring +declassification +declassified +declassifies +declassify +declassifying +declension +declensions +declination +decline +declined +decliner +decliners +declines +declining +declivities +declivity +decode +decoded +decoder +decoders +decodes +decoding +decolletage +decolletages +decollete +decolonization +decolonize +decolonized +decolonizes +decolonizing +decommission +decommissioned +decommissioning +decommissions +decompose +decomposed +decomposes +decomposing +decomposition +decompress +decompressed +decompresses +decompressing +decompression +decongestant +decongestants +deconstruction +deconstructions +decontaminate +decontaminated +decontaminates +decontaminating +decontamination +decontrol +decontrolled +decontrolling +decontrols +decor +decorate +decorated +decorates +decorating +decoration +decorations +decorative +decoratively +decorator +decorators +decorous +decorously +decorousness +decors +decorum +decoupage +decoupaged +decoupages +decoupaging +decoy +decoyed +decoying +decoys +decrease +decreased +decreases +decreasing +decreasingly +decree +decreed +decreeing +decrees +decrepit +decrepitude +decrescendi +decrescendo +decrescendos +decried +decries +decriminalization +decriminalize +decriminalized +decriminalizes +decriminalizing +decry +decrying +dedicate +dedicated +dedicates +dedicating +dedication +dedications +dedicator +dedicators +dedicatory +deduce +deduced +deduces +deducible +deducing +deduct +deducted +deductible +deductibles +deducting +deduction +deductions +deductive +deductively +deducts +deed +deeded +deeding +deeds +deejay +deejays +deem +deemed +deeming +deems +deep +deepen +deepened +deepening +deepens +deeper +deepest +deeply +deepness +deeps +deer +deers +deerskin +deescalate +deescalated +deescalates +deescalating +deescalation +deface +defaced +defacement +defacer +defacers +defaces +defacing +defalcate +defalcated +defalcates +defalcating +defalcation +defalcations +defamation +defamatory +defame +defamed +defamer +defamers +defames +defaming +default +defaulted +defaulter +defaulters +defaulting +defaults +defeat +defeated +defeater +defeaters +defeating +defeatism +defeatist +defeatists +defeats +defecate +defecated +defecates +defecating +defecation +defect +defected +defecting +defection +defections +defective +defectively +defectiveness +defectives +defector +defectors +defects +defence +defences +defend +defendant +defendants +defended +defender +defenders +defending +defends +defense +defensed +defenseless +defenselessly +defenselessness +defenses +defensible +defensibly +defensing +defensive +defensively +defensiveness +defer +deference +deferential +deferentially +deferment +deferments +deferral +deferrals +deferred +deferring +defers +deffer +deffest +defiance +defiant +defiantly +deficiencies +deficiency +deficient +deficit +deficits +defied +defies +defile +defiled +defilement +defiler +defilers +defiles +defiling +definable +define +defined +definer +definers +defines +defining +definite +definitely +definiteness +definition +definitions +definitive +definitively +deflate +deflated +deflates +deflating +deflation +deflationary +deflect +deflected +deflecting +deflection +deflections +deflective +deflector +deflectors +deflects +deflower +deflowered +deflowering +deflowers +defog +defogged +defogger +defoggers +defogging +defogs +defoliant +defoliants +defoliate +defoliated +defoliates +defoliating +defoliation +defoliator +defoliators +deforest +deforestation +deforested +deforesting +deforests +deform +deformation +deformations +deformed +deforming +deformities +deformity +deforms +defraud +defrauded +defrauder +defrauders +defrauding +defrauds +defray +defrayal +defrayed +defraying +defrays +defrock +defrocked +defrocking +defrocks +defrost +defrosted +defroster +defrosters +defrosting +defrosts +deft +defter +deftest +deftly +deftness +defunct +defuse +defused +defuses +defusing +defy +defying +degas +degases +degassed +degassing +degeneracy +degenerate +degenerated +degenerates +degenerating +degeneration +degenerative +degradable +degradation +degrade +degraded +degrades +degrading +degree +degrees +dehumanization +dehumanize +dehumanized +dehumanizes +dehumanizing +dehumidified +dehumidifier +dehumidifiers +dehumidifies +dehumidify +dehumidifying +dehydrate +dehydrated +dehydrates +dehydrating +dehydration +dehydrator +dehydrators +dehydrogenate +dehydrogenated +dehydrogenates +dehydrogenating +deice +deiced +deicer +deicers +deices +deicing +deification +deified +deifies +deify +deifying +deign +deigned +deigning +deigns +deism +deist +deistic +deists +deities +deity +deject +dejected +dejectedly +dejecting +dejection +dejects +delay +delayed +delayer +delayers +delaying +delays +delectable +delectably +delectation +delegate +delegated +delegates +delegating +delegation +delegations +delete +deleted +deleterious +deletes +deleting +deletion +deletions +delft +delftware +deli +deliberate +deliberated +deliberately +deliberateness +deliberates +deliberating +deliberation +deliberations +deliberative +delicacies +delicacy +delicate +delicately +delicateness +delicatessen +delicatessens +delicious +deliciously +deliciousness +delight +delighted +delightedly +delightful +delightfully +delighting +delights +delimit +delimitation +delimited +delimiting +delimits +delineate +delineated +delineates +delineating +delineation +delineations +delinquencies +delinquency +delinquent +delinquently +delinquents +deliquesce +deliquesced +deliquescent +deliquesces +deliquescing +deliria +delirious +deliriously +deliriousness +delirium +deliriums +delis +deliver +deliverance +delivered +deliverer +deliverers +deliveries +delivering +delivers +delivery +deliveryman +deliverymen +dell +dells +delouse +deloused +delouses +delousing +delphinia +delphinium +delphiniums +delta +deltas +delude +deluded +deludes +deluding +deluge +deluged +deluges +deluging +delusion +delusional +delusions +delusive +delusively +deluxe +delve +delved +delver +delvers +delves +delving +demagnetization +demagnetize +demagnetized +demagnetizes +demagnetizing +demagog +demagogic +demagogs +demagogue +demagoguery +demagogues +demagogy +demand +demanded +demanding +demands +demarcate +demarcated +demarcates +demarcating +demarcation +demean +demeaned +demeaning +demeanor +demeanour +demeans +demented +dementedly +dementia +demerit +demerits +demesne +demesnes +demigod +demigoddess +demigoddesses +demigods +demijohn +demijohns +demilitarization +demilitarize +demilitarized +demilitarizes +demilitarizing +demimondaine +demimondaines +demimonde +demise +demised +demises +demising +demitasse +demitasses +demo +demobilization +demobilize +demobilized +demobilizes +demobilizing +democracies +democracy +democrat +democratic +democratically +democratization +democratize +democratized +democratizes +democratizing +democrats +demode +demodulate +demodulated +demodulates +demodulating +demodulation +demographer +demographers +demographic +demographically +demographics +demography +demolish +demolished +demolishes +demolishing +demolition +demolitions +demon +demonetization +demonetize +demonetized +demonetizes +demonetizing +demoniac +demoniacal +demoniacally +demonic +demonologies +demonology +demons +demonstrable +demonstrably +demonstrate +demonstrated +demonstrates +demonstrating +demonstration +demonstrations +demonstrative +demonstratively +demonstrativeness +demonstratives +demonstrator +demonstrators +demoralization +demoralize +demoralized +demoralizes +demoralizing +demos +demote +demoted +demotes +demotic +demoting +demotion +demotions +demulcent +demulcents +demur +demure +demurely +demureness +demurer +demurest +demurral +demurrals +demurred +demurrer +demurrers +demurring +demurs +demystification +demystified +demystifies +demystify +demystifying +denationalize +denationalized +denationalizes +denationalizing +denature +denatured +denatures +denaturing +dendrite +dendrites +dengue +deniable +denial +denials +denied +denier +deniers +denies +denigrate +denigrated +denigrates +denigrating +denigration +denim +denims +denizen +denizens +denominate +denominated +denominates +denominating +denomination +denominational +denominations +denominator +denominators +denotation +denotations +denotative +denote +denoted +denotes +denoting +denouement +denouements +denounce +denounced +denouncement +denouncements +denounces +denouncing +dens +dense +densely +denseness +denser +densest +densities +density +dent +dental +dentally +dented +dentifrice +dentifrices +dentin +dentine +denting +dentist +dentistry +dentists +dentition +dents +denture +dentures +denuclearize +denuclearized +denuclearizes +denuclearizing +denudation +denude +denuded +denudes +denuding +denunciation +denunciations +deny +denying +deodorant +deodorants +deodorization +deodorize +deodorized +deodorizer +deodorizers +deodorizes +deodorizing +depart +departed +departing +department +departmental +departmentalization +departmentalize +departmentalized +departmentalizes +departmentalizing +departmentally +departments +departs +departure +departures +depend +dependability +dependable +dependably +dependance +dependant +dependants +depended +dependence +dependencies +dependency +dependent +dependently +dependents +depending +depends +depersonalize +depersonalized +depersonalizes +depersonalizing +depict +depicted +depicting +depiction +depictions +depicts +depilatories +depilatory +deplane +deplaned +deplanes +deplaning +deplete +depleted +depletes +depleting +depletion +deplorable +deplorably +deplore +deplored +deplores +deploring +deploy +deployed +deploying +deployment +deployments +deploys +depolarization +depolarize +depolarized +depolarizes +depolarizing +depoliticize +depoliticized +depoliticizes +depoliticizing +deponent +deponents +depopulate +depopulated +depopulates +depopulating +depopulation +deport +deportation +deportations +deported +deportee +deportees +deporting +deportment +deports +depose +deposed +deposes +deposing +deposit +deposited +depositing +deposition +depositions +depositor +depositories +depositors +depository +deposits +depot +depots +deprave +depraved +depraves +depraving +depravities +depravity +deprecate +deprecated +deprecates +deprecating +deprecation +deprecatory +depreciate +depreciated +depreciates +depreciating +depreciation +depredation +depredations +depress +depressant +depressants +depressed +depresses +depressing +depressingly +depression +depressions +depressive +depressives +depressor +depressors +depressurize +depressurized +depressurizes +depressurizing +deprivation +deprivations +deprive +deprived +deprives +depriving +deprogram +deprogramed +deprograming +deprogrammed +deprogramming +deprograms +depth +depths +deputation +deputations +depute +deputed +deputes +deputies +deputing +deputize +deputized +deputizes +deputizing +deputy +derail +derailed +derailing +derailleur +derailleurs +derailment +derailments +derails +derange +deranged +derangement +deranges +deranging +derbies +derby +deregulate +deregulated +deregulates +deregulating +deregulation +derelict +dereliction +derelicts +deride +derided +derides +deriding +derision +derisive +derisively +derisiveness +derisory +derivation +derivations +derivative +derivatives +derive +derived +derives +deriving +dermal +dermatitis +dermatological +dermatologist +dermatologists +dermatology +dermis +derogate +derogated +derogates +derogating +derogation +derogatorily +derogatory +derrick +derricks +derriere +derrieres +derringer +derringers +dervish +dervishes +desalinate +desalinated +desalinates +desalinating +desalination +desalinization +desalinize +desalinized +desalinizes +desalinizing +desalt +desalted +desalting +desalts +descant +descanted +descanting +descants +descend +descendant +descendants +descended +descendent +descendents +descending +descends +descent +descents +describable +describe +described +describer +describers +describes +describing +descried +descries +description +descriptions +descriptive +descriptively +descriptiveness +descry +descrying +desecrate +desecrated +desecrates +desecrating +desecration +desegregate +desegregated +desegregates +desegregating +desegregation +desensitization +desensitize +desensitized +desensitizes +desensitizing +desert +deserted +deserter +deserters +deserting +desertion +desertions +deserts +deserve +deserved +deservedly +deserves +deserving +deshabille +desiccant +desiccants +desiccate +desiccated +desiccates +desiccating +desiccation +desiccator +desiccators +desiderata +desideratum +design +designate +designated +designates +designating +designation +designations +designed +designer +designers +designing +designs +desirability +desirable +desirableness +desirably +desire +desired +desires +desiring +desirous +desist +desisted +desisting +desists +desk +desks +desktop +desktops +desolate +desolated +desolately +desolateness +desolates +desolating +desolation +despair +despaired +despairing +despairingly +despairs +despatch +despatched +despatches +despatching +desperado +desperadoes +desperados +desperate +desperately +desperateness +desperation +despicable +despicably +despise +despised +despises +despising +despite +despoil +despoiled +despoiler +despoilers +despoiling +despoilment +despoils +despoliation +despondence +despondency +despondent +despondently +despot +despotic +despotically +despotism +despots +dessert +desserts +destabilization +destabilize +destabilized +destabilizes +destabilizing +destination +destinations +destine +destined +destines +destinies +destining +destiny +destitute +destitution +destroy +destroyed +destroyer +destroyers +destroying +destroys +destruct +destructed +destructibility +destructible +destructing +destruction +destructive +destructively +destructiveness +destructs +desuetude +desultorily +desultory +detach +detachable +detached +detaches +detaching +detachment +detachments +detail +detailed +detailing +details +detain +detained +detainee +detainees +detaining +detainment +detains +detect +detectable +detected +detectible +detecting +detection +detective +detectives +detector +detectors +detects +detente +detentes +detention +detentions +deter +detergent +detergents +deteriorate +deteriorated +deteriorates +deteriorating +deterioration +determent +determinable +determinant +determinants +determinate +determination +determinations +determine +determined +determinedly +determiner +determiners +determines +determining +determinism +deterred +deterrence +deterrent +deterrents +deterring +deters +detest +detestable +detestably +detestation +detested +detesting +detests +dethrone +dethroned +dethronement +dethrones +dethroning +detonate +detonated +detonates +detonating +detonation +detonations +detonator +detonators +detour +detoured +detouring +detours +detox +detoxed +detoxes +detoxification +detoxified +detoxifies +detoxify +detoxifying +detoxing +detract +detracted +detracting +detraction +detractor +detractors +detracts +detriment +detrimental +detrimentally +detriments +detritus +deuce +deuces +deuterium +devaluation +devaluations +devalue +devalued +devalues +devaluing +devastate +devastated +devastates +devastating +devastatingly +devastation +devastator +devastators +develop +developed +developer +developers +developing +development +developmental +developmentally +developments +develops +deviance +deviancy +deviant +deviants +deviate +deviated +deviates +deviating +deviation +deviations +device +devices +devil +deviled +deviling +devilish +devilishly +devilishness +devilled +devilling +devilment +devilries +devilry +devils +deviltries +deviltry +devious +deviously +deviousness +devise +devised +devises +devising +devitalize +devitalized +devitalizes +devitalizing +devoid +devolution +devolve +devolved +devolves +devolving +devote +devoted +devotedly +devotee +devotees +devotes +devoting +devotion +devotional +devotionals +devotions +devour +devoured +devouring +devours +devout +devouter +devoutest +devoutly +devoutness +dewberries +dewberry +dewclaw +dewclaws +dewdrop +dewdrops +dewier +dewiest +dewiness +dewlap +dewlaps +dewy +dexterity +dexterous +dexterously +dexterousness +dextrose +dextrous +dhoti +dhotis +dhow +dhows +diabetes +diabetic +diabetics +diabolic +diabolical +diabolically +diacritic +diacritical +diacritics +diadem +diadems +diaereses +diaeresis +diagnose +diagnosed +diagnoses +diagnosing +diagnosis +diagnostic +diagnostically +diagnostician +diagnosticians +diagnostics +diagonal +diagonally +diagonals +diagram +diagramed +diagraming +diagrammatic +diagrammatically +diagrammed +diagramming +diagrams +dial +dialect +dialectal +dialectic +dialectical +dialectics +dialects +dialed +dialing +dialled +dialling +dialog +dialogs +dialogue +dialogues +dials +dialyses +dialysis +diameter +diameters +diametric +diametrical +diametrically +diamond +diamondback +diamondbacks +diamonds +diapason +diapasons +diaper +diapered +diapering +diapers +diaphanous +diaphragm +diaphragmatic +diaphragms +diaries +diarist +diarists +diarrhea +diarrhoea +diary +diaspora +diasporas +diastase +diastole +diastolic +diathermy +diatom +diatomic +diatoms +diatonic +diatribe +diatribes +dibble +dibbled +dibbles +dibbling +dibs +dice +diced +dices +dicey +dichotomies +dichotomous +dichotomy +dicier +diciest +dicing +dick +dicker +dickered +dickering +dickers +dickey +dickeys +dickies +dicks +dicky +dicotyledon +dicotyledonous +dicotyledons +dicta +dictate +dictated +dictates +dictating +dictation +dictations +dictator +dictatorial +dictatorially +dictators +dictatorship +dictatorships +diction +dictionaries +dictionary +dictum +dictums +didactic +didactically +diddle +diddled +diddler +diddlers +diddles +diddling +dido +didoes +didos +didst +died +diehard +diehards +dielectric +dielectrics +diereses +dieresis +dies +diesel +dieseled +dieseling +diesels +diet +dietaries +dietary +dieted +dieter +dieters +dietetic +dietetics +dietician +dieticians +dieting +dietitian +dietitians +diets +differ +differed +difference +differences +different +differential +differentials +differentiate +differentiated +differentiates +differentiating +differentiation +differently +differing +differs +difficult +difficulties +difficultly +difficulty +diffidence +diffident +diffidently +diffraction +diffuse +diffused +diffusely +diffuseness +diffuses +diffusing +diffusion +diffusive +digerati +digest +digested +digestibility +digestible +digesting +digestion +digestions +digestive +digests +digger +diggers +digging +diggings +digit +digital +digitalis +digitally +digitize +digitized +digitizes +digitizing +digits +dignified +dignifies +dignify +dignifying +dignitaries +dignitary +dignities +dignity +digraph +digraphs +digress +digressed +digresses +digressing +digression +digressions +digressive +digs +dike +diked +dikes +diking +dilapidated +dilapidation +dilatation +dilate +dilated +dilates +dilating +dilation +dilator +dilators +dilatory +dilemma +dilemmas +dilettante +dilettantes +dilettanti +dilettantish +dilettantism +diligence +diligent +diligently +dill +dillies +dills +dilly +dillydallied +dillydallies +dillydally +dillydallying +dilute +diluted +dilutes +diluting +dilution +dime +dimension +dimensional +dimensions +dimes +diminish +diminished +diminishes +diminishing +diminuendo +diminuendoes +diminuendos +diminution +diminutions +diminutive +diminutives +dimity +dimly +dimmed +dimmer +dimmers +dimmest +dimming +dimness +dimple +dimpled +dimples +dimpling +dimply +dims +dimwit +dimwits +dimwitted +dinar +dinars +dine +dined +diner +diners +dines +dinette +dinettes +ding +dingbat +dingbats +dingdong +dinged +dinghies +dinghy +dingier +dingiest +dingily +dinginess +dinging +dingle +dingles +dingo +dingoes +dings +dingus +dinguses +dingy +dining +dinkier +dinkies +dinkiest +dinky +dinned +dinner +dinnered +dinnering +dinners +dinnertime +dinnerware +dinning +dinosaur +dinosaurs +dins +dint +diocesan +diocesans +diocese +dioceses +diode +diodes +diorama +dioramas +dioxide +dioxides +dioxin +dioxins +diphtheria +diphthong +diphthongs +diploid +diploids +diploma +diplomacy +diplomas +diplomat +diplomata +diplomatic +diplomatically +diplomatist +diplomatists +diplomats +dipole +dipoles +dipped +dipper +dippers +dippier +dippiest +dipping +dippy +dips +dipsomania +dipsomaniac +dipsomaniacs +dipstick +dipsticks +dipterous +diptych +diptychs +dire +direct +directed +directer +directest +directing +direction +directional +directions +directive +directives +directly +directness +director +directorate +directorates +directorial +directories +directors +directorship +directorships +directory +directs +direful +direly +direr +direst +dirge +dirges +dirigible +dirigibles +dirk +dirks +dirndl +dirndls +dirt +dirtied +dirtier +dirties +dirtiest +dirtily +dirtiness +dirty +dirtying +disabilities +disability +disable +disabled +disablement +disables +disabling +disabuse +disabused +disabuses +disabusing +disadvantage +disadvantaged +disadvantageous +disadvantageously +disadvantages +disadvantaging +disaffect +disaffected +disaffecting +disaffection +disaffects +disaffiliate +disaffiliated +disaffiliates +disaffiliating +disaffiliation +disagree +disagreeable +disagreeableness +disagreeably +disagreed +disagreeing +disagreement +disagreements +disagrees +disallow +disallowed +disallowing +disallows +disappear +disappearance +disappearances +disappeared +disappearing +disappears +disappoint +disappointed +disappointing +disappointingly +disappointment +disappointments +disappoints +disapprobation +disapproval +disapprove +disapproved +disapproves +disapproving +disapprovingly +disarm +disarmament +disarmed +disarming +disarmingly +disarms +disarrange +disarranged +disarrangement +disarranges +disarranging +disarray +disarrayed +disarraying +disarrays +disassemble +disassembled +disassembles +disassembling +disassociate +disassociated +disassociates +disassociating +disassociation +disaster +disasters +disastrous +disastrously +disavow +disavowal +disavowals +disavowed +disavowing +disavows +disband +disbanded +disbanding +disbandment +disbands +disbar +disbarment +disbarred +disbarring +disbars +disbelief +disbelieve +disbelieved +disbeliever +disbelievers +disbelieves +disbelieving +disbelievingly +disbursal +disburse +disbursed +disbursement +disbursements +disburses +disbursing +disc +discard +discarded +discarding +discards +discern +discerned +discernible +discernibly +discerning +discerningly +discernment +discerns +discharge +discharged +discharges +discharging +disciple +disciples +discipleship +disciplinarian +disciplinarians +disciplinary +discipline +disciplined +disciplines +disciplining +disclaim +disclaimed +disclaimer +disclaimers +disclaiming +disclaims +disclose +disclosed +discloses +disclosing +disclosure +disclosures +disco +discoed +discographies +discography +discoing +discolor +discoloration +discolorations +discolored +discoloring +discolors +discolour +discoloured +discolouring +discolours +discombobulate +discombobulated +discombobulates +discombobulating +discombobulation +discomfit +discomfited +discomfiting +discomfits +discomfiture +discomfort +discomforted +discomforting +discomforts +discommode +discommoded +discommodes +discommoding +discompose +discomposed +discomposes +discomposing +discomposure +disconcert +disconcerted +disconcerting +disconcertingly +disconcerts +disconnect +disconnected +disconnectedly +disconnectedness +disconnecting +disconnection +disconnections +disconnects +disconsolate +disconsolately +discontent +discontented +discontentedly +discontenting +discontentment +discontents +discontinuance +discontinuances +discontinuation +discontinuations +discontinue +discontinued +discontinues +discontinuing +discontinuities +discontinuity +discontinuous +discontinuously +discord +discordance +discordant +discordantly +discorded +discording +discords +discos +discotheque +discotheques +discount +discounted +discountenance +discountenanced +discountenances +discountenancing +discounter +discounters +discounting +discounts +discourage +discouraged +discouragement +discouragements +discourages +discouraging +discouragingly +discourse +discoursed +discourses +discoursing +discourteous +discourteously +discourtesies +discourtesy +discover +discovered +discoverer +discoverers +discoveries +discovering +discovers +discovery +discredit +discreditable +discreditably +discredited +discrediting +discredits +discreet +discreeter +discreetest +discreetly +discreetness +discrepancies +discrepancy +discrepant +discrete +discretely +discreteness +discretion +discretionary +discriminate +discriminated +discriminates +discriminating +discrimination +discriminator +discriminators +discriminatory +discs +discursive +discursively +discursiveness +discus +discuses +discuss +discussant +discussants +discussed +discusses +discussing +discussion +discussions +disdain +disdained +disdainful +disdainfully +disdaining +disdains +disease +diseased +diseases +disembark +disembarkation +disembarked +disembarking +disembarks +disembodied +disembodies +disembodiment +disembody +disembodying +disembowel +disemboweled +disemboweling +disembowelled +disembowelling +disembowelment +disembowels +disenchant +disenchanted +disenchanting +disenchantment +disenchants +disencumber +disencumbered +disencumbering +disencumbers +disenfranchise +disenfranchised +disenfranchisement +disenfranchises +disenfranchising +disengage +disengaged +disengagement +disengagements +disengages +disengaging +disentangle +disentangled +disentanglement +disentangles +disentangling +disequilibrium +disestablish +disestablished +disestablishes +disestablishing +disestablishment +disesteem +disesteemed +disesteeming +disesteems +disfavor +disfavored +disfavoring +disfavors +disfavour +disfavours +disfigure +disfigured +disfigurement +disfigurements +disfigures +disfiguring +disfranchise +disfranchised +disfranchisement +disfranchises +disfranchising +disgorge +disgorged +disgorgement +disgorges +disgorging +disgrace +disgraced +disgraceful +disgracefully +disgracefulness +disgraces +disgracing +disgruntle +disgruntled +disgruntlement +disgruntles +disgruntling +disguise +disguised +disguises +disguising +disgust +disgusted +disgustedly +disgusting +disgustingly +disgusts +dish +dishabille +disharmonious +disharmony +dishcloth +dishcloths +dishearten +disheartened +disheartening +dishearteningly +disheartens +dished +dishes +dishevel +disheveled +disheveling +dishevelled +dishevelling +dishevelment +dishevels +dishing +dishonest +dishonestly +dishonesty +dishonor +dishonorable +dishonorably +dishonored +dishonoring +dishonors +dishpan +dishpans +dishrag +dishrags +dishtowel +dishtowels +dishware +dishwasher +dishwashers +dishwater +disillusion +disillusioned +disillusioning +disillusionment +disillusions +disinclination +disincline +disinclined +disinclines +disinclining +disinfect +disinfectant +disinfectants +disinfected +disinfecting +disinfection +disinfects +disinflation +disinformation +disingenuous +disingenuously +disinherit +disinheritance +disinherited +disinheriting +disinherits +disintegrate +disintegrated +disintegrates +disintegrating +disintegration +disinter +disinterest +disinterested +disinterestedly +disinterestedness +disinterests +disinterment +disinterred +disinterring +disinters +disinvestment +disjoint +disjointed +disjointedly +disjointedness +disjointing +disjoints +disjunctive +disk +diskette +diskettes +disks +dislike +disliked +dislikes +disliking +dislocate +dislocated +dislocates +dislocating +dislocation +dislocations +dislodge +dislodged +dislodges +dislodging +disloyal +disloyally +disloyalty +dismal +dismally +dismantle +dismantled +dismantlement +dismantles +dismantling +dismay +dismayed +dismaying +dismays +dismember +dismembered +dismembering +dismemberment +dismembers +dismiss +dismissal +dismissals +dismissed +dismisses +dismissing +dismissive +dismissively +dismount +dismounted +dismounting +dismounts +disobedience +disobedient +disobediently +disobey +disobeyed +disobeying +disobeys +disoblige +disobliged +disobliges +disobliging +disorder +disordered +disordering +disorderliness +disorderly +disorders +disorganization +disorganize +disorganized +disorganizes +disorganizing +disorient +disorientate +disorientated +disorientates +disorientating +disorientation +disoriented +disorienting +disorients +disown +disowned +disowning +disowns +disparage +disparaged +disparagement +disparages +disparaging +disparagingly +disparate +disparately +disparities +disparity +dispassion +dispassionate +dispassionately +dispatch +dispatched +dispatcher +dispatchers +dispatches +dispatching +dispel +dispelled +dispelling +dispels +dispensable +dispensaries +dispensary +dispensation +dispensations +dispense +dispensed +dispenser +dispensers +dispenses +dispensing +dispersal +disperse +dispersed +disperses +dispersing +dispersion +dispirit +dispirited +dispiriting +dispirits +displace +displaced +displacement +displacements +displaces +displacing +display +displayed +displaying +displays +displease +displeased +displeases +displeasing +displeasure +disport +disported +disporting +disports +disposable +disposables +disposal +disposals +dispose +disposed +disposer +disposers +disposes +disposing +disposition +dispositions +dispossess +dispossessed +dispossesses +dispossessing +dispossession +dispraise +dispraised +dispraises +dispraising +disproof +disproofs +disproportion +disproportional +disproportionate +disproportionately +disproportions +disprovable +disprove +disproved +disproven +disproves +disproving +disputable +disputably +disputant +disputants +disputation +disputations +disputatious +disputatiously +dispute +disputed +disputer +disputers +disputes +disputing +disqualification +disqualifications +disqualified +disqualifies +disqualify +disqualifying +disquiet +disquieted +disquieting +disquiets +disquietude +disquisition +disquisitions +disregard +disregarded +disregardful +disregarding +disregards +disrepair +disreputable +disreputably +disrepute +disrespect +disrespected +disrespectful +disrespectfully +disrespecting +disrespects +disrobe +disrobed +disrobes +disrobing +disrupt +disrupted +disrupting +disruption +disruptions +disruptive +disruptively +disrupts +diss +dissatisfaction +dissatisfied +dissatisfies +dissatisfy +dissatisfying +dissect +dissected +dissecting +dissection +dissections +dissector +dissectors +dissects +dissed +dissemblance +dissemble +dissembled +dissembler +dissemblers +dissembles +dissembling +disseminate +disseminated +disseminates +disseminating +dissemination +dissension +dissensions +dissent +dissented +dissenter +dissenters +dissenting +dissents +dissertation +dissertations +disservice +disservices +disses +dissever +dissevered +dissevering +dissevers +dissidence +dissident +dissidents +dissimilar +dissimilarities +dissimilarity +dissimilitude +dissimilitudes +dissimulate +dissimulated +dissimulates +dissimulating +dissimulation +dissimulator +dissimulators +dissing +dissipate +dissipated +dissipates +dissipating +dissipation +dissociate +dissociated +dissociates +dissociating +dissociation +dissoluble +dissolute +dissolutely +dissoluteness +dissolution +dissolve +dissolved +dissolves +dissolving +dissonance +dissonances +dissonant +dissuade +dissuaded +dissuades +dissuading +dissuasion +dissuasive +distaff +distaffs +distal +distally +distance +distanced +distances +distancing +distant +distantly +distaste +distasteful +distastefully +distastefulness +distastes +distemper +distend +distended +distending +distends +distension +distensions +distention +distentions +distil +distill +distillate +distillates +distillation +distillations +distilled +distiller +distilleries +distillers +distillery +distilling +distills +distils +distinct +distincter +distinctest +distinction +distinctions +distinctive +distinctively +distinctiveness +distinctly +distinctness +distinguish +distinguishable +distinguished +distinguishes +distinguishing +distort +distorted +distorting +distortion +distortions +distorts +distract +distracted +distractedly +distracting +distraction +distractions +distracts +distrait +distraught +distress +distressed +distresses +distressful +distressing +distressingly +distribute +distributed +distributes +distributing +distribution +distributions +distributive +distributively +distributor +distributors +district +districts +distrust +distrusted +distrustful +distrustfully +distrusting +distrusts +disturb +disturbance +disturbances +disturbed +disturber +disturbers +disturbing +disturbs +disunion +disunite +disunited +disunites +disuniting +disunity +disuse +disused +disuses +disusing +ditch +ditched +ditches +ditching +dither +dithered +ditherer +ditherers +dithering +dithers +ditsier +ditsiest +ditsy +ditties +ditto +dittoed +dittoes +dittoing +dittos +ditty +ditz +ditzes +ditzier +ditziest +ditzy +diuretic +diuretics +diurnal +diurnally +diva +divalent +divan +divans +divas +dive +dived +diver +diverge +diverged +divergence +divergences +divergent +diverges +diverging +divers +diverse +diversely +diverseness +diversification +diversified +diversifies +diversify +diversifying +diversion +diversionary +diversions +diversities +diversity +divert +diverted +diverticulitis +diverting +diverts +dives +divest +divested +divesting +divestiture +divestitures +divestment +divests +dividable +divide +divided +dividend +dividends +divider +dividers +divides +dividing +divination +divine +divined +divinely +diviner +diviners +divines +divinest +diving +divining +divinities +divinity +divisibility +divisible +division +divisional +divisions +divisive +divisively +divisiveness +divisor +divisors +divorce +divorced +divorcee +divorcees +divorcement +divorcements +divorces +divorcing +divot +divots +divulge +divulged +divulges +divulging +divvied +divvies +divvy +divvying +dixieland +dizzied +dizzier +dizzies +dizziest +dizzily +dizziness +dizzy +dizzying +djellaba +djellabah +djellabahs +djellabas +djinn +djinns +doable +dobbin +dobbins +doberman +dobermans +docent +docents +docile +docilely +docility +dock +docked +docket +docketed +docketing +dockets +docking +docks +dockworker +dockworkers +dockyard +dockyards +docs +doctor +doctoral +doctorate +doctorates +doctored +doctoring +doctors +doctrinaire +doctrinaires +doctrinal +doctrine +doctrines +docudrama +docudramas +document +documentaries +documentary +documentation +documented +documenting +documents +dodder +doddered +doddering +dodders +dodge +dodged +dodger +dodgers +dodges +dodging +dodo +dodoes +dodos +doer +doers +does +doeskin +doeskins +doff +doffed +doffing +doffs +dogcart +dogcarts +dogcatcher +dogcatchers +doge +dogear +dogeared +dogearing +dogears +doges +dogfight +dogfights +dogfish +dogfishes +dogged +doggedly +doggedness +doggerel +doggie +doggier +doggies +doggiest +dogging +doggone +doggoned +doggoneder +doggonedest +doggoner +doggones +doggonest +doggoning +doggy +doghouse +doghouses +dogie +dogies +dogleg +doglegged +doglegging +doglegs +dogma +dogmas +dogmata +dogmatic +dogmatically +dogmatism +dogmatist +dogmatists +dogs +dogtrot +dogtrots +dogtrotted +dogtrotting +dogwood +dogwoods +dogy +doilies +doily +doing +doings +doldrums +dole +doled +doleful +dolefully +dolefulness +doles +doling +doll +dollar +dollars +dolled +dollhouse +dollhouses +dollies +dolling +dollop +dolloped +dolloping +dollops +dolls +dolly +dolmen +dolmens +dolomite +dolor +dolorous +dolorously +dolphin +dolphins +dolt +doltish +doltishly +doltishness +dolts +domain +domains +dome +domed +domes +domestic +domestically +domesticate +domesticated +domesticates +domesticating +domestication +domesticity +domestics +domicile +domiciled +domiciles +domiciliary +domiciling +dominance +dominant +dominantly +dominants +dominate +dominated +dominates +dominating +domination +dominatrices +dominatrix +dominatrixes +domineer +domineered +domineering +domineeringly +domineers +doming +dominion +dominions +domino +dominoes +dominos +dona +donas +donate +donated +donates +donating +donation +donations +done +dong +dongle +dongles +dongs +donkey +donkeys +donned +donning +donnybrook +donnybrooks +donor +donors +dons +donut +donuts +doodad +doodads +doodle +doodlebug +doodlebugs +doodled +doodler +doodlers +doodles +doodling +doohickey +doohickeys +doom +doomed +dooming +dooms +doomsayer +doomsayers +doomsday +door +doorbell +doorbells +doorkeeper +doorkeepers +doorknob +doorknobs +doorman +doormat +doormats +doormen +doorplate +doorplates +doors +doorstep +doorstepped +doorstepping +doorsteps +doorstop +doorstops +doorway +doorways +dooryard +dooryards +dopa +dope +doped +doper +dopers +dopes +dopey +dopier +dopiest +dopiness +doping +dopy +dories +dork +dorkier +dorkiest +dorks +dorky +dorm +dormancy +dormant +dormer +dormers +dormice +dormitories +dormitory +dormouse +dorms +dorsal +dorsally +dory +dosage +dosages +dose +dosed +doses +dosimeter +dosimeters +dosing +dossier +dossiers +dost +dotage +dotard +dotards +dote +doted +doter +doters +dotes +doth +doting +dotingly +dots +dotted +dottier +dottiest +dotting +dotty +double +doubled +doubleheader +doubleheaders +doubles +doublespeak +doublet +doublets +doubling +doubloon +doubloons +doubly +doubt +doubted +doubter +doubters +doubtful +doubtfuller +doubtfullest +doubtfully +doubtfulness +doubting +doubtingly +doubtless +doubtlessly +doubts +douche +douched +douches +douching +dough +doughier +doughiest +doughnut +doughnuts +doughtier +doughtiest +doughty +doughy +dour +dourer +dourest +dourly +dourness +douse +doused +douses +dousing +dove +dovecote +dovecotes +doves +dovetail +dovetailed +dovetailing +dovetails +dovish +dowager +dowagers +dowdier +dowdiest +dowdily +dowdiness +dowdy +dowel +doweled +doweling +dowelled +dowelling +dowels +dower +dowered +dowering +dowers +down +downbeat +downbeats +downcast +downdraft +downdrafts +downed +downer +downers +downfall +downfallen +downfalls +downgrade +downgraded +downgrades +downgrading +downhearted +downheartedly +downheartedness +downhill +downhills +downier +downiest +downing +download +downloaded +downloading +downloads +downplay +downplayed +downplaying +downplays +downpour +downpours +downrange +downright +downriver +downs +downscale +downscaled +downscales +downscaling +downshift +downshifted +downshifting +downshifts +downside +downsides +downsize +downsized +downsizes +downsizing +downspout +downspouts +downstage +downstairs +downstate +downstream +downswing +downswings +downtime +downtown +downtrend +downtrends +downtrodden +downturn +downturns +downward +downwards +downwind +downy +dowries +dowry +dowse +dowsed +dowser +dowsers +dowses +dowsing +doxologies +doxology +doyen +doyenne +doyennes +doyens +doze +dozed +dozen +dozens +dozenth +dozes +dozing +drab +drabber +drabbest +drably +drabness +drabs +drachma +drachmae +drachmai +drachmas +draconian +draft +drafted +draftee +draftees +drafter +drafters +draftier +draftiest +draftily +draftiness +drafting +drafts +draftsman +draftsmanship +draftsmen +draftswoman +draftswomen +drafty +drag +dragged +draggier +draggiest +dragging +draggy +dragnet +dragnets +dragon +dragonflies +dragonfly +dragons +dragoon +dragooned +dragooning +dragoons +drags +drain +drainage +drainboard +drainboards +drained +drainer +drainers +draining +drainpipe +drainpipes +drains +drake +drakes +dram +drama +dramas +dramatic +dramatically +dramatics +dramatist +dramatists +dramatization +dramatizations +dramatize +dramatized +dramatizes +dramatizing +drams +drank +drape +draped +draper +draperies +drapers +drapery +drapes +draping +drastic +drastically +drat +draught +draughted +draughtier +draughtiest +draughting +draughts +draughtsman +draughtsmen +draughty +draw +drawback +drawbacks +drawbridge +drawbridges +drawer +drawers +drawing +drawings +drawl +drawled +drawling +drawls +drawn +draws +drawstring +drawstrings +dray +drays +dread +dreaded +dreadful +dreadfully +dreadfulness +dreading +dreadlocks +dreadnaught +dreadnaughts +dreadnought +dreadnoughts +dreads +dream +dreamboat +dreamboats +dreamed +dreamer +dreamers +dreamier +dreamiest +dreamily +dreaminess +dreaming +dreamland +dreamless +dreamlike +dreams +dreamt +dreamworld +dreamworlds +dreamy +drear +drearier +dreariest +drearily +dreariness +dreary +dredge +dredged +dredger +dredgers +dredges +dredging +dregs +drench +drenched +drenches +drenching +dress +dressage +dressed +dresser +dressers +dresses +dressier +dressiest +dressiness +dressing +dressings +dressmaker +dressmakers +dressmaking +dressy +drest +drew +dribble +dribbled +dribbler +dribblers +dribbles +dribbling +driblet +driblets +dried +drier +driers +dries +driest +drift +drifted +drifter +drifters +drifting +drifts +driftwood +drill +drilled +driller +drillers +drilling +drillmaster +drillmasters +drills +drily +drink +drinkable +drinker +drinkers +drinking +drinks +drip +dripped +drippier +drippiest +dripping +drippings +drippy +drips +dript +drive +drivel +driveled +driveler +drivelers +driveling +drivelled +drivelling +drivels +driven +driver +drivers +drives +driveway +driveways +driving +drizzle +drizzled +drizzles +drizzlier +drizzliest +drizzling +drizzly +drogue +drogues +droll +droller +drolleries +drollery +drollest +drollness +drolly +dromedaries +dromedary +drone +droned +drones +droning +drool +drooled +drooling +drools +droop +drooped +droopier +droopiest +droopiness +drooping +droops +droopy +drop +dropkick +dropkicks +droplet +droplets +dropout +dropouts +dropped +dropper +droppers +dropping +droppings +drops +dropsical +dropsy +dross +drought +droughts +drouth +drouths +drove +drover +drovers +droves +drown +drowned +drowning +drownings +drowns +drowse +drowsed +drowses +drowsier +drowsiest +drowsily +drowsiness +drowsing +drowsy +drub +drubbed +drubber +drubbers +drubbing +drubbings +drubs +drudge +drudged +drudgery +drudges +drudging +drug +drugged +druggie +druggies +drugging +druggist +druggists +druggy +drugs +drugstore +drugstores +druid +druidism +druids +drum +drumbeat +drumbeats +drumlin +drumlins +drummed +drummer +drummers +drumming +drums +drumstick +drumsticks +drunk +drunkard +drunkards +drunken +drunkenly +drunkenness +drunker +drunkest +drunks +drupe +drupes +druthers +dryad +dryads +dryer +dryers +drying +dryly +dryness +drys +drywall +dual +dualism +duality +dubbed +dubber +dubbers +dubbin +dubbing +dubiety +dubious +dubiously +dubiousness +dubs +ducal +ducat +ducats +duchess +duchesses +duchies +duchy +duck +duckbill +duckbills +ducked +duckier +duckies +duckiest +ducking +duckling +ducklings +duckpins +ducks +duckweed +ducky +duct +ductile +ductility +ductless +ducts +dude +duded +dudes +dudgeon +duding +duds +duel +dueled +dueler +duelers +dueling +duelist +duelists +duelled +duelling +duellist +duellists +duels +duenna +duennas +dues +duet +duets +duff +duffer +duffers +duffs +dugout +dugouts +duke +dukedom +dukedoms +dukes +dulcet +dulcimer +dulcimers +dull +dullard +dullards +dulled +duller +dullest +dulling +dullness +dulls +dully +dulness +duly +dumb +dumbbell +dumbbells +dumber +dumbest +dumbfound +dumbfounded +dumbfounding +dumbfounds +dumbly +dumbness +dumbstruck +dumbwaiter +dumbwaiters +dumdum +dumdums +dumfound +dumfounded +dumfounding +dumfounds +dummies +dummy +dump +dumped +dumpier +dumpiest +dumpiness +dumping +dumpling +dumplings +dumps +dumpster +dumpsters +dumpy +dunce +dunces +dunderhead +dunderheads +dune +dunes +dung +dungaree +dungarees +dunged +dungeon +dungeons +dunghill +dunghills +dunging +dungs +dunk +dunked +dunking +dunks +dunned +dunner +dunnest +dunning +dunno +duns +duodecimal +duodena +duodenal +duodenum +duodenums +duos +dupe +duped +duper +dupers +dupes +duping +duple +duplex +duplexes +duplicate +duplicated +duplicates +duplicating +duplication +duplicator +duplicators +duplicitous +duplicity +durability +durable +durably +durance +duration +duress +during +durst +durum +dusk +duskier +duskiest +duskiness +dusky +dust +dusted +duster +dusters +dustier +dustiest +dustiness +dusting +dustless +dustpan +dustpans +dusts +dusty +dutch +duteous +duteously +dutiable +duties +dutiful +dutifully +dutifulness +duty +duvet +duvets +dwarf +dwarfed +dwarfing +dwarfish +dwarfism +dwarfs +dwarves +dweeb +dweebs +dwell +dwelled +dweller +dwellers +dwelling +dwellings +dwells +dwelt +dwindle +dwindled +dwindles +dwindling +dybbuk +dybbukim +dybbuks +dyed +dyeing +dyer +dyers +dyes +dyestuff +dying +dyke +dykes +dynamic +dynamical +dynamically +dynamics +dynamism +dynamite +dynamited +dynamiter +dynamiters +dynamites +dynamiting +dynamo +dynamos +dynastic +dynasties +dynasty +dysentery +dysfunction +dysfunctional +dysfunctions +dyslectic +dyslectics +dyslexia +dyslexic +dyslexics +dyspepsia +dyspeptic +dyspeptics +dysprosium +each +eager +eagerer +eagerest +eagerly +eagerness +eagle +eagles +eaglet +eaglets +earache +earaches +eardrum +eardrums +eared +earful +earfuls +earl +earldom +earldoms +earlier +earliest +earliness +earlobe +earlobes +earls +early +earmark +earmarked +earmarking +earmarks +earmuff +earmuffs +earn +earned +earner +earners +earnest +earnestly +earnestness +earnests +earning +earnings +earns +earphone +earphones +earplug +earplugs +earring +earrings +ears +earshot +earsplitting +earth +earthbound +earthed +earthen +earthenware +earthier +earthiest +earthiness +earthing +earthlier +earthliest +earthling +earthlings +earthly +earthquake +earthquakes +earths +earthshaking +earthward +earthwards +earthwork +earthworks +earthworm +earthworms +earthy +earwax +earwig +earwigs +ease +eased +easel +easels +easement +easements +eases +easier +easiest +easily +easiness +easing +east +eastbound +easterlies +easterly +eastern +easterner +easterners +easternmost +eastward +eastwards +easy +easygoing +eatable +eatables +eaten +eater +eateries +eaters +eatery +eating +eats +eave +eaves +eavesdrop +eavesdropped +eavesdropper +eavesdroppers +eavesdropping +eavesdrops +ebbed +ebbing +ebbs +ebonies +ebony +ebullience +ebullient +ebulliently +ebullition +eccentric +eccentrically +eccentricities +eccentricity +eccentrics +ecclesiastic +ecclesiastical +ecclesiastically +ecclesiastics +echelon +echelons +echinoderm +echinoderms +echo +echoed +echoes +echoic +echoing +echolocation +echos +eclair +eclairs +eclat +eclectic +eclectically +eclecticism +eclectics +eclipse +eclipsed +eclipses +eclipsing +ecliptic +eclogue +eclogues +ecocide +ecologic +ecological +ecologically +ecologist +ecologists +ecology +economic +economical +economically +economics +economies +economist +economists +economize +economized +economizer +economizers +economizes +economizing +economy +ecosystem +ecosystems +ecru +ecstasies +ecstasy +ecstatic +ecstatically +ecumenical +ecumenically +ecumenicism +ecumenism +eczema +eddied +eddies +eddy +eddying +edelweiss +edema +edge +edged +edger +edgers +edges +edgeways +edgewise +edgier +edgiest +edgily +edginess +edging +edgings +edgy +edibility +edible +edibleness +edibles +edict +edicts +edification +edifice +edifices +edified +edifier +edifiers +edifies +edify +edifying +edit +edited +editing +edition +editions +editor +editorial +editorialize +editorialized +editorializes +editorializing +editorially +editorials +editors +editorship +edits +educability +educable +educate +educated +educates +educating +education +educational +educationally +educations +educator +educators +educe +educed +educes +educing +edutainment +eels +eerie +eerier +eeriest +eerily +eeriness +eery +efface +effaced +effacement +effaces +effacing +effect +effected +effecting +effective +effectively +effectiveness +effects +effectual +effectually +effectuate +effectuated +effectuates +effectuating +effeminacy +effeminate +effeminately +effendi +effendis +efferent +effervesce +effervesced +effervescence +effervescent +effervescently +effervesces +effervescing +effete +effetely +effeteness +efficacious +efficaciously +efficacy +efficiencies +efficiency +efficient +efficiently +effigies +effigy +efflorescence +efflorescent +effluence +effluent +effluents +effluvia +effluvium +effluviums +effort +effortless +effortlessly +effortlessness +efforts +effrontery +effulgence +effulgent +effuse +effused +effuses +effusing +effusion +effusions +effusive +effusively +effusiveness +egad +egalitarian +egalitarianism +egalitarians +eggbeater +eggbeaters +eggcup +eggcups +egged +egghead +eggheads +egging +eggnog +eggplant +eggplants +eggs +eggshell +eggshells +egis +eglantine +eglantines +egocentric +egocentrically +egocentricity +egocentrics +egoism +egoist +egoistic +egoistical +egoistically +egoists +egomania +egomaniac +egomaniacs +egos +egotism +egotist +egotistic +egotistical +egotistically +egotists +egregious +egregiously +egregiousness +egress +egresses +egret +egrets +eider +eiderdown +eiderdowns +eiders +eight +eighteen +eighteens +eighteenth +eighteenths +eighth +eighths +eighties +eightieth +eightieths +eights +eighty +einsteinium +either +ejaculate +ejaculated +ejaculates +ejaculating +ejaculation +ejaculations +ejaculatory +eject +ejected +ejecting +ejection +ejections +ejector +ejectors +ejects +eked +ekes +eking +elaborate +elaborated +elaborately +elaborateness +elaborates +elaborating +elaboration +elaborations +elan +eland +elands +elapse +elapsed +elapses +elapsing +elastic +elastically +elasticity +elasticize +elasticized +elasticizes +elasticizing +elastics +elate +elated +elatedly +elates +elating +elation +elbow +elbowed +elbowing +elbowroom +elbows +elder +elderberries +elderberry +elderly +elders +eldest +elect +electable +elected +electing +election +electioneer +electioneered +electioneering +electioneers +elections +elective +electives +elector +electoral +electorate +electorates +electors +electric +electrical +electrically +electrician +electricians +electricity +electrification +electrified +electrifier +electrifiers +electrifies +electrify +electrifying +electrocardiogram +electrocardiograms +electrocardiograph +electrocardiographs +electrocardiography +electrocute +electrocuted +electrocutes +electrocuting +electrocution +electrocutions +electrode +electrodes +electroencephalogram +electroencephalograms +electroencephalograph +electroencephalographic +electroencephalographs +electroencephalography +electrologist +electrologists +electrolysis +electrolyte +electrolytes +electrolytic +electromagnet +electromagnetic +electromagnetically +electromagnetism +electromagnets +electromotive +electron +electronic +electronically +electronics +electrons +electroplate +electroplated +electroplates +electroplating +electroscope +electroscopes +electroscopic +electroshock +electrostatics +electrotype +electrotypes +elects +eleemosynary +elegance +elegant +elegantly +elegiac +elegiacal +elegiacs +elegies +elegy +element +elemental +elementally +elementary +elements +elephant +elephantiasis +elephantine +elephants +elevate +elevated +elevates +elevating +elevation +elevations +elevator +elevators +eleven +elevens +eleventh +elevenths +elfin +elfish +elicit +elicitation +elicited +eliciting +elicits +elide +elided +elides +eliding +eligibility +eligible +eliminate +eliminated +eliminates +eliminating +elimination +eliminations +elision +elisions +elite +elites +elitism +elitist +elitists +elixir +elixirs +elks +ellipse +ellipses +ellipsis +ellipsoid +ellipsoidal +ellipsoids +elliptic +elliptical +elliptically +ells +elms +elocution +elocutionary +elocutionist +elocutionists +elodea +elodeas +elongate +elongated +elongates +elongating +elongation +elongations +elope +eloped +elopement +elopements +elopes +eloping +eloquence +eloquent +eloquently +else +elsewhere +elucidate +elucidated +elucidates +elucidating +elucidation +elucidations +elude +eluded +eludes +eluding +elusive +elusively +elusiveness +elver +elvers +elves +emaciate +emaciated +emaciates +emaciating +emaciation +email +emailed +emailing +emails +emanate +emanated +emanates +emanating +emanation +emanations +emancipate +emancipated +emancipates +emancipating +emancipation +emancipator +emancipators +emasculate +emasculated +emasculates +emasculating +emasculation +embalm +embalmed +embalmer +embalmers +embalming +embalms +embank +embanked +embanking +embankment +embankments +embanks +embargo +embargoed +embargoes +embargoing +embark +embarkation +embarkations +embarked +embarking +embarks +embarrass +embarrassed +embarrasses +embarrassing +embarrassingly +embarrassment +embarrassments +embassies +embassy +embattled +embed +embedded +embedding +embeds +embellish +embellished +embellishes +embellishing +embellishment +embellishments +ember +embers +embezzle +embezzled +embezzlement +embezzler +embezzlers +embezzles +embezzling +embitter +embittered +embittering +embitterment +embitters +emblazon +emblazoned +emblazoning +emblazonment +emblazons +emblem +emblematic +emblems +embodied +embodies +embodiment +embody +embodying +embolden +emboldened +emboldening +emboldens +embolism +embolisms +emboss +embossed +embosser +embossers +embosses +embossing +embouchure +embower +embowered +embowering +embowers +embrace +embraceable +embraced +embraces +embracing +embrasure +embrasures +embrocation +embrocations +embroider +embroidered +embroiderer +embroiderers +embroideries +embroidering +embroiders +embroidery +embroil +embroiled +embroiling +embroilment +embroils +embryo +embryologist +embryologists +embryology +embryonic +embryos +emcee +emceed +emceeing +emcees +emend +emendation +emendations +emended +emending +emends +emerald +emeralds +emerge +emerged +emergence +emergencies +emergency +emergent +emerges +emerging +emerita +emeritus +emery +emetic +emetics +emigrant +emigrants +emigrate +emigrated +emigrates +emigrating +emigration +emigrations +emigre +emigres +eminence +eminences +eminent +eminently +emir +emirate +emirates +emirs +emissaries +emissary +emission +emissions +emit +emits +emitted +emitter +emitters +emitting +emollient +emollients +emolument +emoluments +emote +emoted +emotes +emoticon +emoticons +emoting +emotion +emotional +emotionalism +emotionalize +emotionalized +emotionalizes +emotionalizing +emotionally +emotions +emotive +empanel +empaneled +empaneling +empanelled +empanelling +empanels +empathetic +empathize +empathized +empathizes +empathizing +empathy +emperor +emperors +emphases +emphasis +emphasize +emphasized +emphasizes +emphasizing +emphatic +emphatically +emphysema +empire +empires +empiric +empirical +empirically +empiricism +empiricist +empiricists +emplacement +emplacements +employ +employable +employe +employed +employee +employees +employer +employers +employes +employing +employment +employments +employs +emporia +emporium +emporiums +empower +empowered +empowering +empowerment +empowers +empress +empresses +emptied +emptier +empties +emptiest +emptily +emptiness +empty +emptying +empyrean +emulate +emulated +emulates +emulating +emulation +emulations +emulative +emulator +emulators +emulsification +emulsified +emulsifier +emulsifiers +emulsifies +emulsify +emulsifying +emulsion +emulsions +emus +enable +enabled +enabler +enablers +enables +enabling +enact +enacted +enacting +enactment +enactments +enacts +enamel +enameled +enameler +enamelers +enameling +enamelled +enamelling +enamels +enamelware +enamor +enamored +enamoring +enamors +enamour +enamoured +enamouring +enamours +encamp +encamped +encamping +encampment +encampments +encamps +encapsulate +encapsulated +encapsulates +encapsulating +encapsulation +encapsulations +encase +encased +encasement +encases +encasing +encephalitic +encephalitis +enchain +enchained +enchaining +enchains +enchant +enchanted +enchanter +enchanters +enchanting +enchantingly +enchantment +enchantments +enchantress +enchantresses +enchants +enchilada +enchiladas +encipher +enciphered +enciphering +enciphers +encircle +encircled +encirclement +encircles +encircling +enclave +enclaves +enclose +enclosed +encloses +enclosing +enclosure +enclosures +encode +encoded +encoder +encoders +encodes +encoding +encomia +encomium +encomiums +encompass +encompassed +encompasses +encompassing +encore +encored +encores +encoring +encounter +encountered +encountering +encounters +encourage +encouraged +encouragement +encouragements +encourages +encouraging +encouragingly +encroach +encroached +encroaches +encroaching +encroachment +encroachments +encrust +encrustation +encrustations +encrusted +encrusting +encrusts +encumber +encumbered +encumbering +encumbers +encumbrance +encumbrances +encyclical +encyclicals +encyclopaedia +encyclopaedias +encyclopaedic +encyclopedia +encyclopedias +encyclopedic +encyst +encysted +encysting +encystment +encysts +endanger +endangered +endangering +endangerment +endangers +endear +endeared +endearing +endearingly +endearment +endearments +endears +endeavor +endeavored +endeavoring +endeavors +endeavour +endeavoured +endeavouring +endeavours +ended +endemic +endemically +endemics +ending +endings +endive +endives +endless +endlessly +endlessness +endmost +endocrine +endocrines +endocrinologist +endocrinologists +endocrinology +endogenous +endogenously +endorphin +endorphins +endorse +endorsed +endorsement +endorsements +endorser +endorsers +endorses +endorsing +endoscope +endoscopes +endoscopic +endoscopy +endothermic +endow +endowed +endowing +endowment +endowments +endows +endpoint +endpoints +ends +endue +endued +endues +enduing +endurable +endurance +endure +endured +endures +enduring +endways +endwise +enema +enemas +enemata +enemies +enemy +energetic +energetically +energies +energize +energized +energizer +energizers +energizes +energizing +energy +enervate +enervated +enervates +enervating +enervation +enfeeble +enfeebled +enfeeblement +enfeebles +enfeebling +enfilade +enfiladed +enfilades +enfilading +enfold +enfolded +enfolding +enfolds +enforce +enforceable +enforced +enforcement +enforcer +enforcers +enforces +enforcing +enfranchise +enfranchised +enfranchisement +enfranchises +enfranchising +engage +engaged +engagement +engagements +engages +engaging +engagingly +engender +engendered +engendering +engenders +engine +engineer +engineered +engineering +engineers +engines +engorge +engorged +engorgement +engorges +engorging +engram +engrams +engrave +engraved +engraver +engravers +engraves +engraving +engravings +engross +engrossed +engrosses +engrossing +engrossment +engulf +engulfed +engulfing +engulfment +engulfs +enhance +enhanced +enhancement +enhancements +enhances +enhancing +enigma +enigmas +enigmatic +enigmatically +enjambement +enjambements +enjambment +enjambments +enjoin +enjoined +enjoining +enjoins +enjoy +enjoyable +enjoyably +enjoyed +enjoying +enjoyment +enjoyments +enjoys +enlarge +enlargeable +enlarged +enlargement +enlargements +enlarger +enlargers +enlarges +enlarging +enlighten +enlightened +enlightening +enlightenment +enlightens +enlist +enlisted +enlistee +enlistees +enlisting +enlistment +enlistments +enlists +enliven +enlivened +enlivening +enlivenment +enlivens +enmesh +enmeshed +enmeshes +enmeshing +enmeshment +enmities +enmity +ennoble +ennobled +ennoblement +ennobles +ennobling +ennui +enormities +enormity +enormous +enormously +enormousness +enough +enplane +enplaned +enplanes +enplaning +enquire +enquired +enquires +enquiries +enquiring +enquiry +enrage +enraged +enrages +enraging +enrapture +enraptured +enraptures +enrapturing +enrich +enriched +enriches +enriching +enrichment +enrol +enroll +enrolled +enrolling +enrollment +enrollments +enrolls +enrolment +enrolments +enrols +ensconce +ensconced +ensconces +ensconcing +ensemble +ensembles +enshrine +enshrined +enshrinement +enshrines +enshrining +enshroud +enshrouded +enshrouding +enshrouds +ensign +ensigns +ensilage +enslave +enslaved +enslavement +enslaves +enslaving +ensnare +ensnared +ensnarement +ensnares +ensnaring +ensue +ensued +ensues +ensuing +ensure +ensured +ensurer +ensurers +ensures +ensuring +entail +entailed +entailing +entailment +entails +entangle +entangled +entanglement +entanglements +entangles +entangling +entente +ententes +enter +entered +entering +enteritis +enterprise +enterprises +enterprising +enterprisingly +enters +entertain +entertained +entertainer +entertainers +entertaining +entertainingly +entertainment +entertainments +entertains +enthral +enthrall +enthralled +enthralling +enthrallment +enthralls +enthrals +enthrone +enthroned +enthronement +enthronements +enthrones +enthroning +enthuse +enthused +enthuses +enthusiasm +enthusiasms +enthusiast +enthusiastic +enthusiastically +enthusiasts +enthusing +entice +enticed +enticement +enticements +entices +enticing +enticingly +entire +entirely +entirety +entities +entitle +entitled +entitlement +entitlements +entitles +entitling +entity +entomb +entombed +entombing +entombment +entombs +entomological +entomologist +entomologists +entomology +entourage +entourages +entrails +entrance +entranced +entrancement +entrances +entrancing +entrancingly +entrant +entrants +entrap +entrapment +entrapped +entrapping +entraps +entreat +entreated +entreaties +entreating +entreatingly +entreats +entreaty +entree +entrees +entrench +entrenched +entrenches +entrenching +entrenchment +entrenchments +entrepreneur +entrepreneurial +entrepreneurs +entries +entropy +entrust +entrusted +entrusting +entrusts +entry +entryway +entryways +entwine +entwined +entwines +entwining +enumerable +enumerate +enumerated +enumerates +enumerating +enumeration +enumerations +enumerator +enumerators +enunciate +enunciated +enunciates +enunciating +enunciation +enure +enured +enures +enuresis +enuring +envelop +envelope +enveloped +enveloper +envelopers +envelopes +enveloping +envelopment +envelops +envenom +envenomed +envenoming +envenoms +enviable +enviably +envied +envies +envious +enviously +enviousness +environment +environmental +environmentalism +environmentalist +environmentalists +environmentally +environments +environs +envisage +envisaged +envisages +envisaging +envision +envisioned +envisioning +envisions +envoy +envoys +envy +envying +envyingly +enzymatic +enzyme +enzymes +eolian +eons +epaulet +epaulets +epaulette +epaulettes +epee +epees +ephedrine +ephemera +ephemeral +ephemerally +epic +epicenter +epicenters +epicentre +epicentres +epics +epicure +epicurean +epicureans +epicures +epidemic +epidemically +epidemics +epidemiologist +epidemiologists +epidemiology +epidermal +epidermic +epidermis +epidermises +epiglottides +epiglottis +epiglottises +epigram +epigrammatic +epigrams +epigraph +epigraphs +epigraphy +epilepsy +epileptic +epileptics +epilog +epilogs +epilogue +epilogues +epinephrin +epinephrine +epiphanies +epiphany +episcopacy +episcopal +episcopate +episode +episodes +episodic +episodically +epistle +epistles +epistolary +epitaph +epitaphs +epithelial +epithelium +epithet +epithets +epitome +epitomes +epitomize +epitomized +epitomizes +epitomizing +epoch +epochal +epochs +epoxied +epoxies +epoxy +epoxyed +epoxying +epsilon +epsilons +equability +equable +equably +equal +equaled +equaling +equality +equalization +equalize +equalized +equalizer +equalizers +equalizes +equalizing +equalled +equalling +equally +equals +equanimity +equatable +equate +equated +equates +equating +equation +equations +equator +equatorial +equators +equerries +equerry +equestrian +equestrianism +equestrians +equestrienne +equestriennes +equidistant +equidistantly +equilateral +equilaterals +equilibrium +equine +equines +equinoctial +equinox +equinoxes +equip +equipage +equipages +equipment +equipoise +equipped +equipping +equips +equitable +equitably +equitation +equities +equity +equivalence +equivalences +equivalencies +equivalency +equivalent +equivalently +equivalents +equivocal +equivocally +equivocalness +equivocate +equivocated +equivocates +equivocating +equivocation +equivocations +equivocator +equivocators +eradicable +eradicate +eradicated +eradicates +eradicating +eradication +eradicator +eradicators +eras +erasable +erase +erased +eraser +erasers +erases +erasing +erasure +erasures +erbium +erect +erected +erectile +erecting +erection +erections +erectly +erectness +erector +erectors +erects +erelong +eremite +eremites +ergo +ergonomic +ergonomically +ergonomics +ergosterol +ergot +ergs +ermine +ermines +erode +eroded +erodes +erodible +eroding +erogenous +erosion +erosive +erotic +erotica +erotically +eroticism +errand +errands +errant +errata +erratas +erratic +erratically +erratum +erred +erring +erroneous +erroneously +error +errors +errs +ersatz +ersatzes +erst +erstwhile +eruct +eructation +eructations +eructed +eructing +eructs +erudite +eruditely +erudition +erupt +erupted +erupting +eruption +eruptions +eruptive +erupts +erysipelas +erythrocyte +erythrocytes +escalate +escalated +escalates +escalating +escalation +escalations +escalator +escalators +escallop +escalloped +escalloping +escallops +escalop +escalops +escapade +escapades +escape +escaped +escapee +escapees +escapement +escapements +escapes +escaping +escapism +escapist +escapists +escargot +escargots +escarole +escaroles +escarpment +escarpments +eschew +eschewed +eschewing +eschews +escort +escorted +escorting +escorts +escritoire +escritoires +escrow +escrows +escudo +escudos +escutcheon +escutcheons +esophageal +esophagi +esophagus +esoteric +esoterically +espadrille +espadrilles +espalier +espaliered +espaliering +espaliers +especial +especially +espied +espies +espionage +esplanade +esplanades +espousal +espouse +espoused +espouses +espousing +espresso +espressos +esprit +espy +espying +esquire +esquires +essay +essayed +essayer +essayers +essaying +essayist +essayists +essays +essence +essences +essential +essentially +essentials +establish +established +establishes +establishing +establishment +establishments +estate +estates +esteem +esteemed +esteeming +esteems +ester +esters +esthete +esthetes +esthetic +esthetically +esthetics +estimable +estimate +estimated +estimates +estimating +estimation +estimations +estimator +estimators +estrange +estranged +estrangement +estrangements +estranges +estranging +estrogen +estrous +estrus +estruses +estuaries +estuary +etas +etch +etched +etcher +etchers +etches +etching +etchings +eternal +eternally +eternalness +eternities +eternity +ethane +ethanol +ether +ethereal +ethereally +ethic +ethical +ethically +ethics +ethnic +ethnically +ethnicity +ethnics +ethnocentric +ethnocentrism +ethnological +ethnologist +ethnologists +ethnology +ethological +ethologist +ethologists +ethology +ethos +ethyl +ethylene +etiologic +etiological +etiologies +etiology +etiquette +etude +etudes +etymological +etymologically +etymologies +etymologist +etymologists +etymology +eucalypti +eucalyptus +eucalyptuses +euchre +euchred +euchres +euchring +euclidean +eugenic +eugenically +eugenicist +eugenicists +eugenics +eulogies +eulogist +eulogistic +eulogists +eulogize +eulogized +eulogizer +eulogizers +eulogizes +eulogizing +eulogy +eunuch +eunuchs +euphemism +euphemisms +euphemistic +euphemistically +euphonious +euphoniously +euphony +euphoria +euphoric +euphorically +eureka +euro +europium +euros +euthanasia +euthanize +euthanized +euthanizes +euthanizing +euthenics +evacuate +evacuated +evacuates +evacuating +evacuation +evacuations +evacuee +evacuees +evade +evaded +evader +evaders +evades +evading +evaluate +evaluated +evaluates +evaluating +evaluation +evaluations +evanescence +evanescent +evangelic +evangelical +evangelicalism +evangelically +evangelicals +evangelism +evangelist +evangelistic +evangelists +evangelize +evangelized +evangelizes +evangelizing +evaporate +evaporated +evaporates +evaporating +evaporation +evaporator +evaporators +evasion +evasions +evasive +evasively +evasiveness +even +evened +evener +evenest +evenhanded +evenhandedly +evening +evenings +evenly +evenness +evens +evensong +event +eventful +eventfully +eventfulness +eventide +events +eventual +eventualities +eventuality +eventually +eventuate +eventuated +eventuates +eventuating +ever +everglade +everglades +evergreen +evergreens +everlasting +everlastingly +everlastings +evermore +every +everybody +everyday +everyone +everyplace +everything +everywhere +eves +evict +evicted +evicting +eviction +evictions +evicts +evidence +evidenced +evidences +evidencing +evident +evidently +evil +evildoer +evildoers +evildoing +eviler +evilest +eviller +evillest +evilly +evilness +evils +evince +evinced +evinces +evincing +eviscerate +eviscerated +eviscerates +eviscerating +evisceration +evocation +evocations +evocative +evocatively +evoke +evoked +evokes +evoking +evolution +evolutionary +evolutionist +evolutionists +evolve +evolved +evolves +evolving +ewer +ewers +ewes +exacerbate +exacerbated +exacerbates +exacerbating +exacerbation +exact +exacted +exacter +exactest +exacting +exactingly +exaction +exactitude +exactly +exactness +exacts +exaggerate +exaggerated +exaggeratedly +exaggerates +exaggerating +exaggeration +exaggerations +exaggerator +exaggerators +exalt +exaltation +exalted +exalting +exalts +exam +examination +examinations +examine +examined +examiner +examiners +examines +examining +example +exampled +examples +exampling +exams +exasperate +exasperated +exasperates +exasperating +exasperation +excavate +excavated +excavates +excavating +excavation +excavations +excavator +excavators +exceed +exceeded +exceeding +exceedingly +exceeds +excel +excelled +excellence +excellencies +excellency +excellent +excellently +excelling +excels +excelsior +except +excepted +excepting +exception +exceptionable +exceptional +exceptionally +exceptions +excepts +excerpt +excerpted +excerpting +excerpts +excess +excesses +excessive +excessively +exchange +exchangeable +exchanged +exchanges +exchanging +exchequer +exchequers +excise +excised +excises +excising +excision +excisions +excitability +excitable +excitably +excitation +excite +excited +excitedly +excitement +excitements +exciter +exciters +excites +exciting +excitingly +exclaim +exclaimed +exclaiming +exclaims +exclamation +exclamations +exclamatory +exclude +excluded +excludes +excluding +exclusion +exclusive +exclusively +exclusiveness +exclusives +exclusivity +excommunicate +excommunicated +excommunicates +excommunicating +excommunication +excommunications +excoriate +excoriated +excoriates +excoriating +excoriation +excoriations +excrement +excremental +excrescence +excrescences +excrescent +excreta +excrete +excreted +excretes +excreting +excretion +excretions +excretory +excruciating +excruciatingly +exculpate +exculpated +exculpates +exculpating +exculpation +exculpatory +excursion +excursionist +excursionists +excursions +excursive +excursively +excursiveness +excusable +excusably +excuse +excused +excuses +excusing +exec +execrable +execrably +execrate +execrated +execrates +execrating +execration +execs +execute +executed +executes +executing +execution +executioner +executioners +executions +executive +executives +executor +executors +executrices +executrix +executrixes +exegeses +exegesis +exegetic +exegetical +exemplar +exemplars +exemplary +exemplification +exemplifications +exemplified +exemplifies +exemplify +exemplifying +exempt +exempted +exempting +exemption +exemptions +exempts +exercise +exercised +exerciser +exercisers +exercises +exercising +exert +exerted +exerting +exertion +exertions +exerts +exes +exhalation +exhalations +exhale +exhaled +exhales +exhaling +exhaust +exhausted +exhaustible +exhausting +exhaustion +exhaustive +exhaustively +exhaustiveness +exhausts +exhibit +exhibited +exhibiting +exhibition +exhibitionism +exhibitionist +exhibitionists +exhibitions +exhibitor +exhibitors +exhibits +exhilarate +exhilarated +exhilarates +exhilarating +exhilaration +exhort +exhortation +exhortations +exhorted +exhorting +exhorts +exhumation +exhumations +exhume +exhumed +exhumes +exhuming +exigence +exigences +exigencies +exigency +exigent +exiguity +exiguous +exile +exiled +exiles +exiling +exist +existed +existence +existences +existent +existential +existentialism +existentialist +existentialists +existentially +existing +exists +exit +exited +exiting +exits +exobiology +exodus +exoduses +exogenous +exonerate +exonerated +exonerates +exonerating +exoneration +exorbitance +exorbitant +exorbitantly +exorcise +exorcised +exorcises +exorcising +exorcism +exorcisms +exorcist +exorcists +exorcize +exorcized +exorcizes +exorcizing +exoskeleton +exoskeletons +exosphere +exospheres +exothermic +exotic +exotically +exoticism +exotics +expand +expandable +expanded +expanding +expands +expanse +expanses +expansible +expansion +expansionary +expansionism +expansionist +expansionists +expansions +expansive +expansively +expansiveness +expatiate +expatiated +expatiates +expatiating +expatiation +expatriate +expatriated +expatriates +expatriating +expatriation +expect +expectancy +expectant +expectantly +expectation +expectations +expected +expecting +expectorant +expectorants +expectorate +expectorated +expectorates +expectorating +expectoration +expects +expedience +expediences +expediencies +expediency +expedient +expediently +expedients +expedite +expedited +expediter +expediters +expedites +expediting +expedition +expeditionary +expeditions +expeditious +expeditiously +expeditiousness +expeditor +expeditors +expel +expelled +expelling +expels +expend +expendable +expendables +expended +expending +expenditure +expenditures +expends +expense +expenses +expensive +expensively +expensiveness +experience +experienced +experiences +experiencing +experiment +experimental +experimentally +experimentation +experimented +experimenter +experimenters +experimenting +experiments +expert +expertise +expertly +expertness +experts +expiate +expiated +expiates +expiating +expiation +expiatory +expiration +expire +expired +expires +expiring +expiry +explain +explainable +explained +explaining +explains +explanation +explanations +explanatory +expletive +expletives +explicable +explicate +explicated +explicates +explicating +explication +explications +explicit +explicitly +explicitness +explode +exploded +explodes +exploding +exploit +exploitable +exploitation +exploitative +exploited +exploiter +exploiters +exploiting +exploits +exploration +explorations +exploratory +explore +explored +explorer +explorers +explores +exploring +explosion +explosions +explosive +explosively +explosiveness +explosives +expo +exponent +exponential +exponentially +exponents +export +exportable +exportation +exported +exporter +exporters +exporting +exports +expos +expose +exposed +exposes +exposing +exposition +expositions +expositor +expositors +expository +expostulate +expostulated +expostulates +expostulating +expostulation +expostulations +exposure +exposures +expound +expounded +expounder +expounders +expounding +expounds +express +expressed +expresses +expressible +expressing +expression +expressionism +expressionist +expressionistic +expressionists +expressionless +expressions +expressive +expressively +expressiveness +expressly +expressway +expressways +expropriate +expropriated +expropriates +expropriating +expropriation +expropriations +expropriator +expropriators +expulsion +expulsions +expunge +expunged +expunges +expunging +expurgate +expurgated +expurgates +expurgating +expurgation +expurgations +exquisite +exquisitely +exquisiteness +extant +extemporaneous +extemporaneously +extemporaneousness +extempore +extemporization +extemporize +extemporized +extemporizes +extemporizing +extend +extendable +extended +extender +extenders +extendible +extending +extends +extensible +extension +extensions +extensive +extensively +extensiveness +extent +extents +extenuate +extenuated +extenuates +extenuating +extenuation +exterior +exteriors +exterminate +exterminated +exterminates +exterminating +extermination +exterminations +exterminator +exterminators +external +externalization +externalizations +externalize +externalized +externalizes +externalizing +externally +externals +extinct +extincted +extincting +extinction +extinctions +extincts +extinguish +extinguishable +extinguished +extinguisher +extinguishers +extinguishes +extinguishing +extirpate +extirpated +extirpates +extirpating +extirpation +extol +extoll +extolled +extolling +extolls +extols +extort +extorted +extorting +extortion +extortionate +extortionately +extortioner +extortioners +extortionist +extortionists +extorts +extra +extract +extracted +extracting +extraction +extractions +extractor +extractors +extracts +extracurricular +extraditable +extradite +extradited +extradites +extraditing +extradition +extraditions +extralegal +extramarital +extramural +extraneous +extraneously +extraordinarily +extraordinary +extrapolate +extrapolated +extrapolates +extrapolating +extrapolation +extrapolations +extras +extrasensory +extraterrestrial +extraterrestrials +extraterritorial +extraterritoriality +extravagance +extravagances +extravagant +extravagantly +extravaganza +extravaganzas +extravehicular +extravert +extraverts +extreme +extremely +extremeness +extremer +extremes +extremest +extremism +extremist +extremists +extremities +extremity +extricable +extricate +extricated +extricates +extricating +extrication +extrinsic +extrinsically +extroversion +extrovert +extroverted +extroverts +extrude +extruded +extrudes +extruding +extrusion +extrusions +extrusive +exuberance +exuberant +exuberantly +exudation +exude +exuded +exudes +exuding +exult +exultant +exultantly +exultation +exulted +exulting +exults +exurb +exurban +exurbanite +exurbanites +exurbia +exurbs +eyeball +eyeballed +eyeballing +eyeballs +eyebrow +eyebrows +eyed +eyedropper +eyedroppers +eyeful +eyefuls +eyeglass +eyeglasses +eyeing +eyelash +eyelashes +eyeless +eyelet +eyelets +eyelid +eyelids +eyeliner +eyeliners +eyeopener +eyeopeners +eyeopening +eyepiece +eyepieces +eyes +eyesight +eyesore +eyesores +eyestrain +eyeteeth +eyetooth +eyewash +eyewitness +eyewitnesses +eying +eyrie +eyries +eyry +fable +fabled +fables +fabric +fabricate +fabricated +fabricates +fabricating +fabrication +fabrications +fabricator +fabricators +fabrics +fabulous +fabulously +facade +facades +face +facecloth +facecloths +faced +faceless +facelift +facelifts +faces +facet +faceted +faceting +facetious +facetiously +facetiousness +facets +facetted +facetting +facial +facially +facials +facile +facilely +facilitate +facilitated +facilitates +facilitating +facilitation +facilitator +facilitators +facilities +facility +facing +facings +facsimile +facsimiled +facsimileing +facsimiles +fact +faction +factional +factionalism +factions +factious +factitious +factoid +factoids +factor +factored +factorial +factorials +factories +factoring +factors +factory +factotum +factotums +facts +factual +factually +faculties +faculty +faddish +faddist +faddists +fade +faded +fades +fading +fads +faecal +faeces +faerie +faeries +faery +fagged +fagging +faggot +faggoting +faggots +fagot +fagoting +fagots +fags +faience +fail +failed +failing +failings +faille +fails +failure +failures +fain +fainer +fainest +faint +fainted +fainter +faintest +fainthearted +fainting +faintly +faintness +faints +fair +fairer +fairest +fairground +fairgrounds +fairies +fairing +fairings +fairly +fairness +fairs +fairway +fairways +fairy +fairyland +fairylands +faith +faithful +faithfully +faithfulness +faithfuls +faithless +faithlessly +faithlessness +faiths +fajita +fajitas +fake +faked +faker +fakers +fakes +faking +fakir +fakirs +falcon +falconer +falconers +falconry +falcons +fall +fallacies +fallacious +fallaciously +fallacy +fallen +fallibility +fallible +fallibleness +fallibly +falling +falloff +falloffs +fallout +fallow +fallowed +fallowing +fallows +falls +false +falsehood +falsehoods +falsely +falseness +falser +falsest +falsetto +falsettos +falsie +falsies +falsification +falsifications +falsified +falsifier +falsifiers +falsifies +falsify +falsifying +falsities +falsity +falter +faltered +faltering +falteringly +falters +fame +famed +familial +familiar +familiarity +familiarization +familiarize +familiarized +familiarizes +familiarizing +familiarly +familiars +families +family +famine +famines +famish +famished +famishes +famishing +famous +famously +fanatic +fanatical +fanatically +fanaticism +fanatics +fancied +fancier +fanciers +fancies +fanciest +fanciful +fancifully +fancifulness +fancily +fanciness +fancy +fancying +fancywork +fandango +fandangoes +fandangos +fanfare +fanfares +fang +fanged +fangs +fanlight +fanlights +fanned +fannies +fanning +fanny +fans +fantail +fantails +fantasia +fantasias +fantasied +fantasies +fantasize +fantasized +fantasizes +fantasizing +fantastic +fantastical +fantastically +fantasy +fantasying +fanzine +fanzines +farad +farads +faraway +farce +farces +farcical +farcically +fare +fared +fares +farewell +farewells +farfetched +farina +farinaceous +faring +farm +farmed +farmer +farmers +farmhand +farmhands +farmhouse +farmhouses +farming +farmland +farms +farmstead +farmsteads +farmyard +farmyards +faro +farrago +farragoes +farragos +farrier +farriers +farrow +farrowed +farrowing +farrows +farseeing +farsighted +farsightedness +fart +farted +farther +farthermost +farthest +farthing +farthings +farting +farts +fascia +fasciae +fascias +fascicle +fascicles +fascinate +fascinated +fascinates +fascinating +fascinatingly +fascination +fascinations +fascism +fascist +fascistic +fascists +fashion +fashionable +fashionably +fashioned +fashioner +fashioners +fashioning +fashions +fast +fastback +fastbacks +fastball +fastballs +fasted +fasten +fastened +fastener +fasteners +fastening +fastenings +fastens +faster +fastest +fastidious +fastidiously +fastidiousness +fasting +fastness +fastnesses +fasts +fatal +fatalism +fatalist +fatalistic +fatalistically +fatalists +fatalities +fatality +fatally +fatback +fate +fated +fateful +fatefully +fatefulness +fates +fathead +fatheaded +fatheads +father +fathered +fatherhood +fathering +fatherland +fatherlands +fatherless +fatherly +fathers +fathom +fathomable +fathomed +fathoming +fathomless +fathoms +fatigue +fatigued +fatigues +fatiguing +fating +fatness +fats +fatten +fattened +fattening +fattens +fatter +fattest +fattier +fatties +fattiest +fattiness +fatty +fatuity +fatuous +fatuously +fatuousness +fatwa +fatwas +faucet +faucets +fault +faulted +faultfinder +faultfinders +faultfinding +faultier +faultiest +faultily +faultiness +faulting +faultless +faultlessly +faultlessness +faults +faulty +faun +fauna +faunae +faunas +fauns +fauvism +fauvist +fauvists +favor +favorable +favorably +favored +favoring +favorite +favorites +favoritism +favors +favour +favoured +favouring +favours +fawn +fawned +fawner +fawners +fawnest +fawning +fawns +faxed +faxes +faxing +fayer +fayest +fays +faze +fazed +fazes +fazing +fealty +fear +feared +fearful +fearfully +fearfulness +fearing +fearless +fearlessly +fearlessness +fears +fearsome +feasibility +feasible +feasibly +feast +feasted +feaster +feasters +feasting +feasts +feat +feather +featherbedding +feathered +featherier +featheriest +feathering +featherless +feathers +featherweight +featherweights +feathery +feats +feature +featured +featureless +features +featuring +febrile +fecal +feces +feckless +fecklessly +fecund +fecundate +fecundated +fecundates +fecundating +fecundation +fecundity +federal +federalism +federalist +federalists +federalization +federalize +federalized +federalizes +federalizing +federally +federals +federate +federated +federates +federating +federation +federations +fedora +fedoras +feds +feeble +feebleness +feebler +feeblest +feebly +feed +feedback +feedbag +feedbags +feeder +feeders +feeding +feedings +feedlot +feedlots +feeds +feel +feeler +feelers +feeling +feelingly +feelings +feels +fees +feet +feign +feigned +feigning +feigns +feint +feinted +feinting +feints +feistier +feistiest +feisty +feldspar +felicitate +felicitated +felicitates +felicitating +felicitation +felicitations +felicities +felicitous +felicitously +felicity +feline +felines +fell +fellatio +felled +feller +fellest +felling +fellow +fellowman +fellowmen +fellows +fellowship +fellowships +fells +felon +felonies +felonious +felons +felony +felt +felted +felting +felts +female +femaleness +females +feminine +femininely +feminines +femininity +feminism +feminist +feminists +femora +femoral +femur +femurs +fence +fenced +fencer +fencers +fences +fencing +fend +fended +fender +fenders +fending +fends +fenestration +fennel +fens +feral +ferment +fermentation +fermented +fermenting +ferments +fermium +fern +fernier +ferniest +ferns +ferny +ferocious +ferociously +ferociousness +ferocity +ferret +ferreted +ferreting +ferrets +ferric +ferried +ferries +ferromagnetic +ferrous +ferrule +ferrules +ferry +ferryboat +ferryboats +ferrying +ferryman +ferrymen +fertile +fertility +fertilization +fertilize +fertilized +fertilizer +fertilizers +fertilizes +fertilizing +ferule +ferules +fervency +fervent +fervently +fervid +fervidly +fervor +fervour +fess +fessed +fesses +fessing +fest +festal +fester +festered +festering +festers +festival +festivals +festive +festively +festiveness +festivities +festivity +festoon +festooned +festooning +festoons +fests +feta +fetal +fetch +fetched +fetcher +fetchers +fetches +fetching +fetchingly +fete +feted +fetes +fetich +fetiches +fetid +fetidness +feting +fetish +fetishes +fetishism +fetishist +fetishistic +fetishists +fetlock +fetlocks +fetter +fettered +fettering +fetters +fettle +fettuccine +fetus +fetuses +feud +feudal +feudalism +feudalistic +feuded +feuding +feuds +fever +fevered +feverish +feverishly +feverishness +fevers +fewer +fewest +fewness +fezes +fezzes +fiance +fiancee +fiancees +fiances +fiasco +fiascoes +fiascos +fiat +fiats +fibbed +fibber +fibbers +fibbing +fiber +fiberboard +fiberfill +fiberglass +fibers +fibre +fibres +fibril +fibrillate +fibrillated +fibrillates +fibrillating +fibrillation +fibrils +fibrin +fibroid +fibrosis +fibrous +fibs +fibula +fibulae +fibular +fibulas +fiche +fiches +fichu +fichus +fickle +fickleness +fickler +ficklest +fiction +fictional +fictionalization +fictionalizations +fictionalize +fictionalized +fictionalizes +fictionalizing +fictionally +fictions +fictitious +fictitiously +fictive +ficus +fiddle +fiddled +fiddler +fiddlers +fiddles +fiddlesticks +fiddling +fidelity +fidget +fidgeted +fidgeting +fidgets +fidgety +fiduciaries +fiduciary +fief +fiefdom +fiefdoms +fiefs +field +fielded +fielder +fielders +fielding +fields +fieldwork +fieldworker +fieldworkers +fiend +fiendish +fiendishly +fiends +fierce +fiercely +fierceness +fiercer +fiercest +fierier +fieriest +fieriness +fiery +fiesta +fiestas +fife +fifer +fifers +fifes +fifteen +fifteens +fifteenth +fifteenths +fifth +fifthly +fifths +fifties +fiftieth +fiftieths +fifty +fight +fighter +fighters +fighting +fights +figment +figments +figs +figuration +figurative +figuratively +figure +figured +figurehead +figureheads +figures +figurine +figurines +figuring +filament +filamentous +filaments +filbert +filberts +filch +filched +filches +filching +file +filed +filer +filers +files +filet +fileted +fileting +filets +filial +filibuster +filibustered +filibusterer +filibusterers +filibustering +filibusters +filigree +filigreed +filigreeing +filigrees +filing +filings +fill +filled +filler +fillers +fillet +filleted +filleting +fillets +fillies +filling +fillings +fillip +filliped +filliping +fillips +fills +filly +film +filmed +filmier +filmiest +filminess +filming +filmmaker +filmmakers +films +filmstrip +filmstrips +filmy +filter +filterable +filtered +filterer +filterers +filtering +filters +filth +filthier +filthiest +filthily +filthiness +filthy +filtrable +filtrate +filtrated +filtrates +filtrating +filtration +finagle +finagled +finagler +finaglers +finagles +finagling +final +finale +finales +finalist +finalists +finality +finalization +finalize +finalized +finalizes +finalizing +finally +finals +finance +financed +finances +financial +financially +financier +financiers +financing +finch +finches +find +finder +finders +finding +findings +finds +fine +fined +finely +fineness +finer +finery +fines +finespun +finesse +finessed +finesses +finessing +finest +finger +fingerboard +fingerboards +fingered +fingering +fingerings +fingerling +fingerlings +fingernail +fingernails +fingerprint +fingerprinted +fingerprinting +fingerprints +fingers +fingertip +fingertips +finial +finials +finical +finickier +finickiest +finickiness +finicky +fining +finis +finises +finish +finished +finisher +finishers +finishes +finishing +finite +finitely +fink +finked +finking +finks +finned +finnier +finniest +finny +fins +fiord +fiords +fire +firearm +firearms +fireball +fireballs +firebomb +firebombed +firebombing +firebombs +firebox +fireboxes +firebrand +firebrands +firebreak +firebreaks +firebrick +firebricks +firebug +firebugs +firecracker +firecrackers +fired +firedamp +firefight +firefighter +firefighters +firefighting +firefights +fireflies +firefly +firehouse +firehouses +firelight +fireman +firemen +fireplace +fireplaces +fireplug +fireplugs +firepower +fireproof +fireproofed +fireproofing +fireproofs +firer +firers +fires +fireside +firesides +firestorm +firestorms +firetrap +firetraps +firetruck +firetrucks +firewall +firewalls +firewater +firewood +firework +fireworks +firing +firm +firmament +firmaments +firmed +firmer +firmest +firming +firmly +firmness +firms +firmware +firs +first +firstborn +firstborns +firsthand +firstly +firsts +firth +firths +fiscal +fiscally +fiscals +fish +fishbowl +fishbowls +fishcake +fishcakes +fished +fisher +fisheries +fisherman +fishermen +fishers +fishery +fishes +fishhook +fishhooks +fishier +fishiest +fishily +fishiness +fishing +fishmonger +fishmongers +fishnet +fishnets +fishpond +fishponds +fishtail +fishtailed +fishtailing +fishtails +fishwife +fishwives +fishy +fissile +fission +fissionable +fissure +fissures +fist +fistfight +fistfights +fistful +fistfuls +fisticuffs +fists +fistula +fistulae +fistulas +fistulous +fitful +fitfully +fitfulness +fitly +fitness +fits +fitted +fitter +fitters +fittest +fitting +fittingly +fittings +five +fives +fixable +fixate +fixated +fixates +fixating +fixation +fixations +fixative +fixatives +fixed +fixedly +fixer +fixers +fixes +fixing +fixings +fixity +fixture +fixtures +fizz +fizzed +fizzes +fizzier +fizziest +fizzing +fizzle +fizzled +fizzles +fizzling +fizzy +fjord +fjords +flab +flabbergast +flabbergasted +flabbergasting +flabbergasts +flabbier +flabbiest +flabbily +flabbiness +flabby +flaccid +flaccidity +flaccidly +flack +flacks +flag +flagella +flagellate +flagellated +flagellates +flagellating +flagellation +flagellum +flagellums +flagged +flagging +flagman +flagmen +flagon +flagons +flagpole +flagpoles +flagrance +flagrancy +flagrant +flagrantly +flags +flagship +flagships +flagstaff +flagstaffs +flagstone +flagstones +flail +flailed +flailing +flails +flair +flairs +flak +flake +flaked +flakes +flakier +flakiest +flakiness +flaking +flaky +flambe +flambeed +flambeing +flambes +flamboyance +flamboyancy +flamboyant +flamboyantly +flame +flamed +flamenco +flamencos +flameproof +flameproofed +flameproofing +flameproofs +flames +flamethrower +flamethrowers +flaming +flamingo +flamingoes +flamingos +flammability +flammable +flammables +flan +flange +flanges +flank +flanked +flanker +flankers +flanking +flanks +flannel +flanneled +flannelet +flannelette +flanneling +flannelled +flannelling +flannels +flans +flap +flapjack +flapjacks +flapped +flapper +flappers +flapping +flaps +flare +flared +flares +flareup +flareups +flaring +flash +flashback +flashbacks +flashbulb +flashbulbs +flashcard +flashcards +flashcube +flashcubes +flashed +flasher +flashers +flashes +flashest +flashgun +flashguns +flashier +flashiest +flashily +flashiness +flashing +flashlight +flashlights +flashpoint +flashpoints +flashy +flask +flasks +flat +flatbed +flatbeds +flatboat +flatboats +flatcar +flatcars +flatfeet +flatfish +flatfishes +flatfoot +flatfooted +flatfoots +flatiron +flatirons +flatland +flatly +flatness +flats +flatted +flatten +flattened +flattening +flattens +flatter +flattered +flatterer +flatterers +flattering +flatteringly +flatters +flattery +flattest +flatting +flattish +flattop +flattops +flatulence +flatulent +flatus +flatware +flatworm +flatworms +flaunt +flaunted +flaunting +flauntingly +flaunts +flautist +flautists +flavor +flavored +flavorful +flavoring +flavorings +flavorless +flavors +flavorsome +flavour +flavoured +flavouring +flavours +flaw +flawed +flawing +flawless +flawlessly +flawlessness +flaws +flax +flaxen +flay +flayed +flaying +flays +flea +fleabag +fleabags +fleas +fleck +flecked +flecking +flecks +fled +fledgeling +fledgelings +fledgling +fledglings +flee +fleece +fleeced +fleecer +fleecers +fleeces +fleecier +fleeciest +fleeciness +fleecing +fleecy +fleeing +flees +fleet +fleeted +fleeter +fleetest +fleeting +fleetingly +fleetingness +fleetly +fleetness +fleets +flesh +fleshed +fleshes +fleshier +fleshiest +fleshing +fleshlier +fleshliest +fleshly +fleshpot +fleshpots +fleshy +flew +flex +flexed +flexes +flexibility +flexible +flexibly +flexing +flexitime +flextime +flibbertigibbet +flibbertigibbets +flick +flicked +flicker +flickered +flickering +flickers +flicking +flicks +flied +flier +fliers +flies +fliest +flight +flightier +flightiest +flightiness +flightless +flights +flighty +flimflam +flimflammed +flimflamming +flimflams +flimsier +flimsiest +flimsily +flimsiness +flimsy +flinch +flinched +flinches +flinching +fling +flinging +flings +flint +flintier +flintiest +flintlock +flintlocks +flints +flinty +flip +flippancy +flippant +flippantly +flipped +flipper +flippers +flippest +flipping +flips +flirt +flirtation +flirtations +flirtatious +flirtatiously +flirtatiousness +flirted +flirting +flirts +flit +flits +flitted +flitting +float +floated +floater +floaters +floating +floats +flock +flocked +flocking +flocks +floe +floes +flog +flogged +flogger +floggers +flogging +floggings +flogs +flood +flooded +floodgate +floodgates +flooding +floodlight +floodlighted +floodlighting +floodlights +floodlit +floodplain +floodplains +floods +floodwater +floor +floorboard +floorboards +floored +flooring +floors +floorwalker +floorwalkers +floozie +floozies +floozy +flop +flophouse +flophouses +flopped +floppier +floppies +floppiest +floppily +floppiness +flopping +floppy +flops +flora +florae +floral +floras +florescence +florescent +floret +florets +florid +florider +floridest +floridly +floridness +florin +florins +florist +florists +floss +flossed +flosses +flossier +flossiest +flossing +flossy +flotation +flotations +flotilla +flotillas +flotsam +flounce +flounced +flounces +flouncier +flounciest +flouncing +flouncy +flounder +floundered +floundering +flounders +flour +floured +flouring +flourish +flourished +flourishes +flourishing +flours +floury +flout +flouted +flouter +flouters +flouting +flouts +flow +flowchart +flowcharts +flowed +flower +flowerbed +flowerbeds +flowered +flowerier +floweriest +floweriness +flowering +flowerless +flowerpot +flowerpots +flowers +flowery +flowing +flown +flows +flub +flubbed +flubbing +flubs +fluctuate +fluctuated +fluctuates +fluctuating +fluctuation +fluctuations +flue +fluency +fluent +fluently +flues +fluff +fluffed +fluffier +fluffiest +fluffiness +fluffing +fluffs +fluffy +fluid +fluidity +fluidly +fluids +fluke +flukes +flukey +flukier +flukiest +fluky +flume +flumes +flummox +flummoxed +flummoxes +flummoxing +flung +flunk +flunked +flunkey +flunkeys +flunkies +flunking +flunks +flunky +fluoresce +fluoresced +fluorescence +fluorescent +fluoresces +fluorescing +fluoridate +fluoridated +fluoridates +fluoridating +fluoridation +fluoride +fluorides +fluorine +fluorite +fluorocarbon +fluorocarbons +fluoroscope +fluoroscopes +fluoroscopic +flurried +flurries +flurry +flurrying +flush +flushed +flusher +flushes +flushest +flushing +fluster +flustered +flustering +flusters +flute +fluted +flutes +fluting +flutist +flutists +flutter +fluttered +fluttering +flutters +fluttery +flux +fluxed +fluxes +fluxing +flyable +flyblown +flyby +flybys +flycatcher +flycatchers +flyer +flyers +flying +flyleaf +flyleaves +flypaper +flypapers +flyspeck +flyspecked +flyspecking +flyspecks +flyswatter +flyswatters +flyway +flyways +flyweight +flyweights +flywheel +flywheels +foal +foaled +foaling +foals +foam +foamed +foamier +foamiest +foaminess +foaming +foams +foamy +fobbed +fobbing +fobs +focal +focally +foci +focus +focused +focuses +focusing +focussed +focusses +focussing +fodder +fodders +foes +foetal +foetus +foetuses +fogbound +fogey +fogeys +fogged +foggier +foggiest +foggily +fogginess +fogging +foggy +foghorn +foghorns +fogies +fogs +fogy +fogyish +foible +foibles +foil +foiled +foiling +foils +foist +foisted +foisting +foists +fold +foldaway +folded +folder +folders +folding +foldout +foldouts +folds +foliage +folio +folios +folk +folklore +folkloric +folklorist +folklorists +folks +folksier +folksiest +folksiness +folksinger +folksingers +folksinging +folksy +folktale +folktales +folkway +folkways +follicle +follicles +follies +follow +followed +follower +followers +following +followings +follows +folly +foment +fomentation +fomented +fomenting +foments +fond +fondant +fondants +fonder +fondest +fondle +fondled +fondles +fondling +fondly +fondness +fondu +fondue +fondues +font +fontanel +fontanelle +fontanelles +fontanels +fonts +food +foodie +foodies +foods +foodstuff +foodstuffs +fool +fooled +fooleries +foolery +foolhardier +foolhardiest +foolhardily +foolhardiness +foolhardy +fooling +foolish +foolishly +foolishness +foolproof +fools +foolscap +foot +footage +football +footballer +footballers +footballs +footbridge +footbridges +footed +footfall +footfalls +foothill +foothills +foothold +footholds +footing +footings +footless +footlights +footling +footlings +footlocker +footlockers +footloose +footman +footmen +footnote +footnoted +footnotes +footnoting +footpath +footpaths +footprint +footprints +footrace +footraces +footrest +footrests +foots +footsie +footsies +footsore +footstep +footsteps +footstool +footstools +footwear +footwork +foppery +foppish +foppishness +fops +fora +forage +foraged +forager +foragers +forages +foraging +foray +forayed +foraying +forays +forbad +forbade +forbear +forbearance +forbearing +forbears +forbid +forbidden +forbidding +forbiddingly +forbids +forbore +forborne +force +forced +forceful +forcefully +forcefulness +forceps +forces +forcible +forcibly +forcing +ford +fordable +forded +fording +fords +fore +forearm +forearmed +forearming +forearms +forebear +forebears +forebode +foreboded +forebodes +foreboding +forebodings +forecast +forecasted +forecaster +forecasters +forecasting +forecastle +forecastles +forecasts +foreclose +foreclosed +forecloses +foreclosing +foreclosure +foreclosures +forecourt +forecourts +foredoom +foredoomed +foredooming +foredooms +forefather +forefathers +forefeet +forefinger +forefingers +forefoot +forefront +forefronts +foregather +foregathered +foregathering +foregathers +forego +foregoes +foregoing +foregone +foreground +foregrounded +foregrounding +foregrounds +forehand +forehands +forehead +foreheads +foreign +foreigner +foreigners +foreignness +foreknew +foreknow +foreknowing +foreknowledge +foreknown +foreknows +foreleg +forelegs +forelimb +forelimbs +forelock +forelocks +foreman +foremast +foremasts +foremen +foremost +forename +forenamed +forenames +forenoon +forenoons +forensic +forensically +forensics +foreordain +foreordained +foreordaining +foreordains +forepart +foreparts +foreperson +forepersons +foreplay +forequarter +forequarters +forerunner +forerunners +fores +foresail +foresails +foresaw +foresee +foreseeable +foreseeing +foreseen +foreseer +foreseers +foresees +foreshadow +foreshadowed +foreshadowing +foreshadows +foreshorten +foreshortened +foreshortening +foreshortens +foresight +foresighted +foresightedness +foreskin +foreskins +forest +forestall +forestalled +forestalling +forestalls +forestation +forested +forester +foresters +foresting +forestland +forestry +forests +foreswear +foreswearing +foreswears +foreswore +foresworn +foretaste +foretasted +foretastes +foretasting +foretell +foretelling +foretells +forethought +foretold +forever +forevermore +forewarn +forewarned +forewarning +forewarns +forewent +forewoman +forewomen +foreword +forewords +forfeit +forfeited +forfeiting +forfeits +forfeiture +forgather +forgathered +forgathering +forgathers +forgave +forge +forged +forger +forgeries +forgers +forgery +forges +forget +forgetful +forgetfully +forgetfulness +forgets +forgettable +forgetting +forging +forgings +forgivable +forgive +forgiven +forgiveness +forgiver +forgivers +forgives +forgiving +forgo +forgoer +forgoers +forgoes +forgoing +forgone +forgot +forgotten +fork +forked +forkful +forkfuls +forking +forklift +forklifts +forks +forlorn +forlornly +form +formal +formaldehyde +formalism +formalist +formalists +formalities +formality +formalization +formalize +formalized +formalizes +formalizing +formally +formals +format +formated +formating +formation +formations +formative +formats +formatted +formatting +formed +former +formerly +formfitting +formic +formidable +formidably +forming +formless +formlessly +formlessness +forms +formula +formulae +formulaic +formulas +formulate +formulated +formulates +formulating +formulation +formulations +formulator +formulators +fornicate +fornicated +fornicates +fornicating +fornication +fornicator +fornicators +forsake +forsaken +forsakes +forsaking +forsook +forsooth +forswear +forswearing +forswears +forswore +forsworn +forsythia +forsythias +fort +forte +fortes +forth +forthcoming +forthright +forthrightly +forthrightness +forthwith +forties +fortieth +fortieths +fortification +fortifications +fortified +fortifier +fortifiers +fortifies +fortify +fortifying +fortissimo +fortitude +fortnight +fortnightly +fortnights +fortress +fortresses +forts +fortuitous +fortuitously +fortuitousness +fortuity +fortunate +fortunately +fortune +fortunes +fortuneteller +fortunetellers +fortunetelling +forty +forum +forums +forward +forwarded +forwarder +forwarders +forwardest +forwarding +forwardly +forwardness +forwards +forwent +fossil +fossilization +fossilize +fossilized +fossilizes +fossilizing +fossils +foster +fostered +fostering +fosters +fought +foul +foulard +fouled +fouler +foulest +fouling +foully +foulmouthed +foulness +fouls +found +foundation +foundations +founded +founder +foundered +foundering +founders +founding +foundling +foundlings +foundries +foundry +founds +fount +fountain +fountainhead +fountainheads +fountains +founts +four +fourfold +fourposter +fourposters +fours +fourscore +foursome +foursomes +foursquare +fourteen +fourteens +fourteenth +fourteenths +fourth +fourthly +fourths +fowl +fowled +fowling +fowls +foxed +foxes +foxfire +foxglove +foxgloves +foxhole +foxholes +foxhound +foxhounds +foxier +foxiest +foxily +foxiness +foxing +foxtrot +foxtrots +foxtrotted +foxtrotting +foxy +foyer +foyers +fracas +fracases +fractal +fractals +fraction +fractional +fractionally +fractions +fractious +fractiously +fractiousness +fracture +fractured +fractures +fracturing +fragile +fragility +fragment +fragmentary +fragmentation +fragmented +fragmenting +fragments +fragrance +fragrances +fragrant +fragrantly +frail +frailer +frailest +frailly +frailness +frailties +frailty +frame +framed +framer +framers +frames +framework +frameworks +framing +franc +franchise +franchised +franchisee +franchisees +franchiser +franchisers +franchises +franchising +franchisor +franchisors +francium +francs +frangibility +frangible +frank +franked +franker +frankest +frankfurter +frankfurters +frankincense +franking +frankly +frankness +franks +frantic +frantically +frappe +frappes +frat +fraternal +fraternally +fraternities +fraternity +fraternization +fraternize +fraternized +fraternizer +fraternizers +fraternizes +fraternizing +fratricidal +fratricide +fratricides +frats +fraud +frauds +fraudulence +fraudulent +fraudulently +fraught +fray +frayed +fraying +frays +frazzle +frazzled +frazzles +frazzling +freak +freaked +freakier +freakiest +freaking +freakish +freakishly +freakishness +freakout +freakouts +freaks +freaky +freckle +freckled +freckles +frecklier +freckliest +freckling +freckly +free +freebase +freebased +freebases +freebasing +freebee +freebees +freebie +freebies +freebooter +freebooters +freeborn +freed +freedman +freedmen +freedom +freedoms +freehand +freehold +freeholder +freeholders +freeholds +freeing +freelance +freelanced +freelancer +freelancers +freelances +freelancing +freeload +freeloaded +freeloader +freeloaders +freeloading +freeloads +freely +freeman +freemen +freer +frees +freest +freestanding +freestone +freestones +freestyle +freestyles +freethinker +freethinkers +freethinking +freeware +freeway +freeways +freewheel +freewheeled +freewheeling +freewheels +freewill +freezable +freeze +freezer +freezers +freezes +freezing +freight +freighted +freighter +freighters +freighting +freights +frenetic +frenetically +frenzied +frenziedly +frenzies +frenzy +frequencies +frequency +frequent +frequented +frequenter +frequenters +frequentest +frequenting +frequently +frequents +fresco +frescoes +frescos +fresh +freshen +freshened +freshener +fresheners +freshening +freshens +fresher +freshest +freshet +freshets +freshly +freshman +freshmen +freshness +freshwater +fret +fretful +fretfully +fretfulness +frets +fretsaw +fretsaws +fretted +fretting +fretwork +friable +friar +friaries +friars +friary +fricassee +fricasseed +fricasseeing +fricassees +fricative +fricatives +friction +frictional +fridge +fridges +fried +friedcake +friedcakes +friend +friendless +friendlier +friendlies +friendliest +friendliness +friendly +friends +friendship +friendships +frier +friers +fries +frieze +friezes +frig +frigate +frigates +frigged +frigging +fright +frighted +frighten +frightened +frightening +frighteningly +frightens +frightful +frightfully +frightfulness +frighting +frights +frigid +frigidity +frigidly +frigidness +frigs +frill +frillier +frilliest +frills +frilly +fringe +fringed +fringes +fringing +fripperies +frippery +frisk +frisked +friskier +friskiest +friskily +friskiness +frisking +frisks +frisky +fritter +frittered +frittering +fritters +fritz +frivolities +frivolity +frivolous +frivolously +frivolousness +friz +frizz +frizzed +frizzes +frizzier +frizziest +frizzing +frizzle +frizzled +frizzles +frizzlier +frizzliest +frizzling +frizzly +frizzy +frock +frocks +frog +frogman +frogmen +frogs +frolic +frolicked +frolicker +frolickers +frolicking +frolics +frolicsome +from +frond +fronds +front +frontage +frontages +frontal +frontally +fronted +frontier +frontiers +frontiersman +frontiersmen +fronting +frontispiece +frontispieces +frontrunner +frontrunners +fronts +frontward +frontwards +frosh +frost +frostbit +frostbite +frostbites +frostbiting +frostbitten +frosted +frostier +frostiest +frostily +frostiness +frosting +frostings +frosts +frosty +froth +frothed +frothier +frothiest +frothiness +frothing +froths +frothy +froufrou +froward +frowardness +frown +frowned +frowning +frowns +frowsier +frowsiest +frowsy +frowzier +frowziest +frowzily +frowziness +frowzy +froze +frozen +fructified +fructifies +fructify +fructifying +fructose +frugal +frugality +frugally +fruit +fruitcake +fruitcakes +fruited +fruitful +fruitfully +fruitfulness +fruitier +fruitiest +fruitiness +fruiting +fruition +fruitless +fruitlessly +fruitlessness +fruits +fruity +frump +frumpier +frumpiest +frumpish +frumps +frumpy +frusta +frustrate +frustrated +frustrates +frustrating +frustratingly +frustration +frustrations +frustum +frustums +fryer +fryers +frying +fuchsia +fuchsias +fuck +fucked +fucker +fuckers +fucking +fucks +fuddle +fuddled +fuddles +fuddling +fudge +fudged +fudges +fudging +fuehrer +fuehrers +fuel +fueled +fueling +fuelled +fuelling +fuels +fugal +fugitive +fugitives +fugue +fugues +fuhrer +fuhrers +fulcra +fulcrum +fulcrums +fulfil +fulfill +fulfilled +fulfilling +fulfillment +fulfills +fulfilment +fulfils +full +fullback +fullbacks +fulled +fuller +fullers +fullest +fulling +fullness +fulls +fully +fulminate +fulminated +fulminates +fulminating +fulmination +fulminations +fulness +fulsome +fulsomely +fulsomeness +fumble +fumbled +fumbler +fumblers +fumbles +fumbling +fumblingly +fume +fumed +fumes +fumier +fumiest +fumigant +fumigants +fumigate +fumigated +fumigates +fumigating +fumigation +fumigator +fumigators +fuming +fumy +function +functional +functionally +functionaries +functionary +functioned +functioning +functions +fund +fundamental +fundamentalism +fundamentalist +fundamentalists +fundamentally +fundamentals +funded +funding +fundraiser +fundraisers +funds +funeral +funerals +funerary +funereal +funereally +fungal +fungi +fungible +fungibles +fungicidal +fungicide +fungicides +fungoid +fungous +fungus +funguses +funicular +funiculars +funk +funked +funkier +funkiest +funkiness +funking +funks +funky +funnel +funneled +funneling +funnelled +funnelling +funnels +funner +funnest +funnier +funnies +funniest +funnily +funniness +funny +funnyman +funnymen +furbelow +furbish +furbished +furbishes +furbishing +furies +furious +furiously +furl +furled +furling +furlong +furlongs +furlough +furloughed +furloughing +furloughs +furls +furnace +furnaces +furnish +furnished +furnishes +furnishing +furnishings +furniture +furor +furore +furores +furors +furred +furrier +furriers +furriest +furriness +furring +furrow +furrowed +furrowing +furrows +furry +furs +further +furtherance +furthered +furthering +furthermore +furthermost +furthers +furthest +furtive +furtively +furtiveness +fury +furze +fuse +fused +fusee +fusees +fuselage +fuselages +fuses +fusibility +fusible +fusileer +fusileers +fusilier +fusiliers +fusillade +fusillades +fusing +fusion +fuss +fussbudget +fussbudgets +fussed +fusses +fussier +fussiest +fussily +fussiness +fussing +fusspot +fusspots +fussy +fustian +fustier +fustiest +fustiness +fusty +futile +futilely +futility +futon +futons +future +futures +futurism +futurist +futuristic +futurists +futurities +futurity +futurologist +futurologists +futurology +futz +futzed +futzes +futzing +fuze +fuzed +fuzes +fuzing +fuzz +fuzzed +fuzzes +fuzzier +fuzziest +fuzzily +fuzziness +fuzzing +fuzzy +gabardine +gabardines +gabbed +gabbier +gabbiest +gabbiness +gabbing +gabble +gabbled +gabbles +gabbling +gabby +gaberdine +gaberdines +gabfest +gabfests +gable +gabled +gables +gabs +gadabout +gadabouts +gadded +gadder +gadders +gadding +gadflies +gadfly +gadget +gadgetry +gadgets +gadolinium +gads +gaff +gaffe +gaffed +gaffer +gaffers +gaffes +gaffing +gaffs +gaga +gage +gaged +gages +gagged +gagging +gaggle +gaggles +gaging +gags +gaiety +gaily +gain +gained +gainer +gainers +gainful +gainfully +gaining +gains +gainsaid +gainsay +gainsayer +gainsayers +gainsaying +gainsays +gait +gaiter +gaiters +gaits +gala +galactic +galas +galaxies +galaxy +gale +galena +gales +gall +gallant +gallanter +gallantest +gallantly +gallantry +gallants +gallbladder +gallbladders +galled +galleon +galleons +galleria +gallerias +galleries +gallery +galley +galleys +gallimaufries +gallimaufry +galling +gallium +gallivant +gallivanted +gallivanting +gallivants +gallon +gallons +gallop +galloped +galloping +gallops +gallows +gallowses +galls +gallstone +gallstones +galoot +galoots +galore +galosh +galoshe +galoshes +gals +galumph +galumphed +galumphing +galumphs +galvanic +galvanise +galvanised +galvanises +galvanising +galvanism +galvanization +galvanize +galvanized +galvanizes +galvanizing +galvanometer +galvanometers +gambit +gambits +gamble +gambled +gambler +gamblers +gambles +gambling +gambol +gamboled +gamboling +gambolled +gambolling +gambols +game +gamecock +gamecocks +gamed +gamekeeper +gamekeepers +gamely +gameness +gamer +games +gamesmanship +gamest +gamester +gamesters +gamete +gametes +gametic +gamey +gamier +gamiest +gamin +gamine +gamines +gaminess +gaming +gamins +gamma +gammas +gammon +gamut +gamuts +gamy +gander +ganders +gang +gangbusters +ganged +ganging +gangland +ganglia +ganglier +gangliest +gangling +ganglion +ganglionic +ganglions +gangly +gangplank +gangplanks +gangrene +gangrened +gangrenes +gangrening +gangrenous +gangs +gangster +gangsters +gangway +gangways +gannet +gannets +gantlet +gantlets +gantries +gantry +gaol +gaoled +gaoler +gaolers +gaoling +gaols +gape +gaped +gapes +gaping +gaps +garage +garaged +garages +garaging +garb +garbage +garbanzo +garbanzos +garbed +garbing +garble +garbled +garbles +garbling +garbs +garcon +garcons +garden +gardened +gardener +gardeners +gardenia +gardenias +gardening +gardens +garfish +garfishes +gargantuan +gargle +gargled +gargles +gargling +gargoyle +gargoyles +garish +garishly +garishness +garland +garlanded +garlanding +garlands +garlic +garlicky +garment +garments +garner +garnered +garnering +garners +garnet +garnets +garnish +garnished +garnishee +garnisheed +garnisheeing +garnishees +garnishes +garnishing +garnishment +garnishments +garote +garoted +garotes +garoting +garotte +garotted +garottes +garotting +garret +garrets +garrison +garrisoned +garrisoning +garrisons +garrote +garroted +garroter +garroters +garrotes +garroting +garrotte +garrotted +garrottes +garrotting +garrulity +garrulous +garrulously +garrulousness +gars +garter +garters +gasbag +gasbags +gaseous +gases +gash +gashed +gashes +gashing +gasket +gaskets +gaslight +gaslights +gasohol +gasolene +gasoline +gasp +gasped +gasping +gasps +gassed +gasses +gassier +gassiest +gassing +gassy +gastric +gastritis +gastroenteritis +gastrointestinal +gastronomic +gastronomical +gastronomically +gastronomy +gastropod +gastropods +gasworks +gate +gatecrash +gatecrashed +gatecrasher +gatecrashers +gatecrashes +gatecrashing +gated +gatehouse +gatehouses +gatekeeper +gatekeepers +gatepost +gateposts +gates +gateway +gateways +gather +gathered +gatherer +gatherers +gathering +gatherings +gathers +gating +gator +gators +gauche +gauchely +gaucheness +gaucher +gaucherie +gauchest +gaucho +gauchos +gaudier +gaudiest +gaudily +gaudiness +gaudy +gauge +gauged +gauges +gauging +gaunt +gaunter +gauntest +gauntlet +gauntlets +gauntness +gauze +gauzier +gauziest +gauziness +gauzy +gave +gavel +gavels +gavotte +gavottes +gawk +gawked +gawkier +gawkiest +gawkily +gawkiness +gawking +gawks +gawky +gayer +gayest +gayety +gayly +gayness +gays +gaze +gazebo +gazeboes +gazebos +gazed +gazelle +gazelles +gazer +gazers +gazes +gazette +gazetted +gazetteer +gazetteers +gazettes +gazetting +gazing +gazpacho +gear +gearbox +gearboxes +geared +gearing +gears +gearshift +gearshifts +gearwheel +gearwheels +gecko +geckoes +geckos +geed +geegaw +geegaws +geeing +geek +geekier +geekiest +geeks +geeky +gees +geese +geez +geezer +geezers +geisha +geishas +gelatin +gelatine +gelatinous +gelcap +gelcaps +geld +gelded +gelding +geldings +gelds +gelid +gelignite +gelled +gelling +gels +gelt +gemmology +gemological +gemologist +gemologists +gemology +gems +gemstone +gemstones +gendarme +gendarmes +gender +genders +gene +genealogical +genealogically +genealogies +genealogist +genealogists +genealogy +genera +general +generalissimo +generalissimos +generalist +generalists +generalities +generality +generalization +generalizations +generalize +generalized +generalizes +generalizing +generally +generals +generalship +generate +generated +generates +generating +generation +generational +generations +generative +generator +generators +generic +generically +generics +generosities +generosity +generous +generously +generousness +genes +geneses +genesis +genetic +genetically +geneticist +geneticists +genetics +genial +geniality +genially +genie +genies +genii +genital +genitalia +genitally +genitals +genitive +genitives +genitourinary +genius +geniuses +genocidal +genocide +genome +genomes +genre +genres +gent +genteel +genteelly +genteelness +gentian +gentians +gentile +gentiles +gentility +gentle +gentled +gentlefolk +gentlefolks +gentleman +gentlemanly +gentlemen +gentleness +gentler +gentles +gentlest +gentlewoman +gentlewomen +gentling +gently +gentries +gentrification +gentrified +gentrifies +gentrify +gentrifying +gentry +gents +genuflect +genuflected +genuflecting +genuflection +genuflections +genuflects +genuine +genuinely +genuineness +genus +genuses +geocentric +geocentrically +geochemistry +geode +geodes +geodesic +geodesics +geodesy +geodetic +geographer +geographers +geographic +geographical +geographically +geographies +geography +geologic +geological +geologically +geologies +geologist +geologists +geology +geomagnetic +geomagnetism +geometric +geometrical +geometrically +geometries +geometry +geophysical +geophysicist +geophysicists +geophysics +geopolitical +geopolitics +geostationary +geosynchronous +geosyncline +geosynclines +geothermal +geothermic +geranium +geraniums +gerbil +gerbils +geriatric +geriatrics +germ +germane +germanium +germicidal +germicide +germicides +germinal +germinate +germinated +germinates +germinating +germination +germs +gerontological +gerontologist +gerontologists +gerontology +gerrymander +gerrymandered +gerrymandering +gerrymanders +gerund +gerunds +gestapo +gestapos +gestate +gestated +gestates +gestating +gestation +gestational +gesticulate +gesticulated +gesticulates +gesticulating +gesticulation +gesticulations +gestural +gesture +gestured +gestures +gesturing +gesundheit +getaway +getaways +gets +getting +getup +gewgaw +gewgaws +geyser +geysered +geysering +geysers +ghastlier +ghastliest +ghastliness +ghastly +ghat +ghats +gherkin +gherkins +ghetto +ghettoes +ghettoize +ghettoized +ghettoizes +ghettoizing +ghettos +ghost +ghosted +ghosting +ghostlier +ghostliest +ghostliness +ghostly +ghosts +ghostwrite +ghostwriter +ghostwriters +ghostwrites +ghostwriting +ghostwritten +ghostwrote +ghoul +ghoulish +ghoulishly +ghoulishness +ghouls +giant +giantess +giantesses +giants +gibber +gibbered +gibbering +gibberish +gibbers +gibbet +gibbeted +gibbeting +gibbets +gibbon +gibbons +gibbous +gibe +gibed +gibes +gibing +giblet +giblets +giddier +giddiest +giddily +giddiness +giddy +gift +gifted +gifting +gifts +gigabyte +gigabytes +gigahertz +gigantic +gigantically +gigged +gigging +giggle +giggled +giggler +gigglers +giggles +gigglier +giggliest +giggling +giggly +gigolo +gigolos +gigs +gild +gilded +gilder +gilders +gilding +gilds +gill +gills +gilt +gilts +gimbals +gimcrack +gimcrackery +gimcracks +gimlet +gimleted +gimleting +gimlets +gimme +gimmes +gimmick +gimmickry +gimmicks +gimmicky +gimp +gimped +gimpier +gimpiest +gimping +gimps +gimpy +ginger +gingerbread +gingerly +gingersnap +gingersnaps +gingery +gingham +gingivitis +gingko +gingkoes +gingkos +ginkgo +ginkgoes +ginkgos +ginned +ginning +gins +ginseng +gipsies +gipsy +giraffe +giraffes +gird +girded +girder +girders +girding +girdle +girdled +girdles +girdling +girds +girl +girlfriend +girlfriends +girlhood +girlhoods +girlish +girlishly +girlishness +girls +girly +girt +girted +girth +girths +girting +girts +gismo +gismos +gist +give +giveaway +giveaways +giveback +givebacks +given +givens +giver +givers +gives +giving +gizmo +gizmos +gizzard +gizzards +glace +glaceed +glaceing +glaces +glacial +glacially +glaciate +glaciated +glaciates +glaciating +glaciation +glaciations +glacier +glaciers +glad +gladden +gladdened +gladdening +gladdens +gladder +gladdest +glade +glades +gladiator +gladiatorial +gladiators +gladiola +gladiolas +gladioli +gladiolus +gladioluses +gladly +gladness +glads +gladsome +glamor +glamorization +glamorize +glamorized +glamorizes +glamorizing +glamorous +glamorously +glamour +glamourize +glamourized +glamourizes +glamourizing +glamourous +glance +glanced +glances +glancing +gland +glandes +glands +glandular +glans +glare +glared +glares +glaring +glaringly +glasnost +glass +glassblower +glassblowers +glassblowing +glassed +glasses +glassful +glassfuls +glassier +glassiest +glassily +glassiness +glassing +glassware +glassy +glaucoma +glaze +glazed +glazes +glazier +glaziers +glazing +gleam +gleamed +gleaming +gleams +glean +gleaned +gleaner +gleaners +gleaning +gleanings +gleans +glee +gleeful +gleefully +gleefulness +glen +glens +glib +glibber +glibbest +glibly +glibness +glide +glided +glider +gliders +glides +gliding +glimmer +glimmered +glimmering +glimmerings +glimmers +glimpse +glimpsed +glimpses +glimpsing +glint +glinted +glinting +glints +glissandi +glissando +glissandos +glisten +glistened +glistening +glistens +glister +glistered +glistering +glisters +glitch +glitches +glitter +glittered +glittering +glitters +glittery +glitz +glitzier +glitziest +glitzy +gloaming +gloamings +gloat +gloated +gloating +gloatingly +gloats +glob +global +globalism +globalist +globalists +globalization +globalize +globalized +globalizes +globalizing +globally +globe +globes +globetrotter +globetrotters +globs +globular +globule +globules +globulin +glockenspiel +glockenspiels +gloom +gloomier +gloomiest +gloomily +gloominess +gloomy +glop +gloppier +gloppiest +gloppy +gloried +glories +glorification +glorified +glorifies +glorify +glorifying +glorious +gloriously +glory +glorying +gloss +glossaries +glossary +glossed +glosses +glossier +glossies +glossiest +glossily +glossiness +glossing +glossolalia +glossy +glottal +glottides +glottis +glottises +glove +gloved +gloves +gloving +glow +glowed +glower +glowered +glowering +glowers +glowing +glowingly +glows +glowworm +glowworms +glucose +glue +glued +glueing +glues +gluey +gluier +gluiest +gluing +glum +glumly +glummer +glummest +glumness +glut +gluten +glutenous +glutinous +glutinously +gluts +glutted +glutting +glutton +gluttonous +gluttonously +gluttons +gluttony +glycerin +glycerine +glycerol +glycogen +gnarl +gnarled +gnarling +gnarls +gnash +gnashed +gnashes +gnashing +gnat +gnats +gnaw +gnawed +gnawing +gnawn +gnaws +gneiss +gnome +gnomes +gnomic +gnomish +gnus +goad +goaded +goading +goads +goal +goalie +goalies +goalkeeper +goalkeepers +goalkeeping +goalpost +goalposts +goals +goaltender +goaltenders +goat +goatee +goatees +goatherd +goatherds +goats +goatskin +goatskins +gobbed +gobbet +gobbets +gobbing +gobble +gobbled +gobbledegook +gobbledygook +gobbler +gobblers +gobbles +gobbling +goblet +goblets +goblin +goblins +gobs +godchild +godchildren +goddammit +goddamn +goddamned +goddaughter +goddaughters +goddess +goddesses +godfather +godfathers +godforsaken +godhead +godhood +godless +godlessness +godlier +godliest +godlike +godliness +godly +godmother +godmothers +godparent +godparents +gods +godsend +godsends +godson +godsons +goer +goers +goes +gofer +gofers +goggle +goggled +goggles +goggling +going +goings +goiter +goiters +goitre +goitres +gold +goldbrick +goldbricked +goldbricker +goldbrickers +goldbricking +goldbricks +golden +goldener +goldenest +goldenrod +goldfinch +goldfinches +goldfish +goldfishes +goldmine +goldmines +golds +goldsmith +goldsmiths +golf +golfed +golfer +golfers +golfing +golfs +gollies +golly +gonad +gonadal +gonads +gondola +gondolas +gondolier +gondoliers +gone +goner +goners +gong +gonged +gonging +gongs +gonna +gonorrhea +gonorrheal +gonorrhoea +goober +goobers +good +goodby +goodbye +goodbyes +goodbys +goodhearted +goodie +goodies +goodish +goodlier +goodliest +goodly +goodness +goods +goodwill +goody +gooey +goof +goofball +goofballs +goofed +goofier +goofiest +goofiness +goofing +goofs +goofy +gooier +gooiest +gook +gooks +goon +goons +goop +goose +gooseberries +gooseberry +goosebumps +goosed +gooseflesh +gooses +goosing +gopher +gophers +gore +gored +gores +gorge +gorged +gorgeous +gorgeously +gorgeousness +gorges +gorging +gorgon +gorgons +gorier +goriest +gorilla +gorillas +gorily +goriness +goring +gormandize +gormandized +gormandizer +gormandizers +gormandizes +gormandizing +gorp +gorse +gory +gosh +goshawk +goshawks +gosling +goslings +gospel +gospels +gossamer +gossip +gossiped +gossiper +gossipers +gossiping +gossipped +gossipping +gossips +gossipy +gotcha +gotta +gotten +gouge +gouged +gouger +gougers +gouges +gouging +goulash +goulashes +gourd +gourde +gourdes +gourds +gourmand +gourmands +gourmet +gourmets +gout +goutier +goutiest +gouty +govern +governable +governance +governed +governess +governesses +governing +government +governmental +governments +governor +governors +governorship +governs +gown +gowned +gowning +gowns +grab +grabbed +grabber +grabbers +grabbier +grabbiest +grabbing +grabby +grabs +grace +graced +graceful +gracefully +gracefulness +graceless +gracelessly +gracelessness +graces +gracing +gracious +graciously +graciousness +grackle +grackles +grad +gradate +gradated +gradates +gradating +gradation +gradations +grade +graded +grader +graders +grades +gradient +gradients +grading +grads +gradual +gradualism +gradually +gradualness +graduate +graduated +graduates +graduating +graduation +graduations +graffiti +graffito +graft +grafted +grafter +grafters +grafting +grafts +graham +grain +grained +grainier +grainiest +graininess +grains +grainy +gram +grammar +grammarian +grammarians +grammars +grammatical +grammatically +gramme +grammes +gramophone +gramophones +grampus +grampuses +grams +granaries +granary +grand +grandam +grandams +grandaunt +grandaunts +grandchild +grandchildren +granddad +granddaddies +granddaddy +granddads +granddaughter +granddaughters +grandee +grandees +grander +grandest +grandeur +grandfather +grandfathered +grandfathering +grandfatherly +grandfathers +grandiloquence +grandiloquent +grandiose +grandiosely +grandiosity +grandly +grandma +grandmas +grandmother +grandmotherly +grandmothers +grandnephew +grandnephews +grandness +grandniece +grandnieces +grandpa +grandparent +grandparents +grandpas +grands +grandson +grandsons +grandstand +grandstanded +grandstanding +grandstands +granduncle +granduncles +grange +granges +granite +granitic +grannie +grannies +granny +granola +grant +granted +grantee +grantees +granter +granters +granting +grantor +grantors +grants +grantsmanship +granular +granularity +granulate +granulated +granulates +granulating +granulation +granule +granules +grape +grapefruit +grapefruits +grapes +grapeshot +grapevine +grapevines +graph +graphed +graphic +graphical +graphically +graphics +graphing +graphite +graphologist +graphologists +graphology +graphs +grapnel +grapnels +grapple +grappled +grapples +grappling +grasp +graspable +grasped +grasping +grasps +grass +grassed +grasses +grasshopper +grasshoppers +grassier +grassiest +grassing +grassland +grassy +grate +grated +grateful +gratefully +gratefulness +grater +graters +grates +gratification +gratifications +gratified +gratifies +gratify +gratifying +grating +gratingly +gratings +gratis +gratitude +gratuities +gratuitous +gratuitously +gratuitousness +gratuity +gravamen +gravamens +gravamina +grave +graved +gravedigger +gravediggers +gravel +graveled +graveling +gravelled +gravelling +gravelly +gravels +gravely +graven +graveness +graver +graves +graveside +gravesides +gravest +gravestone +gravestones +graveyard +graveyards +gravid +gravies +gravimeter +gravimeters +graving +gravitate +gravitated +gravitates +gravitating +gravitation +gravitational +gravity +gravy +gray +graybeard +graybeards +grayed +grayer +grayest +graying +grayish +grayness +grays +graze +grazed +grazer +grazers +grazes +grazing +grease +greased +greasepaint +greases +greasier +greasiest +greasily +greasiness +greasing +greasy +great +greatcoat +greatcoats +greater +greatest +greathearted +greatly +greatness +greats +grebe +grebes +greed +greedier +greediest +greedily +greediness +greedy +green +greenback +greenbacks +greenbelt +greenbelts +greened +greener +greenery +greenest +greengage +greengages +greengrocer +greengrocers +greenhorn +greenhorns +greenhouse +greenhouses +greening +greenish +greenly +greenmail +greenness +greenroom +greenrooms +greens +greensward +greenwood +greet +greeted +greeter +greeters +greeting +greetings +greets +gregarious +gregariously +gregariousness +gremlin +gremlins +grenade +grenades +grenadier +grenadiers +grenadine +grew +grey +greyed +greyer +greyest +greyhound +greyhounds +greying +greys +grid +griddle +griddlecake +griddlecakes +griddles +gridiron +gridirons +gridlock +gridlocked +gridlocks +grids +grief +griefs +grievance +grievances +grieve +grieved +griever +grievers +grieves +grieving +grievous +grievously +grievousness +griffin +griffins +griffon +griffons +grill +grille +grilled +grilles +grilling +grills +grim +grimace +grimaced +grimaces +grimacing +grime +grimed +grimes +grimier +grimiest +griminess +griming +grimly +grimmer +grimmest +grimness +grimy +grin +grind +grinder +grinders +grinding +grinds +grindstone +grindstones +gringo +gringos +grinned +grinning +grins +grip +gripe +griped +griper +gripers +gripes +griping +grippe +gripped +gripper +grippers +gripping +grips +gript +grislier +grisliest +grisliness +grisly +grist +gristle +gristlier +gristliest +gristly +gristmill +gristmills +grit +grits +gritted +gritter +gritters +grittier +grittiest +grittiness +gritting +gritty +grizzled +grizzlier +grizzlies +grizzliest +grizzly +groan +groaned +groaning +groans +groat +groats +grocer +groceries +grocers +grocery +grog +groggier +groggiest +groggily +grogginess +groggy +groin +groins +grommet +grommets +groom +groomed +groomer +groomers +grooming +grooms +groomsman +groomsmen +groove +grooved +grooves +groovier +grooviest +grooving +groovy +grope +groped +groper +gropers +gropes +groping +grosbeak +grosbeaks +grosgrain +gross +grossed +grosser +grosses +grossest +grossing +grossly +grossness +grotesque +grotesquely +grotesqueness +grotesques +grotto +grottoes +grottos +grouch +grouched +grouches +grouchier +grouchiest +grouchily +grouchiness +grouching +grouchy +ground +groundbreaking +groundbreakings +grounded +grounder +grounders +groundhog +groundhogs +grounding +groundings +groundless +groundlessly +groundnut +groundnuts +grounds +groundswell +groundswells +groundwater +groundwork +group +grouped +grouper +groupers +groupie +groupies +grouping +groupings +groups +groupware +grouse +groused +grouser +grousers +grouses +grousing +grout +grouted +grouting +grouts +grove +grovel +groveled +groveler +grovelers +groveling +grovelled +groveller +grovellers +grovelling +grovels +groves +grow +grower +growers +growing +growl +growled +growler +growlers +growling +growls +grown +grownup +grownups +grows +growth +growths +grub +grubbed +grubber +grubbers +grubbier +grubbiest +grubbily +grubbiness +grubbing +grubby +grubs +grubstake +grudge +grudged +grudges +grudging +grudgingly +gruel +grueling +gruelling +gruesome +gruesomely +gruesomeness +gruesomer +gruesomest +gruff +gruffer +gruffest +gruffly +gruffness +grumble +grumbled +grumbler +grumblers +grumbles +grumbling +grump +grumpier +grumpiest +grumpily +grumpiness +grumps +grumpy +grunge +grungier +grungiest +grungy +grunion +grunions +grunt +grunted +grunting +grunts +gryphon +gryphons +guacamole +guanine +guano +guarani +guaranies +guaranis +guarantee +guaranteed +guaranteeing +guarantees +guarantied +guaranties +guarantor +guarantors +guaranty +guarantying +guard +guarded +guardedly +guarder +guarders +guardhouse +guardhouses +guardian +guardians +guardianship +guarding +guardrail +guardrails +guardroom +guardrooms +guards +guardsman +guardsmen +guava +guavas +gubernatorial +guerilla +guerillas +guerrilla +guerrillas +guess +guessed +guesser +guessers +guesses +guessing +guesstimate +guesstimated +guesstimates +guesstimating +guesswork +guest +guested +guesting +guests +guff +guffaw +guffawed +guffawing +guffaws +guidance +guide +guidebook +guidebooks +guided +guideline +guidelines +guidepost +guideposts +guider +guiders +guides +guiding +guild +guilder +guilders +guildhall +guildhalls +guilds +guile +guileful +guileless +guilelessly +guilelessness +guillotine +guillotined +guillotines +guillotining +guilt +guilted +guiltier +guiltiest +guiltily +guiltiness +guilting +guiltless +guilts +guilty +guinea +guineas +guise +guises +guitar +guitarist +guitarists +guitars +gulag +gulags +gulch +gulches +gulden +guldens +gulf +gulfs +gull +gulled +gullet +gullets +gulley +gulleys +gullibility +gullible +gullies +gulling +gulls +gully +gulp +gulped +gulper +gulpers +gulping +gulps +gumbo +gumboil +gumboils +gumbos +gumdrop +gumdrops +gummed +gummier +gummiest +gumming +gummy +gumption +gums +gumshoe +gumshoed +gumshoeing +gumshoes +gunboat +gunboats +gunfight +gunfighter +gunfighters +gunfights +gunfire +gunk +gunkier +gunkiest +gunky +gunman +gunmen +gunmetal +gunned +gunnel +gunnels +gunner +gunners +gunnery +gunning +gunny +gunnysack +gunnysacks +gunpoint +gunpowder +gunrunner +gunrunners +gunrunning +guns +gunship +gunships +gunshot +gunshots +gunslinger +gunslingers +gunsmith +gunsmiths +gunwale +gunwales +guppies +guppy +gurgle +gurgled +gurgles +gurgling +gurney +gurneys +guru +gurus +gush +gushed +gusher +gushers +gushes +gushier +gushiest +gushing +gushy +gusset +gusseted +gusseting +gussets +gussied +gussies +gussy +gussying +gust +gustatory +gusted +gustier +gustiest +gustily +gusting +gusto +gusts +gusty +gutless +gutlessness +guts +gutsier +gutsiest +gutsy +gutted +gutter +guttered +guttering +gutters +guttersnipe +guttersnipes +guttier +guttiest +gutting +guttural +gutturals +gutty +guyed +guying +guys +guzzle +guzzled +guzzler +guzzlers +guzzles +guzzling +gymkhana +gymkhanas +gymnasia +gymnasium +gymnasiums +gymnast +gymnastic +gymnastically +gymnastics +gymnasts +gymnosperm +gymnosperms +gyms +gynaecology +gynecologic +gynecological +gynecologist +gynecologists +gynecology +gypped +gypper +gyppers +gypping +gyps +gypsies +gypster +gypsters +gypsum +gypsy +gyrate +gyrated +gyrates +gyrating +gyration +gyrations +gyrator +gyrators +gyrfalcon +gyrfalcons +gyro +gyros +gyroscope +gyroscopes +gyroscopic +gyve +gyved +gyves +gyving +haberdasher +haberdasheries +haberdashers +haberdashery +habiliment +habiliments +habit +habitability +habitable +habitat +habitation +habitations +habitats +habits +habitual +habitually +habitualness +habituate +habituated +habituates +habituating +habituation +habitue +habitues +hacienda +haciendas +hack +hacked +hacker +hackers +hacking +hackle +hackles +hackney +hackneyed +hackneying +hackneys +hacks +hacksaw +hacksaws +hackwork +haddock +haddocks +hadj +hadjes +hadji +hadjis +hadst +haemoglobin +haemophilia +haemorrhage +haemorrhaged +haemorrhages +haemorrhaging +haemorrhoids +hafnium +haft +hafts +haggard +haggardly +haggardness +haggis +haggises +haggish +haggle +haggled +haggler +hagglers +haggles +haggling +hagiographer +hagiographers +hagiographies +hagiography +hags +hahnium +haiku +hail +hailed +hailing +hails +hailstone +hailstones +hailstorm +hailstorms +hair +hairball +hairballs +hairbreadth +hairbreadths +hairbrush +hairbrushes +haircloth +haircut +haircuts +hairdo +hairdos +hairdresser +hairdressers +hairdressing +hairdryer +hairdryers +haired +hairier +hairiest +hairiness +hairless +hairlike +hairline +hairlines +hairnet +hairnets +hairpiece +hairpieces +hairpin +hairpins +hairs +hairsbreadth +hairsbreadths +hairsplitter +hairsplitters +hairsplitting +hairspring +hairsprings +hairstyle +hairstyles +hairstylist +hairstylists +hairy +hajj +hajjes +hajji +hajjis +hake +hakes +halal +halberd +halberds +halcyon +hale +haled +haler +hales +halest +half +halfback +halfbacks +halfhearted +halfheartedly +halfheartedness +halfpence +halfpennies +halfpenny +halftime +halftimes +halftone +halftones +halfway +halfwit +halfwits +halibut +halibuts +haling +halite +halitosis +hall +halleluiah +halleluiahs +hallelujah +hallelujahs +halliard +halliards +hallmark +hallmarked +hallmarking +hallmarks +hallo +halloo +hallooed +hallooing +halloos +hallos +hallow +hallowed +hallowing +hallows +halls +hallucinate +hallucinated +hallucinates +hallucinating +hallucination +hallucinations +hallucinatory +hallucinogen +hallucinogenic +hallucinogenics +hallucinogens +hallway +hallways +halo +haloed +haloes +halogen +halogens +haloing +halos +halt +halted +halter +haltered +haltering +halters +halting +haltingly +halts +halve +halved +halves +halving +halyard +halyards +hamburg +hamburger +hamburgers +hamburgs +hamlet +hamlets +hammed +hammer +hammered +hammerer +hammerers +hammerhead +hammerheads +hammering +hammerlock +hammerlocks +hammers +hammertoe +hammertoes +hammier +hammiest +hamming +hammock +hammocks +hammy +hamper +hampered +hampering +hampers +hams +hamster +hamsters +hamstring +hamstringing +hamstrings +hamstrung +hand +handbag +handbags +handball +handballs +handbarrow +handbarrows +handbill +handbills +handbook +handbooks +handcar +handcars +handcart +handcarts +handclasp +handclasps +handcraft +handcrafted +handcrafting +handcrafts +handcuff +handcuffed +handcuffing +handcuffs +handed +handful +handfuls +handgun +handguns +handhold +handholds +handicap +handicapped +handicapper +handicappers +handicapping +handicaps +handicraft +handicrafts +handier +handiest +handily +handiness +handing +handiwork +handkerchief +handkerchiefs +handkerchieves +handle +handlebar +handlebars +handled +handler +handlers +handles +handling +handmade +handmaid +handmaiden +handmaidens +handmaids +handout +handouts +handpick +handpicked +handpicking +handpicks +handrail +handrails +hands +handsaw +handsaws +handset +handsets +handsful +handshake +handshakes +handsome +handsomely +handsomeness +handsomer +handsomest +handspring +handsprings +handstand +handstands +handwork +handwoven +handwriting +handwritten +handy +handyman +handymen +hang +hangar +hangars +hangdog +hanged +hanger +hangers +hanging +hangings +hangman +hangmen +hangnail +hangnails +hangout +hangouts +hangover +hangovers +hangs +hangup +hangups +hank +hanker +hankered +hankering +hankerings +hankers +hankie +hankies +hanks +hanky +hansom +hansoms +haphazard +haphazardly +haphazardness +hapless +haplessly +haplessness +haploid +haploids +haply +happen +happened +happening +happenings +happens +happenstance +happenstances +happier +happiest +happily +happiness +happy +harangue +harangued +harangues +haranguing +harass +harassed +harasser +harassers +harasses +harassing +harassment +harbinger +harbingers +harbor +harbored +harboring +harbors +harbour +harboured +harbouring +harbours +hard +hardback +hardbacks +hardball +hardboard +hardbound +hardcore +hardcover +hardcovers +harden +hardened +hardener +hardeners +hardening +hardens +harder +hardest +hardhat +hardhats +hardheaded +hardheadedly +hardheadedness +hardhearted +hardheartedly +hardheartedness +hardier +hardiest +hardihood +hardily +hardiness +hardline +hardliner +hardliners +hardly +hardness +hardscrabble +hardship +hardships +hardstand +hardstands +hardtack +hardtop +hardtops +hardware +hardwood +hardwoods +hardworking +hardy +hare +harebell +harebells +harebrained +hared +harelip +harelipped +harelips +harem +harems +hares +haring +hark +harked +harken +harkened +harkening +harkens +harking +harks +harlequin +harlequins +harlot +harlotry +harlots +harm +harmed +harmful +harmfully +harmfulness +harming +harmless +harmlessly +harmlessness +harmonic +harmonica +harmonically +harmonicas +harmonics +harmonies +harmonious +harmoniously +harmoniousness +harmonium +harmoniums +harmonization +harmonize +harmonized +harmonizer +harmonizers +harmonizes +harmonizing +harmony +harms +harness +harnessed +harnesses +harnessing +harp +harped +harpies +harping +harpist +harpists +harpoon +harpooned +harpooner +harpooners +harpooning +harpoons +harps +harpsichord +harpsichordist +harpsichordists +harpsichords +harpy +harridan +harridans +harried +harrier +harriers +harries +harrow +harrowed +harrowing +harrows +harry +harrying +harsh +harsher +harshest +harshly +harshness +hart +harts +harvest +harvested +harvester +harvesters +harvesting +harvests +hash +hashed +hasheesh +hashes +hashing +hashish +hasp +hasps +hassle +hassled +hassles +hassling +hassock +hassocks +hast +haste +hasted +hasten +hastened +hastening +hastens +hastes +hastier +hastiest +hastily +hastiness +hasting +hasty +hatbox +hatboxes +hatch +hatchback +hatchbacks +hatcheck +hatchecks +hatched +hatcheries +hatchery +hatches +hatchet +hatchets +hatching +hatchway +hatchways +hate +hated +hateful +hatefully +hatefulness +hatemonger +hatemongers +hater +haters +hates +hath +hating +hatred +hatreds +hats +hatted +hatter +hatters +hatting +hauberk +hauberks +haughtier +haughtiest +haughtily +haughtiness +haughty +haul +haulage +hauled +hauler +haulers +hauling +hauls +haunch +haunches +haunt +haunted +haunter +haunters +haunting +hauntingly +haunts +hauteur +have +haven +havens +haversack +haversacks +haves +having +havoc +hawed +hawing +hawk +hawked +hawker +hawkers +hawking +hawkish +hawkishness +hawks +haws +hawser +hawsers +hawthorn +hawthorns +haycock +haycocks +hayed +haying +hayloft +haylofts +haymow +haymows +hayrick +hayricks +hayride +hayrides +hays +hayseed +hayseeds +haystack +haystacks +haywire +hazard +hazarded +hazarding +hazardous +hazardously +hazards +haze +hazed +hazel +hazelnut +hazelnuts +hazels +hazer +hazers +hazes +hazier +haziest +hazily +haziness +hazing +hazings +hazy +head +headache +headaches +headband +headbands +headboard +headboards +headdress +headdresses +headed +header +headers +headfirst +headgear +headhunt +headhunted +headhunter +headhunters +headhunting +headhunts +headier +headiest +headily +headiness +heading +headings +headlamp +headlamps +headland +headlands +headless +headlight +headlights +headline +headlined +headliner +headliners +headlines +headlining +headlock +headlocks +headlong +headman +headmaster +headmasters +headmen +headmistress +headmistresses +headphone +headphones +headpiece +headpieces +headpin +headpins +headquarter +headquartered +headquartering +headquarters +headrest +headrests +headroom +heads +headset +headsets +headship +headships +headshrinker +headshrinkers +headsman +headsmen +headstall +headstalls +headstand +headstands +headstone +headstones +headstrong +headwaiter +headwaiters +headwaters +headway +headwind +headwinds +headword +headwords +heady +heal +healed +healer +healers +healing +heals +health +healthcare +healthful +healthfully +healthfulness +healthier +healthiest +healthily +healthiness +healthy +heap +heaped +heaping +heaps +hear +heard +hearer +hearers +hearing +hearings +hearken +hearkened +hearkening +hearkens +hears +hearsay +hearse +hearses +heart +heartache +heartaches +heartbeat +heartbeats +heartbreak +heartbreaking +heartbreaks +heartbroken +heartburn +hearten +heartened +heartening +heartens +heartfelt +hearth +hearths +hearthstone +hearthstones +heartier +hearties +heartiest +heartily +heartiness +heartland +heartlands +heartless +heartlessly +heartlessness +heartrending +heartrendingly +hearts +heartsick +heartsickness +heartstrings +heartthrob +heartthrobs +heartwarming +heartwood +hearty +heat +heated +heatedly +heater +heaters +heath +heathen +heathendom +heathenish +heathenism +heathens +heather +heaths +heating +heatproof +heatproofed +heatproofing +heatproofs +heats +heatstroke +heave +heaved +heaven +heavenlier +heavenliest +heavenly +heavens +heavenward +heavenwards +heaver +heavers +heaves +heavier +heavies +heaviest +heavily +heaviness +heaving +heavy +heavyhearted +heavyset +heavyweight +heavyweights +heck +heckle +heckled +heckler +hecklers +heckles +heckling +hectare +hectares +hectic +hectically +hectogram +hectograms +hectometer +hectometers +hector +hectored +hectoring +hectors +hedge +hedged +hedgehog +hedgehogs +hedgehop +hedgehopped +hedgehopping +hedgehops +hedger +hedgerow +hedgerows +hedgers +hedges +hedging +hedonism +hedonist +hedonistic +hedonists +heed +heeded +heedful +heedfully +heeding +heedless +heedlessly +heedlessness +heeds +heehaw +heehawed +heehawing +heehaws +heel +heeled +heeling +heelless +heels +heft +hefted +heftier +heftiest +heftily +heftiness +hefting +hefts +hefty +hegemony +hegira +hegiras +heifer +heifers +height +heighten +heightened +heightening +heightens +heights +heinous +heinously +heinousness +heir +heiress +heiresses +heirloom +heirlooms +heirs +heist +heisted +heisting +heists +held +helical +helices +helicopter +helicoptered +helicoptering +helicopters +heliocentric +heliotrope +heliotropes +heliport +heliports +helium +helix +helixes +hell +hellbent +hellcat +hellcats +hellebore +hellhole +hellholes +hellion +hellions +hellish +hellishly +hellishness +hello +hellos +helluva +helm +helmet +helmeted +helmets +helms +helmsman +helmsmen +helot +helots +help +helped +helper +helpers +helpful +helpfully +helpfulness +helping +helpings +helpless +helplessly +helplessness +helpmate +helpmates +helpmeet +helpmeets +helps +helve +helves +hematite +hematologic +hematological +hematologist +hematologists +hematology +heme +hemisphere +hemispheres +hemispheric +hemispherical +hemline +hemlines +hemlock +hemlocks +hemmed +hemmer +hemmers +hemming +hemoglobin +hemophilia +hemophiliac +hemophiliacs +hemorrhage +hemorrhaged +hemorrhages +hemorrhagic +hemorrhaging +hemorrhoid +hemorrhoids +hemostat +hemostats +hemp +hempen +hems +hemstitch +hemstitched +hemstitches +hemstitching +hence +henceforth +henceforward +henchman +henchmen +henna +hennaed +hennaing +hennas +henpeck +henpecked +henpecking +henpecks +hens +heparin +hepatic +hepatitis +hepper +heppest +heptagon +heptagonal +heptagons +heptathlon +heptathlons +herald +heralded +heraldic +heralding +heraldry +heralds +herb +herbaceous +herbage +herbal +herbalist +herbalists +herbicidal +herbicide +herbicides +herbivore +herbivores +herbivorous +herbs +herculean +herd +herded +herder +herders +herding +herds +herdsman +herdsmen +here +hereabout +hereabouts +hereafter +hereafters +hereby +hereditary +heredity +herein +hereof +hereon +heresies +heresy +heretic +heretical +heretics +hereto +heretofore +hereunto +hereupon +herewith +heritable +heritage +heritages +hermaphrodite +hermaphrodites +hermaphroditic +hermetic +hermetical +hermetically +hermit +hermitage +hermitages +hermits +hernia +herniae +hernial +hernias +herniate +herniated +herniates +herniating +herniation +hero +heroes +heroic +heroically +heroics +heroin +heroine +heroines +heroism +heron +herons +heros +herpes +herpetologist +herpetologists +herpetology +herring +herringbone +herrings +hers +herself +hertz +hertzes +hesitance +hesitancy +hesitant +hesitantly +hesitate +hesitated +hesitates +hesitating +hesitatingly +hesitation +hesitations +hetero +heterodox +heterodoxy +heterogeneity +heterogeneous +heterogeneously +heteros +heterosexual +heterosexuality +heterosexuals +heuristic +heuristically +heuristics +hewed +hewer +hewers +hewing +hewn +hews +hexadecimal +hexagon +hexagonal +hexagons +hexagram +hexagrams +hexameter +hexameters +hexed +hexes +hexing +heyday +heydays +hiatus +hiatuses +hibachi +hibachis +hibernate +hibernated +hibernates +hibernating +hibernation +hibernator +hibernators +hibiscus +hibiscuses +hiccough +hiccoughed +hiccoughing +hiccoughs +hiccup +hiccuped +hiccuping +hiccupped +hiccupping +hiccups +hick +hickey +hickeys +hickies +hickories +hickory +hicks +hidden +hide +hideaway +hideaways +hidebound +hided +hideous +hideously +hideousness +hideout +hideouts +hider +hiders +hides +hiding +hied +hieing +hierarchic +hierarchical +hierarchically +hierarchies +hierarchy +hieroglyph +hieroglyphic +hieroglyphics +hieroglyphs +hies +high +highball +highballs +highborn +highboy +highboys +highbrow +highbrows +highchair +highchairs +higher +highest +highfalutin +highfaluting +highhanded +highhandedly +highhandedness +highjack +highjacked +highjacking +highjacks +highland +highlander +highlanders +highlands +highlight +highlighted +highlighter +highlighters +highlighting +highlights +highly +highness +highrise +highrises +highroad +highroads +highs +hightail +hightailed +hightailing +hightails +highway +highwayman +highwaymen +highways +hijack +hijacked +hijacker +hijackers +hijacking +hijackings +hijacks +hike +hiked +hiker +hikers +hikes +hiking +hilarious +hilariously +hilariousness +hilarity +hill +hillbillies +hillbilly +hillier +hilliest +hilliness +hillock +hillocks +hills +hillside +hillsides +hilltop +hilltops +hilly +hilt +hilts +hims +himself +hind +hinder +hindered +hindering +hinders +hindmost +hindquarter +hindquarters +hindrance +hindrances +hinds +hindsight +hinge +hinged +hinges +hinging +hint +hinted +hinter +hinterland +hinterlands +hinters +hinting +hints +hipbone +hipbones +hipness +hipped +hipper +hippest +hippie +hippies +hipping +hippo +hippodrome +hippodromes +hippopotami +hippopotamus +hippopotamuses +hippos +hippy +hips +hipster +hipsters +hire +hired +hireling +hirelings +hires +hiring +hirsute +hirsuteness +hiss +hissed +hisses +hissing +hist +histamine +histamines +histogram +histograms +histologist +histologists +histology +historian +historians +historic +historical +historically +historicity +histories +historiographer +historiographers +historiography +history +histrionic +histrionically +histrionics +hitch +hitched +hitcher +hitchers +hitches +hitchhike +hitchhiked +hitchhiker +hitchhikers +hitchhikes +hitchhiking +hitching +hither +hitherto +hits +hitter +hitters +hitting +hive +hived +hives +hiving +hoagie +hoagies +hoagy +hoard +hoarded +hoarder +hoarders +hoarding +hoardings +hoards +hoarfrost +hoarier +hoariest +hoariness +hoarse +hoarsely +hoarseness +hoarser +hoarsest +hoary +hoax +hoaxed +hoaxer +hoaxers +hoaxes +hoaxing +hobbies +hobble +hobbled +hobbler +hobblers +hobbles +hobbling +hobby +hobbyhorse +hobbyhorses +hobbyist +hobbyists +hobgoblin +hobgoblins +hobnail +hobnailed +hobnailing +hobnails +hobnob +hobnobbed +hobnobbing +hobnobs +hobo +hoboes +hobos +hobs +hock +hocked +hockey +hocking +hocks +hockshop +hockshops +hodgepodge +hodgepodges +hods +hoecake +hoecakes +hoed +hoedown +hoedowns +hoeing +hoer +hoers +hoes +hogan +hogans +hogback +hogbacks +hogged +hogging +hoggish +hoggishly +hogs +hogshead +hogsheads +hogtie +hogtied +hogtieing +hogties +hogtying +hogwash +hoist +hoisted +hoisting +hoists +hoke +hoked +hokes +hokey +hokier +hokiest +hoking +hokum +hold +holder +holders +holding +holdings +holdout +holdouts +holdover +holdovers +holds +holdup +holdups +hole +holed +holes +holey +holiday +holidayed +holidaying +holidays +holier +holiest +holiness +holing +holistic +holistically +holler +hollered +hollering +hollers +hollies +hollow +hollowed +hollower +hollowest +hollowing +hollowly +hollowness +hollows +holly +hollyhock +hollyhocks +holmium +holocaust +holocausts +hologram +holograms +holograph +holographic +holographs +holography +holster +holstered +holstering +holsters +holy +homage +homages +hombre +hombres +homburg +homburgs +home +homebodies +homebody +homeboy +homeboys +homecoming +homecomings +homed +homegrown +homeland +homelands +homeless +homelessness +homelier +homeliest +homelike +homeliness +homely +homemade +homemaker +homemakers +homemaking +homeopath +homeopathic +homeopaths +homeopathy +homeostasis +homeostatic +homeowner +homeowners +homepage +homepages +homer +homered +homering +homeroom +homerooms +homers +homes +homeschooling +homesick +homesickness +homespun +homestead +homesteaded +homesteader +homesteaders +homesteading +homesteads +homestretch +homestretches +hometown +hometowns +homeward +homewards +homework +homey +homeyness +homeys +homicidal +homicide +homicides +homier +homiest +homiletic +homilies +homily +hominess +homing +hominid +hominids +hominy +homo +homogeneity +homogeneous +homogeneously +homogenization +homogenize +homogenized +homogenizes +homogenizing +homograph +homographs +homologous +homonym +homonyms +homophobia +homophobic +homophone +homophones +homos +homosexual +homosexuality +homosexuals +homy +honcho +honchos +hone +honed +honer +honers +hones +honest +honester +honestest +honestly +honesty +honey +honeybee +honeybees +honeycomb +honeycombed +honeycombing +honeycombs +honeydew +honeydews +honeyed +honeying +honeylocust +honeylocusts +honeymoon +honeymooned +honeymooner +honeymooners +honeymooning +honeymoons +honeys +honeysuckle +honeysuckles +honied +honing +honk +honked +honker +honkers +honkie +honkies +honking +honks +honky +honor +honorable +honorableness +honorably +honoraria +honorarily +honorarium +honorariums +honorary +honored +honoree +honorees +honorer +honorers +honorific +honorifics +honoring +honors +honour +honourable +honoured +honouring +honours +hons +hooch +hood +hooded +hooding +hoodlum +hoodlums +hoodoo +hoodooed +hoodooing +hoodoos +hoods +hoodwink +hoodwinked +hoodwinking +hoodwinks +hooey +hoof +hoofed +hoofing +hoofs +hook +hooka +hookah +hookahs +hookas +hooked +hooker +hookers +hookey +hooking +hooks +hookup +hookups +hookworm +hookworms +hooky +hooligan +hooliganism +hooligans +hoop +hooped +hooping +hoopla +hoops +hooray +hoorayed +hooraying +hoorays +hoosegow +hoosegows +hoot +hootch +hooted +hootenannies +hootenanny +hooter +hooters +hooting +hoots +hooves +hope +hoped +hopeful +hopefully +hopefulness +hopefuls +hopeless +hopelessly +hopelessness +hopes +hoping +hopped +hopper +hoppers +hopping +hops +hopscotch +hopscotched +hopscotches +hopscotching +hora +horas +horde +horded +hordes +hording +horehound +horehounds +horizon +horizons +horizontal +horizontally +horizontals +hormonal +hormone +hormones +horn +hornblende +horned +hornet +hornets +hornier +horniest +hornless +hornlike +hornpipe +hornpipes +horns +horny +horologic +horological +horologist +horologists +horology +horoscope +horoscopes +horrendous +horrendously +horrible +horribleness +horribly +horrid +horrider +horridest +horridly +horrific +horrifically +horrified +horrifies +horrify +horrifying +horror +horrors +horse +horseback +horsed +horseflesh +horseflies +horsefly +horsehair +horsehide +horselaugh +horselaughs +horseless +horseman +horsemanship +horsemen +horseplay +horsepower +horseradish +horseradishes +horses +horseshoe +horseshoed +horseshoeing +horseshoes +horsetail +horsetails +horsewhip +horsewhipped +horsewhipping +horsewhips +horsewoman +horsewomen +horsey +horsier +horsiest +horsing +horsy +hortatory +horticultural +horticulture +horticulturist +horticulturists +hosanna +hosannas +hose +hosed +hoses +hosier +hosiers +hosiery +hosing +hospice +hospices +hospitable +hospitably +hospital +hospitality +hospitalization +hospitalizations +hospitalize +hospitalized +hospitalizes +hospitalizing +hospitals +host +hostage +hostages +hosted +hostel +hosteled +hosteler +hostelers +hosteling +hostelled +hostelling +hostelries +hostelry +hostels +hostess +hostessed +hostesses +hostessing +hostile +hostilely +hostiles +hostilities +hostility +hosting +hostler +hostlers +hosts +hotbed +hotbeds +hotblooded +hotbox +hotboxes +hotcake +hotcakes +hotdog +hotdogged +hotdogging +hotdogs +hotel +hotelier +hoteliers +hotels +hotfoot +hotfooted +hotfooting +hotfoots +hothead +hotheaded +hotheadedly +hotheadedness +hotheads +hothouse +hothouses +hotline +hotlines +hotly +hotness +hotplate +hotplates +hots +hotshot +hotshots +hotspot +hotspots +hotter +hottest +hound +hounded +hounding +hounds +hour +hourglass +hourglasses +houri +houris +hourly +hours +house +houseboat +houseboats +housebound +houseboy +houseboys +housebreak +housebreaker +housebreakers +housebreaking +housebreaks +housebroke +housebroken +houseclean +housecleaned +housecleaning +housecleans +housecoat +housecoats +housed +houseflies +housefly +houseful +housefuls +household +householder +householders +households +househusband +househusbands +housekeeper +housekeepers +housekeeping +houselights +housemaid +housemaids +houseman +housemen +housemother +housemothers +houseparent +houseparents +houseplant +houseplants +houses +housetop +housetops +housewares +housewarming +housewarmings +housewife +housewifely +housewives +housework +housing +housings +hove +hovel +hovels +hover +hovercraft +hovercrafts +hovered +hovering +hovers +howbeit +howdah +howdahs +howdy +however +howitzer +howitzers +howl +howled +howler +howlers +howling +howls +hows +howsoever +hoyden +hoydenish +hoydens +huarache +huaraches +hubbies +hubbub +hubbubs +hubby +hubcap +hubcaps +hubris +hubs +huckleberries +huckleberry +huckster +huckstered +huckstering +hucksterism +hucksters +huddle +huddled +huddles +huddling +hued +hues +huff +huffed +huffier +huffiest +huffily +huffiness +huffing +huffs +huffy +huge +hugely +hugeness +huger +hugest +hugged +hugging +hugs +hula +hulas +hulk +hulking +hulks +hull +hullabaloo +hullabaloos +hulled +huller +hullers +hulling +hullo +hullos +hulls +human +humane +humanely +humaneness +humaner +humanest +humanism +humanist +humanistic +humanists +humanitarian +humanitarianism +humanitarians +humanities +humanity +humanization +humanize +humanized +humanizer +humanizers +humanizes +humanizing +humankind +humanly +humanness +humanoid +humanoids +humans +humble +humbled +humbleness +humbler +humblers +humbles +humblest +humbling +humbly +humbug +humbugged +humbugging +humbugs +humdinger +humdingers +humdrum +humeral +humeri +humerus +humid +humider +humidest +humidification +humidified +humidifier +humidifiers +humidifies +humidify +humidifying +humidity +humidly +humidor +humidors +humiliate +humiliated +humiliates +humiliating +humiliatingly +humiliation +humiliations +humility +hummed +hummer +hummers +humming +hummingbird +hummingbirds +hummock +hummocks +hummocky +hummus +humongous +humor +humored +humoring +humorist +humorists +humorless +humorlessly +humorlessness +humorous +humorously +humorousness +humors +humour +humoured +humouring +humours +hump +humpback +humpbacked +humpbacks +humped +humph +humphed +humphing +humphs +humping +humps +hums +humungous +humus +hunch +hunchback +hunchbacked +hunchbacks +hunched +hunches +hunching +hundred +hundredfold +hundreds +hundredth +hundredths +hundredweight +hundredweights +hung +hunger +hungered +hungering +hungers +hungover +hungrier +hungriest +hungrily +hungriness +hungry +hunk +hunker +hunkered +hunkering +hunkers +hunkier +hunkiest +hunks +hunky +hunt +hunted +hunter +hunters +hunting +huntress +huntresses +hunts +huntsman +huntsmen +hurdle +hurdled +hurdler +hurdlers +hurdles +hurdling +hurl +hurled +hurler +hurlers +hurling +hurls +hurrah +hurrahed +hurrahing +hurrahs +hurray +hurrayed +hurraying +hurrays +hurricane +hurricanes +hurried +hurriedly +hurries +hurry +hurrying +hurt +hurtful +hurtfully +hurtfulness +hurting +hurtle +hurtled +hurtles +hurtling +hurts +husband +husbanded +husbanding +husbandman +husbandmen +husbandry +husbands +hush +hushed +hushes +hushing +husk +husked +husker +huskers +huskier +huskies +huskiest +huskily +huskiness +husking +husks +husky +hussar +hussars +hussies +hussy +hustings +hustle +hustled +hustler +hustlers +hustles +hustling +hutch +hutches +huts +hutzpa +hutzpah +huzza +huzzaed +huzzah +huzzahed +huzzahing +huzzahs +huzzaing +huzzas +hyacinth +hyacinths +hyaena +hyaenas +hybrid +hybridism +hybridization +hybridize +hybridized +hybridizes +hybridizing +hybrids +hydra +hydrae +hydrangea +hydrangeas +hydrant +hydrants +hydras +hydrate +hydrated +hydrates +hydrating +hydration +hydraulic +hydraulically +hydraulics +hydro +hydrocarbon +hydrocarbons +hydrocephalus +hydrocephaly +hydrodynamic +hydrodynamics +hydroelectric +hydroelectrically +hydroelectricity +hydrofoil +hydrofoils +hydrogen +hydrogenate +hydrogenated +hydrogenates +hydrogenating +hydrogenation +hydrogenous +hydrologist +hydrologists +hydrology +hydrolysis +hydrolyze +hydrolyzed +hydrolyzes +hydrolyzing +hydrometer +hydrometers +hydrometry +hydrophobia +hydrophobic +hydrophone +hydrophones +hydroplane +hydroplaned +hydroplanes +hydroplaning +hydroponic +hydroponically +hydroponics +hydrosphere +hydrotherapy +hydrous +hydroxide +hydroxides +hyena +hyenas +hygiene +hygienic +hygienically +hygienist +hygienists +hygrometer +hygrometers +hying +hymen +hymeneal +hymens +hymn +hymnal +hymnals +hymnbook +hymnbooks +hymned +hymning +hymns +hype +hyped +hyper +hyperactive +hyperactivity +hyperbola +hyperbolae +hyperbolas +hyperbole +hyperbolic +hypercritical +hypercritically +hyperglycemia +hyperlink +hyperlinks +hypermedia +hypersensitive +hypersensitiveness +hypersensitivities +hypersensitivity +hypertension +hypertensive +hypertensives +hypertext +hyperthyroid +hyperthyroidism +hypertrophied +hypertrophies +hypertrophy +hypertrophying +hyperventilate +hyperventilated +hyperventilates +hyperventilating +hyperventilation +hypes +hyphen +hyphenate +hyphenated +hyphenates +hyphenating +hyphenation +hyphened +hyphening +hyphens +hyping +hypnosis +hypnotherapy +hypnotic +hypnotically +hypnotics +hypnotism +hypnotist +hypnotists +hypnotize +hypnotized +hypnotizes +hypnotizing +hypo +hypoallergenic +hypochondria +hypochondriac +hypochondriacs +hypocrisies +hypocrisy +hypocrite +hypocrites +hypocritical +hypocritically +hypodermic +hypodermics +hypoglycemia +hypoglycemic +hypoglycemics +hypos +hypotenuse +hypotenuses +hypothalami +hypothalamus +hypothermia +hypotheses +hypothesis +hypothesize +hypothesized +hypothesizes +hypothesizing +hypothetical +hypothetically +hypothyroid +hypothyroidism +hyssop +hysterectomies +hysterectomy +hysteria +hysteric +hysterical +hysterically +hysterics +iamb +iambi +iambic +iambics +iambs +iambus +iambuses +ibex +ibexes +ibices +ibidem +ibis +ibises +ibuprofen +iceberg +icebergs +iceboat +iceboats +icebound +icebox +iceboxes +icebreaker +icebreakers +icecap +icecaps +iced +iceman +icemen +ices +ichthyologist +ichthyologists +ichthyology +icicle +icicles +icier +iciest +icily +iciness +icing +icings +ickier +ickiest +icky +icon +iconic +iconoclasm +iconoclast +iconoclastic +iconoclasts +iconography +icons +ictus +idea +ideal +idealism +idealist +idealistic +idealistically +idealists +idealization +idealize +idealized +idealizes +idealizing +ideally +ideals +ideas +idem +identical +identically +identifiable +identification +identified +identifies +identify +identifying +identities +identity +ideogram +ideograms +ideograph +ideographs +ideological +ideologically +ideologies +ideologist +ideologists +ideologue +ideologues +ideology +ides +idiocies +idiocy +idiom +idiomatic +idiomatically +idioms +idiopathic +idiosyncrasies +idiosyncrasy +idiosyncratic +idiosyncratically +idiot +idiotic +idiotically +idiots +idle +idled +idleness +idler +idlers +idles +idlest +idling +idly +idol +idolater +idolaters +idolatress +idolatresses +idolatrous +idolatry +idolization +idolize +idolized +idolizes +idolizing +idols +idyl +idyll +idyllic +idyllically +idylls +idyls +iffier +iffiest +iffiness +iffy +igloo +igloos +igneous +ignitable +ignite +ignited +ignites +ignitible +igniting +ignition +ignitions +ignoble +ignobler +ignoblest +ignobly +ignominies +ignominious +ignominiously +ignominy +ignoramus +ignoramuses +ignorance +ignorant +ignorantly +ignore +ignored +ignores +ignoring +iguana +iguanas +ikon +ikons +ilea +ileitis +ileum +ilia +ilium +ilks +illegal +illegalities +illegality +illegally +illegals +illegibility +illegible +illegibly +illegitimacy +illegitimate +illegitimately +illiberal +illiberality +illiberally +illicit +illicitly +illicitness +illimitable +illiteracy +illiterate +illiterately +illiterates +illness +illnesses +illogical +illogicality +illogically +ills +illuminable +illuminate +illuminated +illuminates +illuminating +illuminatingly +illumination +illuminations +illumine +illumined +illumines +illumining +illusion +illusionist +illusionists +illusions +illusive +illusory +illustrate +illustrated +illustrates +illustrating +illustration +illustrations +illustrative +illustratively +illustrator +illustrators +illustrious +illustriously +illustriousness +image +imaged +imagery +images +imaginable +imaginably +imaginary +imagination +imaginations +imaginative +imaginatively +imagine +imagined +imagines +imaging +imagining +imago +imagoes +imam +imams +imbalance +imbalances +imbecile +imbeciles +imbecilic +imbecilities +imbecility +imbed +imbedded +imbedding +imbeds +imbibe +imbibed +imbiber +imbibers +imbibes +imbibing +imbrication +imbroglio +imbroglios +imbue +imbued +imbues +imbuing +imitable +imitate +imitated +imitates +imitating +imitation +imitations +imitative +imitatively +imitativeness +imitator +imitators +immaculate +immaculately +immaculateness +immanence +immanency +immanent +immanently +immaterial +immateriality +immaterially +immaterialness +immature +immaturely +immaturity +immeasurable +immeasurably +immediacies +immediacy +immediate +immediately +immediateness +immemorial +immemorially +immense +immensely +immensities +immensity +immerse +immersed +immerses +immersible +immersing +immersion +immersions +immigrant +immigrants +immigrate +immigrated +immigrates +immigrating +immigration +imminence +imminent +imminently +immobile +immobility +immobilization +immobilize +immobilized +immobilizes +immobilizing +immoderate +immoderately +immodest +immodestly +immodesty +immolate +immolated +immolates +immolating +immolation +immoral +immoralities +immorality +immorally +immortal +immortality +immortalize +immortalized +immortalizes +immortalizing +immortally +immortals +immovability +immovable +immovably +immune +immunity +immunization +immunizations +immunize +immunized +immunizes +immunizing +immunodeficiency +immunodeficient +immunologic +immunological +immunologist +immunologists +immunology +immure +immured +immures +immuring +immutability +immutable +immutably +impact +impacted +impacting +impacts +impair +impaired +impairing +impairment +impairments +impairs +impala +impalas +impale +impaled +impalement +impales +impaling +impalpable +impalpably +impanel +impaneled +impaneling +impanelled +impanelling +impanels +impart +imparted +impartial +impartiality +impartially +imparting +imparts +impassable +impassably +impasse +impasses +impassibility +impassible +impassibly +impassioned +impassive +impassively +impassiveness +impassivity +impasto +impatience +impatiences +impatiens +impatient +impatiently +impeach +impeachable +impeached +impeacher +impeachers +impeaches +impeaching +impeachment +impeachments +impeccability +impeccable +impeccably +impecunious +impecuniously +impecuniousness +impedance +impede +impeded +impedes +impediment +impedimenta +impediments +impeding +impel +impelled +impeller +impellers +impelling +impels +impend +impended +impending +impends +impenetrability +impenetrable +impenetrably +impenitence +impenitent +impenitently +imperative +imperatively +imperatives +imperceptibility +imperceptible +imperceptibly +imperceptive +imperfect +imperfection +imperfections +imperfectly +imperfectness +imperfects +imperial +imperialism +imperialist +imperialistic +imperialistically +imperialists +imperially +imperials +imperil +imperiled +imperiling +imperilled +imperilling +imperilment +imperils +imperious +imperiously +imperiousness +imperishable +imperishably +impermanence +impermanent +impermanently +impermeability +impermeable +impermeably +impermissible +impersonal +impersonally +impersonate +impersonated +impersonates +impersonating +impersonation +impersonations +impersonator +impersonators +impertinence +impertinent +impertinently +imperturbability +imperturbable +imperturbably +impervious +imperviously +impetigo +impetuosity +impetuous +impetuously +impetuousness +impetus +impetuses +impieties +impiety +impinge +impinged +impingement +impinges +impinging +impious +impiously +impiousness +impish +impishly +impishness +implacability +implacable +implacably +implant +implantable +implantation +implanted +implanting +implants +implausibilities +implausibility +implausible +implausibly +implement +implementation +implementations +implemented +implementing +implements +implicate +implicated +implicates +implicating +implication +implications +implicit +implicitly +implicitness +implied +implies +implode +imploded +implodes +imploding +implore +implored +implores +imploring +imploringly +implosion +implosions +implosive +imply +implying +impolite +impolitely +impoliteness +impolitenesses +impolitic +imponderable +imponderables +import +importable +importance +important +importantly +importation +importations +imported +importer +importers +importing +imports +importunate +importunately +importune +importuned +importunes +importuning +importunity +impose +imposed +imposer +imposers +imposes +imposing +imposingly +imposition +impositions +impossibilities +impossibility +impossible +impossibly +impost +imposter +imposters +impostor +impostors +imposts +imposture +impostures +impotence +impotency +impotent +impotently +impound +impounded +impounding +impounds +impoverish +impoverished +impoverishes +impoverishing +impoverishment +impracticable +impracticably +impractical +impracticality +impractically +imprecate +imprecated +imprecates +imprecating +imprecation +imprecations +imprecise +imprecisely +impreciseness +imprecision +impregnability +impregnable +impregnably +impregnate +impregnated +impregnates +impregnating +impregnation +impresario +impresarios +impress +impressed +impresses +impressibility +impressible +impressing +impression +impressionability +impressionable +impressionism +impressionist +impressionistic +impressionists +impressions +impressive +impressively +impressiveness +imprimatur +imprimaturs +imprint +imprinted +imprinter +imprinters +imprinting +imprints +imprison +imprisoned +imprisoning +imprisonment +imprisonments +imprisons +improbabilities +improbability +improbable +improbably +impromptu +impromptus +improper +improperly +improprieties +impropriety +improvable +improve +improved +improvement +improvements +improves +improvidence +improvident +improvidently +improving +improvisation +improvisational +improvisations +improvise +improvised +improviser +improvisers +improvises +improvising +improvisor +improvisors +imprudence +imprudent +imprudently +imps +impudence +impudent +impudently +impugn +impugned +impugner +impugners +impugning +impugns +impulse +impulsed +impulses +impulsing +impulsion +impulsive +impulsively +impulsiveness +impunity +impure +impurely +impurer +impurest +impurities +impurity +imputable +imputation +imputations +impute +imputed +imputes +imputing +inabilities +inability +inaccessibility +inaccessible +inaccessibly +inaccuracies +inaccuracy +inaccurate +inaccurately +inaction +inactivate +inactivated +inactivates +inactivating +inactivation +inactive +inactively +inactivity +inadequacies +inadequacy +inadequate +inadequately +inadmissibility +inadmissible +inadvertence +inadvertent +inadvertently +inadvisability +inadvisable +inalienability +inalienable +inalienably +inamorata +inamoratas +inane +inanely +inaner +inanest +inanimate +inanimately +inanimateness +inanities +inanity +inapplicable +inappreciable +inappreciably +inapproachable +inappropriate +inappropriately +inappropriateness +inapt +inaptly +inaptness +inarguable +inarticulate +inarticulately +inarticulateness +inartistic +inattention +inattentive +inattentively +inattentiveness +inaudibility +inaudible +inaudibly +inaugural +inaugurals +inaugurate +inaugurated +inaugurates +inaugurating +inauguration +inaugurations +inauspicious +inauspiciously +inauthentic +inboard +inboards +inborn +inbound +inbounded +inbounding +inbounds +inbred +inbreed +inbreeding +inbreeds +incalculable +incalculably +incandescence +incandescent +incandescently +incantation +incantations +incapability +incapable +incapably +incapacitate +incapacitated +incapacitates +incapacitating +incapacity +incarcerate +incarcerated +incarcerates +incarcerating +incarceration +incarcerations +incarnadine +incarnadined +incarnadines +incarnadining +incarnate +incarnated +incarnates +incarnating +incarnation +incarnations +incautious +incendiaries +incendiary +incense +incensed +incenses +incensing +incentive +incentives +inception +inceptions +incertitude +incessant +incessantly +incest +incestuous +incestuously +incestuousness +inch +inched +inches +inching +inchoate +inchworm +inchworms +incidence +incidences +incident +incidental +incidentally +incidentals +incidents +incinerate +incinerated +incinerates +incinerating +incineration +incinerator +incinerators +incipience +incipient +incipiently +incise +incised +incises +incising +incision +incisions +incisive +incisively +incisiveness +incisor +incisors +incite +incited +incitement +incitements +inciter +inciters +incites +inciting +incivilities +incivility +inclemency +inclement +inclination +inclinations +incline +inclined +inclines +inclining +inclose +inclosed +incloses +inclosing +inclosure +inclosures +include +included +includes +including +inclusion +inclusions +inclusive +inclusively +inclusiveness +incognito +incognitos +incoherence +incoherent +incoherently +incombustible +income +incomes +incoming +incommensurate +incommensurately +incommode +incommoded +incommodes +incommoding +incommodious +incommunicable +incommunicado +incomparable +incomparably +incompatibilities +incompatibility +incompatible +incompatibles +incompatibly +incompetence +incompetency +incompetent +incompetently +incompetents +incomplete +incompletely +incompleteness +incompletes +incomprehensibility +incomprehensible +incomprehensibly +incomprehension +inconceivability +inconceivable +inconceivably +inconclusive +inconclusively +inconclusiveness +incongruities +incongruity +incongruous +incongruously +incongruousness +inconsequential +inconsequentially +inconsiderable +inconsiderate +inconsiderately +inconsiderateness +inconsideration +inconsistencies +inconsistency +inconsistent +inconsistently +inconsolable +inconsolably +inconspicuous +inconspicuously +inconspicuousness +inconstancy +inconstant +inconstantly +incontestability +incontestable +incontestably +incontinence +incontinent +incontrovertible +incontrovertibly +inconvenience +inconvenienced +inconveniences +inconveniencing +inconvenient +inconveniently +incorporate +incorporated +incorporates +incorporating +incorporation +incorporeal +incorrect +incorrectly +incorrectness +incorrigibility +incorrigible +incorrigibly +incorruptibility +incorruptible +incorruptibly +increase +increased +increases +increasing +increasingly +incredibility +incredible +incredibly +incredulity +incredulous +incredulously +increment +incremental +increments +incriminate +incriminated +incriminates +incriminating +incrimination +incriminatory +incrust +incrustation +incrustations +incrusted +incrusting +incrusts +incubate +incubated +incubates +incubating +incubation +incubator +incubators +incubi +incubus +incubuses +inculcate +inculcated +inculcates +inculcating +inculcation +inculpable +inculpate +inculpated +inculpates +inculpating +incumbencies +incumbency +incumbent +incumbents +incumber +incumbered +incumbering +incumbers +incunabula +incunabulum +incur +incurable +incurables +incurably +incurious +incurred +incurring +incurs +incursion +incursions +indebted +indebtedness +indecencies +indecency +indecent +indecently +indecipherable +indecision +indecisive +indecisively +indecisiveness +indecorous +indecorously +indeed +indefatigable +indefatigably +indefeasible +indefeasibly +indefensible +indefensibly +indefinable +indefinably +indefinite +indefinitely +indefiniteness +indelible +indelibly +indelicacies +indelicacy +indelicate +indelicately +indemnification +indemnifications +indemnified +indemnifies +indemnify +indemnifying +indemnities +indemnity +indemonstrable +indent +indentation +indentations +indented +indenting +indention +indents +indenture +indentured +indentures +indenturing +independence +independent +independently +independents +indescribable +indescribably +indestructibility +indestructible +indestructibly +indeterminable +indeterminably +indeterminacy +indeterminate +indeterminately +index +indexation +indexations +indexed +indexer +indexers +indexes +indexing +indicate +indicated +indicates +indicating +indication +indications +indicative +indicatively +indicatives +indicator +indicators +indices +indict +indictable +indicted +indicting +indictment +indictments +indicts +indifference +indifferent +indifferently +indigence +indigenous +indigent +indigently +indigents +indigestible +indigestion +indignant +indignantly +indignation +indignities +indignity +indigo +indirect +indirected +indirecting +indirection +indirectly +indirectness +indirects +indiscernible +indiscreet +indiscreetly +indiscretion +indiscretions +indiscriminate +indiscriminately +indispensability +indispensable +indispensables +indispensably +indisposed +indisposition +indispositions +indisputable +indisputably +indissoluble +indissolubly +indistinct +indistinctly +indistinctness +indistinguishable +indistinguishably +indite +indited +indites +inditing +indium +individual +individualism +individualist +individualistic +individualistically +individualists +individuality +individualization +individualize +individualized +individualizes +individualizing +individually +individuals +individuate +individuated +individuates +individuating +individuation +indivisibility +indivisible +indivisibly +indoctrinate +indoctrinated +indoctrinates +indoctrinating +indoctrination +indolence +indolent +indolently +indomitable +indomitably +indoor +indoors +indorse +indorsed +indorses +indorsing +indubitable +indubitably +induce +induced +inducement +inducements +inducer +inducers +induces +inducing +induct +inductance +inducted +inductee +inductees +inducting +induction +inductions +inductive +inducts +indue +indued +indues +induing +indulge +indulged +indulgence +indulgences +indulgent +indulgently +indulges +indulging +industrial +industrialism +industrialist +industrialists +industrialization +industrialize +industrialized +industrializes +industrializing +industrially +industries +industrious +industriously +industriousness +industry +indwell +indwelled +indwelling +indwells +indwelt +inebriate +inebriated +inebriates +inebriating +inebriation +inedible +ineducable +ineffability +ineffable +ineffably +ineffective +ineffectively +ineffectiveness +ineffectual +ineffectually +inefficacy +inefficiencies +inefficiency +inefficient +inefficiently +inelastic +inelegance +inelegant +inelegantly +ineligibility +ineligible +ineligibles +ineligibly +ineluctable +ineluctably +inept +inepter +ineptest +ineptitude +ineptly +ineptness +inequalities +inequality +inequitable +inequitably +inequities +inequity +ineradicable +inerrant +inert +inertia +inertial +inertly +inertness +inescapable +inescapably +inessential +inessentials +inestimable +inestimably +inevitability +inevitable +inevitables +inevitably +inexact +inexactly +inexactness +inexcusable +inexcusably +inexhaustible +inexhaustibly +inexorable +inexorably +inexpedience +inexpediency +inexpedient +inexpensive +inexpensively +inexpensiveness +inexperience +inexperienced +inexpert +inexpertly +inexpiable +inexplicable +inexplicably +inexpressible +inexpressibly +inexpressive +inextinguishable +inextricable +inextricably +infallibility +infallible +infallibly +infamies +infamous +infamously +infamy +infancy +infant +infanticide +infanticides +infantile +infantries +infantry +infantryman +infantrymen +infants +infarct +infarction +infarcts +infatuate +infatuated +infatuates +infatuating +infatuation +infatuations +infect +infected +infecting +infection +infections +infectious +infectiously +infectiousness +infects +infelicities +infelicitous +infelicity +infer +inference +inferences +inferential +inferior +inferiority +inferiors +infernal +infernally +inferno +infernos +inferred +inferring +infers +infertile +infertility +infest +infestation +infestations +infested +infesting +infests +infidel +infidelities +infidelity +infidels +infield +infielder +infielders +infields +infighter +infighters +infighting +infiltrate +infiltrated +infiltrates +infiltrating +infiltration +infiltrator +infiltrators +infinite +infinitely +infinitesimal +infinitesimally +infinitesimals +infinities +infinitival +infinitive +infinitives +infinitude +infinity +infirm +infirmaries +infirmary +infirmities +infirmity +inflame +inflamed +inflames +inflaming +inflammability +inflammable +inflammation +inflammations +inflammatory +inflatable +inflatables +inflate +inflated +inflates +inflating +inflation +inflationary +inflect +inflected +inflecting +inflection +inflectional +inflections +inflects +inflexibility +inflexible +inflexibly +inflexion +inflexions +inflict +inflicted +inflicting +infliction +inflictive +inflicts +inflight +inflorescence +inflorescent +inflow +inflows +influence +influenced +influences +influencing +influential +influentially +influenza +influx +influxes +info +infold +infolded +infolding +infolds +infomercial +infomercials +inform +informal +informality +informally +informant +informants +information +informational +informative +informatively +informativeness +informed +informer +informers +informing +informs +infotainment +infra +infraction +infractions +infrared +infrasonic +infrastructure +infrastructures +infrequence +infrequency +infrequent +infrequently +infringe +infringed +infringement +infringements +infringes +infringing +infuriate +infuriated +infuriates +infuriating +infuriatingly +infuse +infused +infuser +infusers +infuses +infusing +infusion +infusions +ingenious +ingeniously +ingeniousness +ingenue +ingenues +ingenuity +ingenuous +ingenuously +ingenuousness +ingest +ingested +ingesting +ingestion +ingests +inglenook +inglenooks +inglorious +ingloriously +ingot +ingots +ingrain +ingrained +ingraining +ingrains +ingrate +ingrates +ingratiate +ingratiated +ingratiates +ingratiating +ingratiatingly +ingratiation +ingratitude +ingredient +ingredients +ingress +ingresses +ingrowing +ingrown +inguinal +inhabit +inhabitable +inhabitant +inhabitants +inhabited +inhabiting +inhabits +inhalant +inhalants +inhalation +inhalations +inhalator +inhalators +inhale +inhaled +inhaler +inhalers +inhales +inhaling +inharmonious +inhere +inhered +inherent +inherently +inheres +inhering +inherit +inheritable +inheritance +inheritances +inherited +inheriting +inheritor +inheritors +inherits +inhibit +inhibited +inhibiting +inhibition +inhibitions +inhibitor +inhibitors +inhibitory +inhibits +inhospitable +inhospitably +inhuman +inhumane +inhumanely +inhumanities +inhumanity +inhumanly +inimical +inimically +inimitable +inimitably +iniquities +iniquitous +iniquitously +iniquity +initial +initialed +initialing +initialize +initialized +initializes +initializing +initialled +initialling +initially +initials +initiate +initiated +initiates +initiating +initiation +initiations +initiative +initiatives +initiator +initiators +initiatory +inject +injected +injecting +injection +injections +injector +injectors +injects +injudicious +injudiciously +injudiciousness +injunction +injunctions +injure +injured +injurer +injurers +injures +injuries +injuring +injurious +injury +injustice +injustices +inkblot +inkblots +inked +inkier +inkiest +inkiness +inking +inkling +inklings +inks +inkstand +inkstands +inkwell +inkwells +inky +inlaid +inland +inlay +inlaying +inlays +inlet +inlets +inmate +inmates +inmost +innards +innate +innately +innateness +inner +innermost +innersole +innersoles +innerspring +innervate +innervated +innervates +innervating +innervation +inning +innings +innkeeper +innkeepers +innocence +innocent +innocently +innocents +innocuous +innocuously +innocuousness +innovate +innovated +innovates +innovating +innovation +innovations +innovative +innovator +innovators +inns +innuendo +innuendoes +innuendos +innumerable +innumerably +innumeracy +innumerate +inoculate +inoculated +inoculates +inoculating +inoculation +inoculations +inoffensive +inoffensively +inoffensiveness +inoperable +inoperative +inopportune +inopportunely +inordinate +inordinately +inorganic +inorganically +inpatient +inpatients +input +inputs +inputted +inputting +inquest +inquests +inquietude +inquire +inquired +inquirer +inquirers +inquires +inquiries +inquiring +inquiringly +inquiry +inquisition +inquisitional +inquisitions +inquisitive +inquisitively +inquisitiveness +inquisitor +inquisitorial +inquisitors +inroad +inroads +inrush +inrushes +insalubrious +insane +insanely +insaner +insanest +insanitary +insanity +insatiability +insatiable +insatiably +inscribe +inscribed +inscriber +inscribers +inscribes +inscribing +inscription +inscriptions +inscrutability +inscrutable +inscrutableness +inscrutably +inseam +inseams +insect +insecticidal +insecticide +insecticides +insectivore +insectivores +insectivorous +insects +insecure +insecurely +insecurities +insecurity +inseminate +inseminated +inseminates +inseminating +insemination +insensate +insensibility +insensible +insensibly +insensitive +insensitively +insensitivity +insentience +insentient +inseparability +inseparable +inseparables +inseparably +insert +inserted +inserting +insertion +insertions +inserts +inset +insets +insetted +insetting +inshore +inside +insider +insiders +insides +insidious +insidiously +insidiousness +insight +insightful +insights +insigne +insignia +insignias +insignificance +insignificant +insignificantly +insincere +insincerely +insincerity +insinuate +insinuated +insinuates +insinuating +insinuation +insinuations +insinuative +insinuator +insinuators +insipid +insipidity +insipidly +insist +insisted +insistence +insistent +insistently +insisting +insistingly +insists +insobriety +insofar +insole +insolence +insolent +insolently +insoles +insolubility +insoluble +insolubly +insolvable +insolvency +insolvent +insolvents +insomnia +insomniac +insomniacs +insomuch +insouciance +insouciant +inspect +inspected +inspecting +inspection +inspections +inspector +inspectorate +inspectorates +inspectors +inspects +inspiration +inspirational +inspirations +inspire +inspired +inspires +inspiring +inspirit +inspirited +inspiriting +inspirits +instability +instal +install +installation +installations +installed +installer +installers +installing +installment +installments +installs +instalment +instalments +instals +instance +instanced +instances +instancing +instant +instantaneous +instantaneously +instanter +instantiate +instantiated +instantiates +instantiating +instantly +instants +instate +instated +instates +instating +instead +instep +insteps +instigate +instigated +instigates +instigating +instigation +instigator +instigators +instil +instill +instillation +instilled +instilling +instills +instils +instinct +instinctive +instinctively +instincts +instinctual +institute +instituted +instituter +instituters +institutes +instituting +institution +institutional +institutionalization +institutionalize +institutionalized +institutionalizes +institutionalizing +institutionally +institutions +institutor +institutors +instruct +instructed +instructing +instruction +instructional +instructions +instructive +instructively +instructor +instructors +instructs +instrument +instrumental +instrumentalist +instrumentalists +instrumentality +instrumentally +instrumentals +instrumentation +instrumented +instrumenting +instruments +insubordinate +insubordination +insubstantial +insubstantially +insufferable +insufferably +insufficiency +insufficient +insufficiently +insular +insularity +insulate +insulated +insulates +insulating +insulation +insulator +insulators +insulin +insult +insulted +insulting +insultingly +insults +insuperable +insuperably +insupportable +insurable +insurance +insurances +insure +insured +insureds +insurer +insurers +insures +insurgence +insurgences +insurgencies +insurgency +insurgent +insurgents +insuring +insurmountable +insurmountably +insurrection +insurrectionist +insurrectionists +insurrections +insusceptible +intact +intagli +intaglio +intaglios +intake +intakes +intangibility +intangible +intangibles +intangibly +integer +integers +integral +integrally +integrals +integrate +integrated +integrates +integrating +integration +integrative +integrity +integument +integuments +intellect +intellects +intellectual +intellectualism +intellectualize +intellectualized +intellectualizes +intellectualizing +intellectually +intellectuals +intelligence +intelligent +intelligently +intelligentsia +intelligibility +intelligible +intelligibly +intemperance +intemperate +intemperately +intend +intended +intendeds +intending +intends +intense +intensely +intenser +intensest +intensification +intensified +intensifier +intensifiers +intensifies +intensify +intensifying +intensities +intensity +intensive +intensively +intensiveness +intensives +intent +intention +intentional +intentionally +intentions +intently +intentness +intents +inter +interact +interacted +interacting +interaction +interactions +interactive +interactively +interacts +interbred +interbreed +interbreeding +interbreeds +intercede +interceded +intercedes +interceding +intercept +intercepted +intercepting +interception +interceptions +interceptor +interceptors +intercepts +intercession +intercessions +intercessor +intercessors +intercessory +interchange +interchangeable +interchangeably +interchanged +interchanges +interchanging +intercollegiate +intercom +intercommunicate +intercommunicated +intercommunicates +intercommunicating +intercommunication +intercoms +interconnect +interconnected +interconnecting +interconnection +interconnections +interconnects +intercontinental +intercourse +intercultural +interdenominational +interdepartmental +interdependence +interdependent +interdependently +interdict +interdicted +interdicting +interdiction +interdicts +interdisciplinary +interest +interested +interesting +interestingly +interests +interface +interfaced +interfaces +interfacing +interfaith +interfere +interfered +interference +interferes +interfering +interferon +interfile +interfiled +interfiles +interfiling +intergalactic +intergovernmental +interim +interior +interiors +interject +interjected +interjecting +interjection +interjections +interjects +interlace +interlaced +interlaces +interlacing +interlard +interlarded +interlarding +interlards +interleave +interleaved +interleaves +interleaving +interleukin +interline +interlinear +interlined +interlines +interlining +interlinings +interlink +interlinked +interlinking +interlinks +interlock +interlocked +interlocking +interlocks +interlocutor +interlocutors +interlocutory +interlope +interloped +interloper +interlopers +interlopes +interloping +interlude +interludes +intermarriage +intermarriages +intermarried +intermarries +intermarry +intermarrying +intermediaries +intermediary +intermediate +intermediately +intermediates +interment +interments +intermezzi +intermezzo +intermezzos +interminable +interminably +intermingle +intermingled +intermingles +intermingling +intermission +intermissions +intermittent +intermittently +intermix +intermixed +intermixes +intermixing +intern +internal +internalization +internalize +internalized +internalizes +internalizing +internally +international +internationalise +internationalised +internationalises +internationalising +internationalism +internationalist +internationalists +internationalize +internationalized +internationalizes +internationalizing +internationally +internationals +interne +internecine +interned +internee +internees +internes +interning +internist +internists +internment +interns +internship +internships +interoffice +interpersonal +interplanetary +interplay +interpolate +interpolated +interpolates +interpolating +interpolation +interpolations +interpose +interposed +interposes +interposing +interposition +interpret +interpretation +interpretations +interpretative +interpreted +interpreter +interpreters +interpreting +interpretive +interprets +interracial +interred +interregna +interregnum +interregnums +interrelate +interrelated +interrelates +interrelating +interrelation +interrelations +interrelationship +interrelationships +interring +interrogate +interrogated +interrogates +interrogating +interrogation +interrogations +interrogative +interrogatively +interrogatives +interrogator +interrogatories +interrogators +interrogatory +interrupt +interrupted +interrupter +interrupters +interrupting +interruption +interruptions +interrupts +inters +interscholastic +intersect +intersected +intersecting +intersection +intersections +intersects +intersession +intersessions +intersperse +interspersed +intersperses +interspersing +interspersion +interstate +interstates +interstellar +interstice +interstices +interstitial +intertwine +intertwined +intertwines +intertwining +interurban +interval +intervals +intervene +intervened +intervenes +intervening +intervention +interventionism +interventionist +interventionists +interventions +interview +interviewed +interviewee +interviewees +interviewer +interviewers +interviewing +interviews +intervocalic +interweave +interweaved +interweaves +interweaving +interwove +interwoven +intestacy +intestate +intestinal +intestine +intestines +intimacies +intimacy +intimate +intimated +intimately +intimates +intimating +intimation +intimations +intimidate +intimidated +intimidates +intimidating +intimidatingly +intimidation +into +intolerable +intolerably +intolerance +intolerant +intolerantly +intonation +intonations +intone +intoned +intoner +intoners +intones +intoning +intoxicant +intoxicants +intoxicate +intoxicated +intoxicates +intoxicating +intoxication +intractability +intractable +intractably +intramural +intramuscular +intransigence +intransigent +intransigently +intransigents +intransitive +intransitively +intransitives +intrastate +intrauterine +intravenous +intravenouses +intravenously +intrench +intrenched +intrenches +intrenching +intrepid +intrepidity +intrepidly +intricacies +intricacy +intricate +intricately +intrigue +intrigued +intriguer +intriguers +intrigues +intriguing +intriguingly +intrinsic +intrinsically +intro +introduce +introduced +introduces +introducing +introduction +introductions +introductory +introit +introits +intros +introspect +introspected +introspecting +introspection +introspective +introspectively +introspects +introversion +introvert +introverted +introverts +intrude +intruded +intruder +intruders +intrudes +intruding +intrusion +intrusions +intrusive +intrusively +intrusiveness +intrust +intrusted +intrusting +intrusts +intuit +intuited +intuiting +intuition +intuitions +intuitive +intuitively +intuitiveness +intuits +inundate +inundated +inundates +inundating +inundation +inundations +inure +inured +inures +inuring +invade +invaded +invader +invaders +invades +invading +invalid +invalidate +invalidated +invalidates +invalidating +invalidation +invalided +invaliding +invalidism +invalidity +invalidly +invalids +invaluable +invaluably +invariability +invariable +invariables +invariably +invasion +invasions +invasive +invective +inveigh +inveighed +inveighing +inveighs +inveigle +inveigled +inveigler +inveiglers +inveigles +inveigling +invent +invented +inventing +invention +inventions +inventive +inventively +inventiveness +inventor +inventoried +inventories +inventors +inventory +inventorying +invents +inverse +inversely +inverses +inversion +inversions +invert +invertebrate +invertebrates +inverted +inverting +inverts +invest +invested +investigate +investigated +investigates +investigating +investigation +investigations +investigative +investigator +investigators +investigatory +investing +investiture +investitures +investment +investments +investor +investors +invests +inveteracy +inveterate +invidious +invidiously +invidiousness +invigorate +invigorated +invigorates +invigorating +invigoratingly +invigoration +invincibility +invincible +invincibly +inviolability +inviolable +inviolably +inviolate +invisibility +invisible +invisibly +invitation +invitational +invitationals +invitations +invite +invited +invitee +invitees +invites +inviting +invitingly +invocation +invocations +invoice +invoiced +invoices +invoicing +invoke +invoked +invokes +invoking +involuntarily +involuntariness +involuntary +involution +involve +involved +involvement +involvements +involves +involving +invulnerability +invulnerable +invulnerably +inward +inwardly +inwards +iodide +iodides +iodine +iodize +iodized +iodizes +iodizing +ionic +ionization +ionize +ionized +ionizer +ionizers +ionizes +ionizing +ionosphere +ionospheres +ionospheric +ions +iota +iotas +ipecac +ipecacs +irascibility +irascible +irascibly +irate +irately +irateness +ireful +irenic +irides +iridescence +iridescent +iridescently +iridium +iris +irises +irked +irking +irks +irksome +irksomely +irksomeness +iron +ironclad +ironclads +ironed +ironic +ironical +ironically +ironies +ironing +irons +ironstone +ironware +ironwood +ironwoods +ironwork +irony +irradiate +irradiated +irradiates +irradiating +irradiation +irrational +irrationality +irrationally +irrationals +irreclaimable +irreconcilability +irreconcilable +irreconcilably +irrecoverable +irrecoverably +irredeemable +irredeemably +irreducible +irreducibly +irrefutable +irrefutably +irregardless +irregular +irregularities +irregularity +irregularly +irregulars +irrelevance +irrelevances +irrelevancies +irrelevancy +irrelevant +irrelevantly +irreligious +irremediable +irremediably +irremovable +irreparable +irreparably +irreplaceable +irrepressible +irrepressibly +irreproachable +irreproachably +irresistible +irresistibly +irresolute +irresolutely +irresoluteness +irresolution +irrespective +irresponsibility +irresponsible +irresponsibly +irretrievable +irretrievably +irreverence +irreverent +irreverently +irreversible +irreversibly +irrevocable +irrevocably +irrigable +irrigate +irrigated +irrigates +irrigating +irrigation +irritability +irritable +irritably +irritant +irritants +irritate +irritated +irritates +irritating +irritatingly +irritation +irritations +irrupt +irrupted +irrupting +irruption +irruptions +irruptive +irrupts +isinglass +island +islander +islanders +islands +isle +isles +islet +islets +isms +isobar +isobaric +isobars +isolate +isolated +isolates +isolating +isolation +isolationism +isolationist +isolationists +isomer +isomeric +isomerism +isomers +isometric +isometrically +isometrics +isosceles +isotherm +isotherms +isotope +isotopes +isotopic +issuance +issue +issued +issuer +issuers +issues +issuing +isthmi +isthmian +isthmus +isthmuses +italic +italicization +italicize +italicized +italicizes +italicizing +italics +itch +itched +itches +itchier +itchiest +itchiness +itching +itchy +item +itemization +itemize +itemized +itemizes +itemizing +items +iterate +iterated +iterates +iterating +iteration +iterations +iterative +itinerant +itinerants +itineraries +itinerary +itself +ivied +ivies +ivories +ivory +jabbed +jabber +jabbered +jabberer +jabberers +jabbering +jabbers +jabbing +jabot +jabots +jabs +jacaranda +jacarandas +jack +jackal +jackals +jackass +jackasses +jackboot +jackboots +jackdaw +jackdaws +jacked +jacket +jacketed +jackets +jackhammer +jackhammers +jacking +jackknife +jackknifed +jackknifes +jackknifing +jackknives +jackpot +jackpots +jackrabbit +jackrabbits +jacks +jackstraw +jackstraws +jacquard +jade +jaded +jadedly +jadedness +jadeite +jades +jading +jagged +jaggeder +jaggedest +jaggedly +jaggedness +jags +jaguar +jaguars +jail +jailbird +jailbirds +jailbreak +jailbreaks +jailed +jailer +jailers +jailing +jailor +jailors +jails +jalapeno +jalapenos +jalopies +jalopy +jalousie +jalousies +jamb +jambalaya +jamboree +jamborees +jambs +jammed +jamming +jams +jangle +jangled +jangler +janglers +jangles +jangling +janitor +janitorial +janitors +japan +japanned +japanning +japans +jape +japed +japes +japing +jardiniere +jardinieres +jarful +jarfuls +jargon +jarred +jarring +jarringly +jars +jasmine +jasmines +jasper +jato +jatos +jaundice +jaundiced +jaundices +jaundicing +jaunt +jaunted +jauntier +jauntiest +jauntily +jauntiness +jaunting +jaunts +jaunty +java +javelin +javelins +jawbone +jawboned +jawbones +jawboning +jawbreaker +jawbreakers +jawed +jawing +jaws +jaybird +jaybirds +jays +jaywalk +jaywalked +jaywalker +jaywalkers +jaywalking +jaywalks +jazz +jazzed +jazzes +jazzier +jazziest +jazzing +jazzy +jealous +jealousies +jealously +jealousy +jean +jeans +jeep +jeeps +jeer +jeered +jeering +jeeringly +jeers +jeez +jehad +jehads +jejuna +jejune +jejunum +jejunums +jell +jelled +jellied +jellies +jelling +jello +jells +jelly +jellybean +jellybeans +jellyfish +jellyfishes +jellying +jellylike +jellyroll +jellyrolls +jennet +jennets +jennies +jenny +jeopardize +jeopardized +jeopardizes +jeopardizing +jeopardy +jeremiad +jeremiads +jerk +jerked +jerkier +jerkiest +jerkily +jerkin +jerkiness +jerking +jerkins +jerks +jerkwater +jerky +jerrybuilt +jersey +jerseys +jessamine +jessamines +jest +jested +jester +jesters +jesting +jestingly +jests +jetliner +jetliners +jetport +jetports +jets +jetsam +jetted +jetties +jetting +jettison +jettisoned +jettisoning +jettisons +jetty +jewed +jewel +jeweled +jeweler +jewelers +jeweling +jewelled +jeweller +jewelleries +jewellers +jewellery +jewelling +jewelries +jewelry +jewels +jewing +jews +jibbed +jibbing +jibe +jibed +jibes +jibing +jibs +jiff +jiffies +jiffs +jiffy +jigged +jigger +jiggered +jiggering +jiggers +jigging +jiggle +jiggled +jiggles +jigglier +jiggliest +jiggling +jiggly +jigs +jigsaw +jigsawed +jigsawing +jigsawn +jigsaws +jihad +jihads +jilt +jilted +jilting +jilts +jimmied +jimmies +jimmy +jimmying +jimsonweed +jingle +jingled +jingles +jinglier +jingliest +jingling +jingly +jingoism +jingoist +jingoistic +jingoists +jinn +jinni +jinnis +jinns +jinricksha +jinrickshas +jinrikisha +jinrikishas +jinriksha +jinrikshas +jinx +jinxed +jinxes +jinxing +jitney +jitneys +jitterbug +jitterbugged +jitterbugger +jitterbuggers +jitterbugging +jitterbugs +jitterier +jitteriest +jitters +jittery +jiujitsu +jive +jived +jives +jiving +jobbed +jobber +jobbers +jobbing +jobholder +jobholders +jobless +joblessness +jobs +jock +jockey +jockeyed +jockeying +jockeys +jocks +jockstrap +jockstraps +jocose +jocosely +jocoseness +jocosity +jocular +jocularity +jocularly +jocund +jocundity +jocundly +jodhpurs +jogged +jogger +joggers +jogging +joggle +joggled +joggles +joggling +jogs +john +johnnies +johnny +johnnycake +johnnycakes +johns +join +joined +joiner +joiners +joinery +joining +joins +joint +jointed +jointing +jointly +joints +joist +joists +joke +joked +joker +jokers +jokes +jokey +jokier +jokiest +joking +jokingly +joky +jollied +jollier +jollies +jolliest +jollification +jollifications +jollily +jolliness +jollity +jolly +jollying +jolt +jolted +jolter +jolters +jolting +jolts +jonquil +jonquils +josh +joshed +josher +joshers +joshes +joshing +jostle +jostled +jostles +jostling +jots +jotted +jotter +jotters +jotting +jottings +joule +joules +jounce +jounced +jounces +jouncier +jounciest +jouncing +jouncy +journal +journalese +journalism +journalist +journalistic +journalists +journals +journey +journeyed +journeyer +journeyers +journeying +journeyman +journeymen +journeys +joust +jousted +jouster +jousters +jousting +jousts +jovial +joviality +jovially +jowl +jowlier +jowliest +jowls +jowly +joyed +joyful +joyfuller +joyfullest +joyfully +joyfulness +joying +joyless +joylessly +joylessness +joyous +joyously +joyousness +joyridden +joyride +joyrider +joyriders +joyrides +joyriding +joyrode +joys +joystick +joysticks +jubilant +jubilantly +jubilation +jubilee +jubilees +judge +judged +judgement +judgements +judges +judgeship +judging +judgment +judgmental +judgmentally +judgments +judicatories +judicatory +judicature +judicial +judicially +judiciaries +judiciary +judicious +judiciously +judiciousness +judo +jugful +jugfuls +jugged +juggernaut +juggernauts +jugging +juggle +juggled +juggler +jugglers +jugglery +juggles +juggling +jugs +jugular +jugulars +juice +juiced +juicer +juicers +juices +juicier +juiciest +juicily +juiciness +juicing +juicy +jujitsu +jujube +jujubes +jujutsu +jukebox +jukeboxes +julep +juleps +julienne +julienned +juliennes +julienning +jumble +jumbled +jumbles +jumbling +jumbo +jumbos +jump +jumped +jumper +jumpers +jumpier +jumpiest +jumpily +jumpiness +jumping +jumps +jumpsuit +jumpsuits +jumpy +junco +juncoes +juncos +junction +junctions +juncture +junctures +jungle +jungles +junior +juniors +juniper +junipers +junk +junked +junker +junkers +junket +junketed +junketeer +junketeers +junketer +junketers +junketing +junkets +junkie +junkier +junkies +junkiest +junking +junks +junky +junkyard +junkyards +junta +juntas +juridic +juridical +juridically +juries +jurisdiction +jurisdictional +jurisprudence +jurist +juristic +jurists +juror +jurors +jury +juryman +jurymen +jurywoman +jurywomen +just +juster +justest +justice +justices +justifiable +justifiably +justification +justifications +justified +justifies +justify +justifying +justly +justness +jute +juts +jutted +jutting +juvenile +juveniles +juxtapose +juxtaposed +juxtaposes +juxtaposing +juxtaposition +juxtapositions +kabob +kabobs +kabuki +kaddish +kaddishes +kaffeeklatch +kaffeeklatches +kaffeeklatsch +kaffeeklatsches +kaftan +kaftans +kaiser +kaisers +kale +kaleidoscope +kaleidoscopes +kaleidoscopic +kaleidoscopically +kamikaze +kamikazes +kangaroo +kangaroos +kaolin +kapok +kappa +kappas +kaput +karakul +karaoke +karaokes +karat +karate +karats +karma +karmic +kart +karts +katydid +katydids +kayak +kayaked +kayaking +kayaks +kayo +kayoed +kayoing +kayos +kazoo +kazoos +kebab +kebabs +kebob +kebobs +keel +keeled +keelhaul +keelhauled +keelhauling +keelhauls +keeling +keels +keen +keened +keener +keenest +keening +keenly +keenness +keens +keep +keeper +keepers +keeping +keeps +keepsake +keepsakes +kegs +kelp +kelvin +kelvins +kenned +kennel +kenneled +kenneling +kennelled +kennelling +kennels +kenning +keno +kens +kent +kepi +kepis +kept +keratin +kerb +kerbs +kerchief +kerchiefs +kerchieves +kernel +kernels +kerosene +kerosine +kestrel +kestrels +ketch +ketches +ketchup +kettle +kettledrum +kettledrums +kettles +keyboard +keyboarded +keyboarder +keyboarders +keyboarding +keyboardist +keyboardists +keyboards +keyed +keyhole +keyholes +keying +keynote +keynoted +keynoter +keynoters +keynotes +keynoting +keypad +keypads +keypunch +keypunched +keypuncher +keypunchers +keypunches +keypunching +keys +keystone +keystones +keystroke +keystrokes +keyword +keywords +khaki +khakis +khan +khans +kibble +kibbled +kibbles +kibbling +kibbutz +kibbutzim +kibitz +kibitzed +kibitzer +kibitzers +kibitzes +kibitzing +kibosh +kick +kickback +kickbacks +kickball +kicked +kicker +kickers +kickier +kickiest +kicking +kickoff +kickoffs +kicks +kickstand +kickstands +kicky +kidded +kidder +kidders +kiddie +kiddies +kidding +kiddish +kiddo +kiddoes +kiddos +kiddy +kidnap +kidnaped +kidnaper +kidnapers +kidnaping +kidnapped +kidnapper +kidnappers +kidnapping +kidnappings +kidnaps +kidney +kidneys +kids +kidskin +kielbasa +kielbasas +kielbasi +kielbasy +kill +killdeer +killdeers +killed +killer +killers +killing +killings +killjoy +killjoys +kills +kiln +kilned +kilning +kilns +kilo +kilobyte +kilobytes +kilocycle +kilocycles +kilogram +kilograms +kilohertz +kilohertzes +kiloliter +kiloliters +kilometer +kilometers +kilometre +kilometres +kilos +kiloton +kilotons +kilowatt +kilowatts +kilt +kilter +kilts +kimono +kimonos +kind +kinda +kinder +kindergarten +kindergartener +kindergarteners +kindergartens +kindergartner +kindergartners +kindest +kindhearted +kindheartedly +kindheartedness +kindle +kindled +kindles +kindlier +kindliest +kindliness +kindling +kindly +kindness +kindnesses +kindred +kinds +kine +kinematic +kinematics +kinetic +kinetically +kinetics +kinfolk +kinfolks +king +kingdom +kingdoms +kingfisher +kingfishers +kinglier +kingliest +kingly +kingpin +kingpins +kings +kingship +kink +kinked +kinkier +kinkiest +kinkily +kinkiness +kinking +kinks +kinky +kinsfolk +kinsfolks +kinship +kinsman +kinsmen +kinswoman +kinswomen +kiosk +kiosks +kipped +kipper +kippered +kippering +kippers +kipping +kips +kirk +kirks +kirsch +kismet +kiss +kissable +kissed +kisser +kissers +kisses +kissing +kissoff +kissoffs +kitbag +kitbags +kitchen +kitchenette +kitchenettes +kitchens +kitchenware +kite +kited +kites +kith +kiting +kits +kitsch +kitschier +kitschiest +kitschy +kitten +kittenish +kittens +kitties +kitty +kiwi +kiwifruit +kiwifruits +kiwis +kleptomania +kleptomaniac +kleptomaniacs +klutz +klutzes +klutzier +klutziest +klutziness +klutzy +knack +knacks +knackwurst +knackwursts +knapsack +knapsacks +knave +knavery +knaves +knavish +knavishly +knead +kneaded +kneader +kneaders +kneading +kneads +knee +kneecap +kneecapped +kneecapping +kneecaps +kneed +kneeing +kneel +kneeled +kneeling +kneels +knees +knell +knelled +knelling +knells +knelt +knew +knickerbockers +knickers +knickknack +knickknacks +knife +knifed +knifes +knifing +knight +knighted +knighthood +knighthoods +knighting +knightlier +knightliest +knightliness +knightly +knights +knish +knishes +knit +knits +knitted +knitter +knitters +knitting +knitwear +knives +knob +knobbier +knobbiest +knobby +knobs +knock +knockdown +knockdowns +knocked +knocker +knockers +knocking +knockoff +knockoffs +knockout +knockouts +knocks +knockwurst +knockwursts +knoll +knolls +knot +knothole +knotholes +knots +knotted +knottier +knottiest +knotting +knotty +know +knowable +knowing +knowingly +knowledge +knowledgeable +knowledgeably +known +knows +knuckle +knuckled +knucklehead +knuckleheads +knuckles +knuckling +knurl +knurled +knurling +knurls +koala +koalas +kohlrabi +kohlrabies +kola +kolas +kook +kookaburra +kookaburras +kookie +kookier +kookiest +kookiness +kooks +kooky +kopeck +kopecks +kopek +kopeks +kosher +koshered +koshering +koshers +kowtow +kowtowed +kowtowing +kowtows +kraal +kraals +kraut +krauts +krill +krona +krone +kroner +kronor +kronur +krypton +kuchen +kuchens +kudos +kudzu +kudzus +kumquat +kumquats +kvetch +kvetched +kvetcher +kvetchers +kvetches +kvetching +label +labeled +labeling +labelled +labelling +labels +labia +labial +labials +labile +labium +labor +laboratories +laboratory +labored +laborer +laborers +laboring +laborious +laboriously +laboriousness +labors +laborsaving +labour +laboured +labouring +labours +labs +laburnum +laburnums +labyrinth +labyrinthine +labyrinths +lace +laced +lacerate +lacerated +lacerates +lacerating +laceration +lacerations +laces +lacewing +lacewings +lacework +lachrymal +lachrymose +lacier +laciest +lacing +lack +lackadaisical +lackadaisically +lacked +lackey +lackeys +lacking +lackluster +lacklustre +lacks +laconic +laconically +lacquer +lacquered +lacquering +lacquers +lacrimal +lacrosse +lactate +lactated +lactates +lactating +lactation +lacteal +lactic +lactose +lacuna +lacunae +lacunas +lacy +ladder +laddered +laddering +ladders +laddie +laddies +lade +laded +laden +lades +ladies +lading +ladings +ladle +ladled +ladles +ladling +lads +lady +ladybird +ladybirds +ladybug +ladybugs +ladyfinger +ladyfingers +ladylike +ladylove +ladyloves +ladyship +laetrile +lager +lagers +laggard +laggardly +laggards +lagged +lagging +lagnappe +lagnappes +lagniappe +lagniappes +lagoon +lagoons +lags +laid +lain +lair +laird +lairds +lairs +laity +lake +lakefront +lakes +lallygag +lallygagged +lallygagging +lallygags +lama +lamas +lamaseries +lamasery +lamb +lambada +lambadas +lambast +lambaste +lambasted +lambastes +lambasting +lambasts +lambda +lambdas +lambed +lambency +lambent +lambently +lambing +lambkin +lambkins +lambs +lambskin +lambskins +lame +lamebrain +lamebrained +lamebrains +lamed +lamely +lameness +lament +lamentable +lamentably +lamentation +lamentations +lamented +lamenting +laments +lamer +lames +lamest +lamina +laminae +laminar +laminas +laminate +laminated +laminates +laminating +lamination +laming +lammed +lamming +lamp +lampblack +lamplight +lamplighter +lamplighters +lampoon +lampooned +lampooning +lampoons +lamppost +lampposts +lamprey +lampreys +lamps +lampshade +lampshades +lams +lanai +lanais +lance +lanced +lancer +lancers +lances +lancet +lancets +lancing +land +landau +landaus +landed +landfall +landfalls +landfill +landfills +landholder +landholders +landholding +landing +landings +landladies +landlady +landless +landlocked +landlord +landlords +landlubber +landlubbers +landmark +landmarks +landmass +landmasses +landowner +landowners +landowning +lands +landscape +landscaped +landscaper +landscapers +landscapes +landscaping +landslid +landslidden +landslide +landslides +landsliding +landsman +landsmen +landward +landwards +lane +lanes +language +languages +languid +languidly +languidness +languish +languished +languishes +languishing +languor +languorous +languorously +lank +lanker +lankest +lankier +lankiest +lankiness +lankly +lankness +lanky +lanolin +lantern +lanterns +lanthanum +lanyard +lanyards +lapboard +lapboards +lapdog +lapdogs +lapel +lapels +lapidaries +lapidary +lapin +lapins +lapped +lappet +lappets +lapping +laps +lapse +lapsed +lapses +lapsing +laptop +laptops +lapwing +lapwings +larboard +larboards +larcenies +larcenist +larcenists +larcenous +larceny +larch +larches +lard +larded +larder +larders +lardier +lardiest +larding +lards +lardy +large +largehearted +largely +largeness +larger +larges +largess +largesse +largest +largish +largo +largos +lariat +lariats +lark +larked +larking +larks +larkspur +larkspurs +larva +larvae +larval +larvas +laryngeal +larynges +laryngitis +larynx +larynxes +lasagna +lasagnas +lasagne +lasagnes +lascivious +lasciviously +lasciviousness +laser +lasers +lash +lashed +lashes +lashing +lashings +lass +lasses +lassie +lassies +lassitude +lasso +lassoed +lassoes +lassoing +lassos +last +lasted +lasting +lastingly +lastly +lasts +latch +latched +latches +latching +latchkey +latchkeys +late +latecomer +latecomers +lately +latency +lateness +latent +later +lateral +lateraled +lateraling +lateralled +lateralling +laterally +laterals +latest +latex +lath +lathe +lathed +lather +lathered +lathering +lathers +lathery +lathes +lathing +laths +latish +latitude +latitudes +latitudinal +latitudinarian +latitudinarians +latrine +latrines +latte +latter +latterly +lattes +lattice +latticed +lattices +latticework +latticeworks +laud +laudable +laudably +laudanum +laudatory +lauded +lauding +lauds +laugh +laughable +laughably +laughed +laughing +laughingly +laughingstock +laughingstocks +laughs +laughter +launch +launched +launcher +launchers +launches +launching +launchpad +launchpads +launder +laundered +launderer +launderers +launderette +launderettes +laundering +launders +laundress +laundresses +laundrette +laundrettes +laundries +laundromat +laundromats +laundry +laundryman +laundrymen +laundrywoman +laundrywomen +laureate +laureates +laureateship +laurel +laurels +lava +lavage +lavalier +lavaliere +lavalieres +lavaliers +lavatories +lavatory +lave +laved +lavender +lavenders +laves +laving +lavish +lavished +lavisher +lavishes +lavishest +lavishing +lavishly +lavishness +lawbreaker +lawbreakers +lawbreaking +lawful +lawfully +lawfulness +lawgiver +lawgivers +lawless +lawlessly +lawlessness +lawmaker +lawmakers +lawmaking +lawman +lawmen +lawn +lawnmower +lawnmowers +lawns +lawrencium +laws +lawsuit +lawsuits +lawyer +lawyers +laxative +laxatives +laxer +laxest +laxity +laxly +laxness +layaway +layer +layered +layering +layers +layette +layettes +laying +layman +laymen +layoff +layoffs +layout +layouts +layover +layovers +laypeople +layperson +laypersons +lays +layup +layups +laywoman +laywomen +laze +lazed +lazes +lazied +lazier +lazies +laziest +lazily +laziness +lazing +lazy +lazybones +lazying +leach +leached +leaches +leaching +lead +leaded +leaden +leader +leaderless +leaders +leadership +leading +leads +leaf +leafage +leafed +leafier +leafiest +leafing +leafless +leaflet +leafleted +leafleting +leaflets +leafletted +leafletting +leafs +leafstalk +leafstalks +leafy +league +leagued +leagues +leaguing +leak +leakage +leakages +leaked +leakier +leakiest +leakiness +leaking +leaks +leaky +lean +leaned +leaner +leanest +leaning +leanings +leanness +leans +leant +leap +leaped +leaper +leapers +leapfrog +leapfrogged +leapfrogging +leapfrogs +leaping +leaps +leapt +learn +learned +learnedly +learner +learners +learning +learns +learnt +leas +lease +leaseback +leasebacks +leased +leasehold +leaseholder +leaseholders +leaseholds +leaser +leasers +leases +leash +leashed +leashes +leashing +leasing +least +leastways +leastwise +leather +leatherette +leatherneck +leathernecks +leathery +leave +leaved +leaven +leavened +leavening +leavens +leaver +leavers +leaves +leaving +leavings +lech +lecher +lecherous +lecherously +lecherousness +lechers +lechery +leches +lecithin +lectern +lecterns +lecture +lectured +lecturer +lecturers +lectures +lectureship +lectureships +lecturing +ledge +ledger +ledgers +ledges +leech +leeched +leeches +leeching +leek +leeks +leer +leered +leerier +leeriest +leeriness +leering +leers +leery +lees +leeward +leewards +leeway +left +lefter +leftest +leftie +lefties +leftism +leftist +leftists +leftmost +leftover +leftovers +lefts +leftward +leftwards +lefty +legacies +legacy +legal +legalese +legalism +legalisms +legalistic +legality +legalization +legalize +legalized +legalizes +legalizing +legally +legals +legate +legatee +legatees +legates +legation +legations +legato +legatos +legend +legendarily +legendary +legends +legerdemain +legged +leggier +leggiest +leggin +legginess +legging +leggings +leggins +leggy +leghorn +leghorns +legibility +legible +legibly +legion +legionaries +legionary +legionnaire +legionnaires +legions +legislate +legislated +legislates +legislating +legislation +legislative +legislatively +legislator +legislators +legislature +legislatures +legit +legitimacy +legitimate +legitimated +legitimately +legitimates +legitimating +legitimatize +legitimatized +legitimatizes +legitimatizing +legitimization +legitimize +legitimized +legitimizes +legitimizing +legless +legman +legmen +legroom +legrooms +legs +legume +legumes +leguminous +legwork +leis +leisure +leisured +leisureliness +leisurely +leisurewear +leitmotif +leitmotifs +leitmotiv +leitmotivs +lemming +lemmings +lemon +lemonade +lemons +lemony +lemur +lemurs +lend +lender +lenders +lending +lends +length +lengthen +lengthened +lengthening +lengthens +lengthier +lengthiest +lengthily +lengthiness +lengths +lengthways +lengthwise +lengthy +lenience +leniency +lenient +leniently +lenitive +lens +lenses +lent +lenten +lentil +lentils +lento +leonine +leopard +leopardess +leopardesses +leopards +leotard +leotards +leper +lepers +leprechaun +leprechauns +leprosy +leprous +lept +lepta +lepton +leptons +lesbian +lesbianism +lesbians +lesion +lesions +less +lessee +lessees +lessen +lessened +lessening +lessens +lesser +lesson +lessons +lessor +lessors +lest +letdown +letdowns +lethal +lethally +lethargic +lethargically +lethargy +lets +letter +lettered +letterer +letterers +letterhead +letterheads +lettering +letterpress +letters +letting +lettuce +lettuces +letup +letups +leukaemia +leukemia +leukemic +leukemics +leukocyte +leukocytes +levee +levees +level +leveled +leveler +levelers +levelheaded +levelheadedness +leveling +levelled +leveller +levellers +levelling +levelly +levelness +levels +lever +leverage +leveraged +leverages +leveraging +levered +levering +levers +leviathan +leviathans +levied +levier +leviers +levies +levitate +levitated +levitates +levitating +levitation +levity +levy +levying +lewd +lewder +lewdest +lewdly +lewdness +lexica +lexical +lexicographer +lexicographers +lexicographic +lexicographical +lexicography +lexicon +lexicons +liabilities +liability +liable +liaise +liaised +liaises +liaising +liaison +liaisons +liar +liars +libation +libations +libber +libbers +libel +libeled +libeler +libelers +libeling +libelled +libeller +libellers +libelling +libellous +libelous +libels +liberal +liberalism +liberality +liberalization +liberalizations +liberalize +liberalized +liberalizes +liberalizing +liberally +liberalness +liberals +liberate +liberated +liberates +liberating +liberation +liberator +liberators +libertarian +libertarians +liberties +libertine +libertines +liberty +libidinal +libidinous +libido +libidos +librarian +librarians +libraries +library +libretti +librettist +librettists +libretto +librettos +lice +licence +licenced +licences +licencing +license +licensed +licensee +licensees +licenses +licensing +licentiate +licentiates +licentious +licentiously +licentiousness +lichee +lichees +lichen +lichens +licit +licitly +lick +licked +licking +lickings +licks +licorice +licorices +lidded +lidless +lido +lidos +lids +lied +lieder +lief +liefer +liefest +liege +lieges +lien +liens +lies +lieu +lieutenancy +lieutenant +lieutenants +life +lifeblood +lifeboat +lifeboats +lifebuoy +lifebuoys +lifeguard +lifeguards +lifeless +lifelessly +lifelessness +lifelike +lifeline +lifelines +lifelong +lifer +lifers +lifesaver +lifesavers +lifesaving +lifestyle +lifestyles +lifetime +lifetimes +lifework +lifeworks +lift +lifted +lifter +lifters +lifting +liftoff +liftoffs +lifts +ligament +ligaments +ligate +ligated +ligates +ligating +ligation +ligature +ligatured +ligatures +ligaturing +light +lighted +lighten +lightened +lightener +lighteners +lightening +lightens +lighter +lighters +lightest +lightface +lightfaced +lightheaded +lighthearted +lightheartedly +lightheartedness +lighthouse +lighthouses +lighting +lightly +lightness +lightning +lightninged +lightnings +lightproof +lights +lightship +lightships +lightweight +lightweights +ligneous +lignite +likability +likable +likableness +like +likeable +liked +likelier +likeliest +likelihood +likelihoods +likeliness +likely +liken +likened +likeness +likenesses +likening +likens +liker +likes +likest +likewise +liking +lilac +lilacs +lilies +lilliputian +lilt +lilted +lilting +lilts +lily +limb +limber +limbered +limbering +limberness +limbers +limbless +limbo +limbos +limbs +lime +limeade +limeades +limed +limelight +limerick +limericks +limes +limestone +limier +limiest +liming +limit +limitation +limitations +limited +limiter +limiters +limiting +limitless +limitlessness +limits +limn +limned +limning +limns +limo +limos +limousine +limousines +limp +limped +limper +limpest +limpet +limpets +limpid +limpidity +limpidly +limpidness +limping +limply +limpness +limps +limy +linage +linchpin +linchpins +linden +lindens +line +lineage +lineages +lineal +lineally +lineament +lineaments +linear +linearity +linearly +linebacker +linebackers +lined +lineman +linemen +linen +linens +liner +liners +lines +linesman +linesmen +lineup +lineups +ling +linger +lingered +lingerer +lingerers +lingerie +lingering +lingeringly +lingers +lingo +lingoes +lingos +lings +lingual +linguine +linguini +linguist +linguistic +linguistically +linguistics +linguists +liniment +liniments +lining +linings +link +linkage +linkages +linked +linking +links +linkup +linkups +linnet +linnets +linoleum +linseed +lint +lintel +lintels +lintier +lintiest +linty +lion +lioness +lionesses +lionhearted +lionization +lionize +lionized +lionizes +lionizing +lions +lipid +lipids +liposuction +lipped +lippier +lippiest +lippy +lipread +lipreader +lipreaders +lipreading +lipreads +lips +lipstick +lipsticks +liquefaction +liquefied +liquefies +liquefy +liquefying +liqueur +liqueurs +liquid +liquidate +liquidated +liquidates +liquidating +liquidation +liquidations +liquidator +liquidators +liquidity +liquidize +liquidized +liquidizer +liquidizers +liquidizes +liquidizing +liquids +liquified +liquifies +liquify +liquifying +liquor +liquored +liquorice +liquorices +liquoring +liquors +lira +liras +lire +lisle +lisp +lisped +lisper +lispers +lisping +lisps +lissom +lissome +list +listed +listen +listened +listener +listeners +listening +listens +listing +listings +listless +listlessly +listlessness +lists +litanies +litany +litchi +litchis +lite +liter +literacy +literal +literally +literalness +literals +literariness +literary +literate +literately +literates +literati +literature +liters +lithe +lithely +litheness +lither +lithesome +lithest +lithium +lithograph +lithographed +lithographer +lithographers +lithographic +lithographically +lithographing +lithographs +lithography +lithosphere +lithospheres +litigant +litigants +litigate +litigated +litigates +litigating +litigation +litigator +litigators +litigious +litigiousness +litmus +litotes +litre +litres +litter +litterateur +litterateurs +litterbug +litterbugs +littered +litterer +litterers +littering +litters +little +littleness +littler +littlest +littoral +littorals +liturgical +liturgically +liturgies +liturgist +liturgists +liturgy +livability +livable +live +liveable +lived +livelier +liveliest +livelihood +livelihoods +liveliness +livelong +lively +liven +livened +livening +livens +liver +liveried +liveries +liverish +livers +liverwort +liverworts +liverwurst +livery +liveryman +liverymen +lives +livest +livestock +livid +lividly +living +livings +lizard +lizards +llama +llamas +llano +llanos +load +loaded +loader +loaders +loading +loads +loadstar +loadstars +loadstone +loadstones +loaf +loafed +loafer +loafers +loafing +loafs +loam +loamier +loamiest +loamy +loan +loaned +loaner +loaners +loaning +loans +loansharking +loanword +loanwords +loath +loathe +loathed +loather +loathers +loathes +loathing +loathings +loathsome +loathsomely +loathsomeness +loaves +lobar +lobbed +lobber +lobbers +lobbied +lobbies +lobbing +lobby +lobbying +lobbyist +lobbyists +lobe +lobed +lobes +lobotomies +lobotomize +lobotomized +lobotomizes +lobotomizing +lobotomy +lobs +lobster +lobsters +local +locale +locales +localities +locality +localization +localize +localized +localizes +localizing +locally +locals +locate +located +locates +locating +location +locations +locator +locators +loch +lochs +loci +lock +locked +locker +lockers +locket +lockets +locking +lockjaw +lockout +lockouts +locks +locksmith +locksmiths +lockstep +lockup +lockups +loco +locomotion +locomotive +locomotives +locoweed +locoweeds +locus +locust +locusts +locution +locutions +lode +lodes +lodestar +lodestars +lodestone +lodestones +lodge +lodged +lodger +lodgers +lodges +lodging +lodgings +loft +lofted +loftier +loftiest +loftily +loftiness +lofting +lofts +lofty +loganberries +loganberry +logarithm +logarithmic +logarithms +logbook +logbooks +loge +loges +logged +logger +loggerhead +loggerheads +loggers +loggia +loggias +logging +logic +logical +logicality +logically +logician +logicians +logier +logiest +logistic +logistical +logistically +logistics +logjam +logjams +logo +logos +logotype +logotypes +logrolling +logs +logy +loin +loincloth +loincloths +loins +loiter +loitered +loiterer +loiterers +loitering +loiters +loll +lolled +lolling +lollipop +lollipops +lolls +lollygag +lollygagged +lollygagging +lollygags +lollypop +lollypops +lone +lonelier +loneliest +loneliness +lonely +loner +loners +lonesome +lonesomely +lonesomeness +long +longboat +longboats +longbow +longbows +longed +longer +longest +longevity +longhair +longhairs +longhand +longhorn +longhorns +longing +longingly +longings +longish +longitude +longitudes +longitudinal +longitudinally +longs +longshoreman +longshoremen +longsighted +longstanding +longtime +longueur +longueurs +longways +loofah +loofahs +look +lookalike +lookalikes +looked +looker +lookers +looking +lookout +lookouts +looks +loom +loomed +looming +looms +loon +looney +looneys +loonier +loonies +looniest +loons +loony +loop +looped +loophole +loopholes +loopier +loopiest +looping +loops +loopy +loose +loosed +loosely +loosen +loosened +looseness +loosening +loosens +looser +looses +loosest +loosing +loot +looted +looter +looters +looting +loots +lope +loped +lopes +loping +lopped +lopping +lops +lopsided +lopsidedly +lopsidedness +loquacious +loquaciousness +loquacity +lord +lorded +lording +lordlier +lordliest +lordliness +lordly +lords +lordship +lordships +lore +lorgnette +lorgnettes +loris +lorises +lorn +lorries +lorry +lose +loser +losers +loses +losing +losings +loss +losses +lost +loth +lotion +lotions +lots +lotteries +lottery +lotto +lotus +lotuses +loud +louder +loudest +loudhailer +loudhailers +loudly +loudmouth +loudmouthed +loudmouths +loudness +loudspeaker +loudspeakers +lounge +lounged +lounger +loungers +lounges +lounging +lour +loured +louring +lours +louse +louses +lousier +lousiest +lousily +lousiness +lousy +lout +loutish +loutishly +louts +louver +louvered +louvers +louvre +louvred +louvres +lovable +lovableness +lovably +love +loveable +lovebird +lovebirds +lovechild +lovechildren +loved +loveless +lovelier +lovelies +loveliest +loveliness +lovelorn +lovely +lovemaking +lover +lovers +loves +loveseat +loveseats +lovesick +loving +lovingly +lowborn +lowboy +lowboys +lowbrow +lowbrows +lowdown +lowed +lower +lowercase +lowered +lowering +lowermost +lowers +lowest +lowing +lowland +lowlander +lowlanders +lowlands +lowlier +lowliest +lowlife +lowlifes +lowliness +lowlives +lowly +lowness +lows +loyal +loyaler +loyalest +loyalism +loyalist +loyalists +loyaller +loyallest +loyally +loyalties +loyalty +lozenge +lozenges +luau +luaus +lubber +lubberly +lubbers +lube +lubed +lubes +lubing +lubricant +lubricants +lubricate +lubricated +lubricates +lubricating +lubrication +lubricator +lubricators +lubricious +lubricity +lucid +lucider +lucidest +lucidity +lucidly +lucidness +luck +lucked +luckier +luckiest +luckily +luckiness +lucking +luckless +lucks +lucky +lucrative +lucratively +lucrativeness +lucre +lucubrate +lucubrated +lucubrates +lucubrating +lucubration +ludicrous +ludicrously +ludicrousness +luff +luffed +luffing +luffs +luggage +lugged +lugger +luggers +lugging +lugs +lugsail +lugsails +lugubrious +lugubriously +lugubriousness +lukewarm +lukewarmly +lukewarmness +lull +lullabies +lullaby +lulled +lulling +lulls +lumbago +lumbar +lumber +lumbered +lumberer +lumberers +lumbering +lumberjack +lumberjacks +lumberman +lumbermen +lumbers +lumberyard +lumberyards +luminaries +luminary +luminescence +luminescent +luminosity +luminous +luminously +lummox +lummoxes +lump +lumped +lumpier +lumpiest +lumpiness +lumping +lumpish +lumps +lumpy +lunacies +lunacy +lunar +lunatic +lunatics +lunch +lunched +luncheon +luncheonette +luncheonettes +luncheons +lunches +lunching +lunchroom +lunchrooms +lunchtime +lunchtimes +lung +lunge +lunged +lunges +lungfish +lungfishes +lunging +lungs +lunkhead +lunkheads +lupin +lupine +lupines +lupins +lupus +lurch +lurched +lurches +lurching +lure +lured +lures +lurid +luridly +luridness +luring +lurk +lurked +lurking +lurks +luscious +lusciously +lusciousness +lush +lusher +lushes +lushest +lushly +lushness +lust +lusted +luster +lusterless +lustful +lustfully +lustier +lustiest +lustily +lustiness +lusting +lustre +lustrous +lustrously +lusts +lusty +lutanist +lutanists +lute +lutenist +lutenists +lutes +lutetium +luxuriance +luxuriant +luxuriantly +luxuriate +luxuriated +luxuriates +luxuriating +luxuriation +luxuries +luxurious +luxuriously +luxuriousness +luxury +lyceum +lyceums +lychee +lychees +lying +lymph +lymphatic +lymphatics +lymphocyte +lymphocytes +lymphoid +lymphoma +lymphomas +lymphomata +lynch +lynched +lyncher +lynchers +lynches +lynching +lynchings +lynchpin +lynchpins +lynx +lynxes +lyre +lyrebird +lyrebirds +lyres +lyric +lyrical +lyrically +lyricism +lyricist +lyricists +lyrics +macabre +macadam +macadamia +macadamias +macadamize +macadamized +macadamizes +macadamizing +macaque +macaques +macaroni +macaronies +macaronis +macaroon +macaroons +macaw +macaws +mace +maced +macerate +macerated +macerates +macerating +maceration +maces +mach +machete +machetes +machinate +machinated +machinates +machinating +machination +machinations +machine +machined +machinery +machines +machining +machinist +machinists +machismo +macho +macing +macintosh +macintoshes +mackerel +mackerels +mackinaw +mackinaws +mackintosh +mackintoshes +macrame +macro +macrobiotic +macrobiotics +macrocosm +macrocosms +macroeconomics +macron +macrons +macros +macroscopic +macs +madam +madame +madams +madcap +madcaps +madden +maddened +maddening +maddeningly +maddens +madder +madders +maddest +madding +made +mademoiselle +mademoiselles +madhouse +madhouses +madly +madman +madmen +madness +madras +madrases +madrigal +madrigals +mads +madwoman +madwomen +maelstrom +maelstroms +maestri +maestro +maestros +mafia +mafias +mafiosi +mafioso +mafiosos +magazine +magazines +magenta +maggot +maggots +maggoty +magi +magic +magical +magically +magician +magicians +magisterial +magisterially +magistracy +magistrate +magistrates +magma +magnanimity +magnanimous +magnanimously +magnate +magnates +magnesia +magnesium +magnet +magnetic +magnetically +magnetise +magnetised +magnetises +magnetising +magnetism +magnetite +magnetizable +magnetization +magnetize +magnetized +magnetizes +magnetizing +magneto +magnetometer +magnetometers +magnetos +magnets +magnification +magnifications +magnificence +magnificent +magnificently +magnified +magnifier +magnifiers +magnifies +magnify +magnifying +magniloquence +magniloquent +magnitude +magnitudes +magnolia +magnolias +magnum +magnums +magpie +magpies +mags +magus +maharaja +maharajah +maharajahs +maharajas +maharanee +maharanees +maharani +maharanis +maharishi +maharishis +mahatma +mahatmas +mahjong +mahoganies +mahogany +mahout +mahouts +maid +maiden +maidenhair +maidenhead +maidenheads +maidenhood +maidenly +maidens +maids +maidservant +maidservants +mail +mailbag +mailbags +mailbox +mailboxes +mailed +mailer +mailers +mailing +mailings +maillot +maillots +mailman +mailmen +mails +maim +maimed +maiming +maims +main +mainframe +mainframes +mainland +mainlands +mainline +mainlined +mainlines +mainlining +mainly +mainmast +mainmasts +mains +mainsail +mainsails +mainspring +mainsprings +mainstay +mainstays +mainstream +mainstreamed +mainstreaming +mainstreams +maintain +maintainable +maintained +maintaining +maintains +maintenance +maintop +maintops +maiolica +maisonette +maisonettes +maize +majestic +majestically +majesties +majesty +majolica +major +majordomo +majordomos +majored +majorette +majorettes +majoring +majorities +majority +majors +make +makeover +makeovers +maker +makers +makes +makeshift +makeshifts +makeup +makeups +making +makings +malachite +maladies +maladjusted +maladjustment +maladroit +maladroitly +maladroitness +malady +malaise +malamute +malamutes +malapropism +malapropisms +malaria +malarial +malarkey +malathion +malcontent +malcontents +male +malediction +maledictions +malefaction +malefactor +malefactors +malefic +maleficence +maleficent +maleness +males +malevolence +malevolent +malevolently +malfeasance +malformation +malformations +malformed +malfunction +malfunctioned +malfunctioning +malfunctions +malice +malicious +maliciously +maliciousness +malign +malignancies +malignancy +malignant +malignantly +maligned +maligning +malignity +maligns +malinger +malingered +malingerer +malingerers +malingering +malingers +mall +mallard +mallards +malleability +malleable +mallet +mallets +mallow +mallows +malls +malnourished +malnutrition +malocclusion +malodorous +malpractice +malpractices +malt +malted +malteds +maltier +maltiest +malting +maltose +maltreat +maltreated +maltreating +maltreatment +maltreats +malts +malty +mama +mamas +mamba +mambas +mambo +mamboed +mamboing +mambos +mamma +mammal +mammalian +mammalians +mammals +mammary +mammas +mammies +mammogram +mammograms +mammography +mammon +mammoth +mammoths +mammy +manacle +manacled +manacles +manacling +manage +manageability +manageable +managed +management +manager +managerial +managers +manages +managing +manana +mananas +manatee +manatees +mandala +mandalas +mandamus +mandamuses +mandarin +mandarins +mandate +mandated +mandates +mandating +mandatory +mandible +mandibles +mandibular +mandolin +mandolins +mandrake +mandrakes +mandrel +mandrels +mandril +mandrill +mandrills +mandrils +mane +maned +manege +manes +maneuver +maneuverability +maneuverable +maneuvered +maneuvering +maneuvers +manful +manfully +manganese +mange +manger +mangers +mangier +mangiest +manginess +mangle +mangled +mangles +mangling +mango +mangoes +mangos +mangrove +mangroves +mangy +manhandle +manhandled +manhandles +manhandling +manhole +manholes +manhood +manhunt +manhunts +mania +maniac +maniacal +maniacally +maniacs +manias +manic +manically +manics +manicure +manicured +manicures +manicuring +manicurist +manicurists +manifest +manifestation +manifestations +manifested +manifesting +manifestly +manifesto +manifestoes +manifestos +manifests +manifold +manifolded +manifolding +manifolds +manikin +manikins +manila +manilla +manioc +maniocs +manipulable +manipulate +manipulated +manipulates +manipulating +manipulation +manipulations +manipulative +manipulatively +manipulator +manipulators +mankind +manlier +manliest +manlike +manliness +manly +manna +manned +mannequin +mannequins +manner +mannered +mannerism +mannerisms +mannerly +manners +mannikin +mannikins +manning +mannish +mannishly +mannishness +manoeuvre +manoeuvred +manoeuvres +manoeuvring +manometer +manometers +manor +manorial +manors +manpower +manque +mans +mansard +mansards +manse +manservant +manses +mansion +mansions +manslaughter +manta +mantas +mantel +mantelpiece +mantelpieces +mantels +mantes +mantilla +mantillas +mantis +mantises +mantissa +mantissas +mantle +mantled +mantles +mantling +mantra +mantras +manual +manually +manuals +manufacture +manufactured +manufacturer +manufacturers +manufactures +manufacturing +manumission +manumissions +manumit +manumits +manumitted +manumitting +manure +manured +manures +manuring +manuscript +manuscripts +many +maple +maples +mapmaker +mapmakers +mapped +mapper +mappers +mapping +maps +marabou +marabous +marabout +marabouts +maraca +maracas +maraschino +maraschinos +marathon +marathoner +marathoners +marathons +maraud +marauded +marauder +marauders +marauding +marauds +marble +marbled +marbleize +marbleized +marbleizes +marbleizing +marbles +marbling +march +marched +marcher +marchers +marches +marching +marchioness +marchionesses +mare +mares +margarine +margarita +margaritas +margin +marginal +marginalia +marginalization +marginalize +marginalized +marginalizes +marginalizing +marginally +margins +maria +mariachi +mariachis +marigold +marigolds +marihuana +marijuana +marimba +marimbas +marina +marinade +marinaded +marinades +marinading +marinara +marinas +marinate +marinated +marinates +marinating +marination +marine +mariner +mariners +marines +marionette +marionettes +marital +maritally +maritime +marjoram +mark +markdown +markdowns +marked +markedly +marker +markers +market +marketability +marketable +marketed +marketeer +marketeers +marketer +marketers +marketing +marketplace +marketplaces +markets +marking +markings +markka +markkaa +markkas +marks +marksman +marksmanship +marksmen +markup +markups +marl +marlin +marlinespike +marlinespikes +marlins +marlinspike +marlinspikes +marmalade +marmoreal +marmoset +marmosets +marmot +marmots +maroon +marooned +marooning +maroons +marque +marquee +marquees +marques +marquess +marquesses +marquetry +marquis +marquise +marquises +marquisette +marred +marriage +marriageability +marriageable +marriages +married +marrieds +marries +marring +marrow +marrows +marry +marrying +mars +marsh +marshal +marshaled +marshaling +marshalled +marshalling +marshals +marshes +marshier +marshiest +marshland +marshmallow +marshmallows +marshy +marsupial +marsupials +mart +marten +martens +martial +martially +martin +martinet +martinets +martingale +martingales +martini +martinis +martins +marts +martyr +martyrdom +martyred +martyring +martyrs +marvel +marveled +marveling +marvelled +marvelling +marvellous +marvellously +marvelous +marvelously +marvels +marzipan +mascara +mascaraed +mascaraing +mascaras +mascot +mascots +masculine +masculines +masculinity +maser +masers +mash +mashed +masher +mashers +mashes +mashing +mask +masked +masker +maskers +masking +masks +masochism +masochist +masochistic +masochistically +masochists +mason +masonry +masons +masque +masquerade +masqueraded +masquerader +masqueraders +masquerades +masquerading +masques +mass +massacre +massacred +massacres +massacring +massage +massaged +massages +massaging +massed +masses +masseur +masseurs +masseuse +masseuses +massif +massifs +massing +massive +massively +massiveness +mast +mastectomies +mastectomy +masted +master +mastered +masterful +masterfully +mastering +masterly +mastermind +masterminded +masterminding +masterminds +masterpiece +masterpieces +masters +masterstroke +masterstrokes +masterwork +masterworks +mastery +masthead +mastheads +mastic +masticate +masticated +masticates +masticating +mastication +mastiff +mastiffs +mastodon +mastodons +mastoid +mastoids +masts +masturbate +masturbated +masturbates +masturbating +masturbation +masturbatory +matador +matadors +match +matchbook +matchbooks +matchbox +matchboxes +matched +matches +matching +matchless +matchlock +matchlocks +matchmaker +matchmakers +matchmaking +matchstick +matchsticks +matchwood +mate +mated +material +materialise +materialised +materialises +materialising +materialism +materialist +materialistic +materialistically +materialists +materialization +materialize +materialized +materializes +materializing +materially +materials +materiel +maternal +maternally +maternity +mates +math +mathematical +mathematically +mathematician +mathematicians +mathematics +matinee +matinees +mating +matins +matriarch +matriarchal +matriarchies +matriarchs +matriarchy +matrices +matricidal +matricide +matricides +matriculate +matriculated +matriculates +matriculating +matriculation +matrimonial +matrimony +matrix +matrixes +matron +matronly +matrons +mats +matt +matte +matted +matter +mattered +mattering +matters +mattes +matting +mattins +mattock +mattocks +mattress +mattresses +matts +maturate +maturated +maturates +maturating +maturation +mature +matured +maturely +maturer +matures +maturest +maturing +maturities +maturity +matzo +matzoh +matzohs +matzos +matzot +matzoth +maudlin +maul +mauled +mauler +maulers +mauling +mauls +maunder +maundered +maundering +maunders +mausolea +mausoleum +mausoleums +mauve +mauver +mauvest +maven +mavens +maverick +mavericks +mavin +mavins +mawkish +mawkishly +mawkishness +maws +maxed +maxes +maxi +maxilla +maxillae +maxillary +maxillas +maxim +maxima +maximal +maximally +maximization +maximize +maximized +maximizes +maximizing +maxims +maximum +maximums +maxing +maxis +maybe +maybes +mayday +maydays +mayflies +mayflower +mayflowers +mayfly +mayhem +mayo +mayonnaise +mayor +mayoral +mayoralty +mayoress +mayoresses +mayors +maypole +maypoles +mayst +maze +mazes +mazourka +mazourkas +mazurka +mazurkas +mead +meadow +meadowlark +meadowlarks +meadows +meager +meagerer +meagerest +meagerly +meagerness +meagre +meagrer +meagrest +meal +mealier +mealiest +mealiness +meals +mealtime +mealtimes +mealy +mealybug +mealybugs +mealymouthed +mean +meander +meandered +meandering +meanderings +meanders +meaner +meanest +meanie +meanies +meaning +meaningful +meaningfully +meaningfulness +meaningless +meaninglessly +meaninglessness +meanings +meanly +meanness +means +meant +meantime +meanwhile +meany +measles +measlier +measliest +measly +measurable +measurably +measure +measured +measureless +measurement +measurements +measures +measuring +meat +meatball +meatballs +meatier +meatiest +meatiness +meatless +meatloaf +meatloaves +meatpacking +meats +meaty +mecca +meccas +mechanic +mechanical +mechanically +mechanics +mechanism +mechanisms +mechanistic +mechanistically +mechanization +mechanize +mechanized +mechanizes +mechanizing +medal +medalist +medalists +medallion +medallions +medallist +medallists +medals +meddle +meddled +meddler +meddlers +meddles +meddlesome +meddling +media +mediaeval +medial +medially +median +medians +medias +mediate +mediated +mediates +mediating +mediation +mediator +mediators +medic +medicaid +medical +medically +medicals +medicament +medicare +medicate +medicated +medicates +medicating +medication +medications +medicinal +medicinally +medicine +medicines +medico +medicos +medics +medieval +medievalist +medievalists +mediocre +mediocrities +mediocrity +meditate +meditated +meditates +meditating +meditation +meditations +meditative +meditatively +medium +mediums +medley +medleys +meds +medulla +medullae +medullas +meed +meek +meeker +meekest +meekly +meekness +meerschaum +meerschaums +meet +meeting +meetinghouse +meetinghouses +meetings +meets +mega +megabit +megabits +megabucks +megabyte +megabytes +megacycle +megacycles +megadeath +megadeaths +megahertz +megahertzes +megalith +megalithic +megaliths +megalomania +megalomaniac +megalomaniacs +megalopolis +megalopolises +megaphone +megaphoned +megaphones +megaphoning +megaton +megatons +megawatt +megawatts +meiosis +meiotic +melamine +melancholia +melancholic +melancholy +melange +melanges +melanin +melanoma +melanomas +melanomata +meld +melded +melding +melds +melee +melees +meliorate +meliorated +meliorates +meliorating +melioration +meliorative +mellifluous +mellifluously +mellifluousness +mellow +mellowed +mellower +mellowest +mellowing +mellowly +mellowness +mellows +melodic +melodically +melodies +melodious +melodiously +melodiousness +melodrama +melodramas +melodramatic +melodramatically +melodramatics +melody +melon +melons +melt +meltdown +meltdowns +melted +melting +melts +member +members +membership +memberships +membrane +membranes +membranous +memento +mementoes +mementos +memo +memoir +memoirs +memorabilia +memorability +memorable +memorably +memoranda +memorandum +memorandums +memorial +memorialize +memorialized +memorializes +memorializing +memorials +memories +memorization +memorize +memorized +memorizes +memorizing +memory +memos +menace +menaced +menaces +menacing +menacingly +menage +menagerie +menageries +menages +mend +mendacious +mendaciously +mendacity +mended +mendelevium +mender +menders +mendicancy +mendicant +mendicants +mending +mends +menfolk +menfolks +menhaden +menhadens +menial +menially +menials +meningeal +meninges +meningitis +meninx +menisci +meniscus +meniscuses +menopausal +menopause +menorah +menorahs +mensch +mensches +menservants +menses +menstrual +menstruate +menstruated +menstruates +menstruating +menstruation +mensurable +mensuration +menswear +mental +mentalist +mentalists +mentalities +mentality +mentally +menthol +mentholated +mention +mentioned +mentioning +mentions +mentor +mentored +mentoring +mentors +menu +menus +meow +meowed +meowing +meows +mercantile +mercantilism +mercenaries +mercenary +mercer +mercerize +mercerized +mercerizes +mercerizing +mercers +merchandise +merchandised +merchandiser +merchandisers +merchandises +merchandising +merchandize +merchandized +merchandizes +merchandizing +merchant +merchantable +merchantman +merchantmen +merchants +mercies +merciful +mercifully +merciless +mercilessly +mercilessness +mercurial +mercurially +mercuric +mercury +mercy +mere +merely +meres +merest +meretricious +meretriciously +meretriciousness +merganser +mergansers +merge +merged +merger +mergers +merges +merging +meridian +meridians +meringue +meringues +merino +merinos +merit +merited +meriting +meritocracies +meritocracy +meritorious +meritoriously +meritoriousness +merits +mermaid +mermaids +merman +mermen +merrier +merriest +merrily +merriment +merriness +merry +merrymaker +merrymakers +merrymaking +mesa +mesas +mescal +mescaline +mescals +mesdames +mesdemoiselles +mesh +meshed +meshes +meshing +mesmerism +mesmerize +mesmerized +mesmerizer +mesmerizers +mesmerizes +mesmerizing +mesomorph +mesomorphs +meson +mesons +mesosphere +mesospheres +mesquit +mesquite +mesquites +mesquits +mess +message +messaged +messages +messaging +messed +messeigneurs +messenger +messengers +messes +messiah +messiahs +messianic +messier +messiest +messieurs +messily +messiness +messing +messmate +messmates +messy +mestizo +mestizoes +mestizos +metabolic +metabolically +metabolism +metabolisms +metabolite +metabolites +metabolize +metabolized +metabolizes +metabolizing +metacarpal +metacarpals +metacarpi +metacarpus +metal +metalanguage +metalanguages +metallic +metallurgic +metallurgical +metallurgist +metallurgists +metallurgy +metals +metalwork +metalworker +metalworkers +metalworking +metamorphic +metamorphism +metamorphose +metamorphosed +metamorphoses +metamorphosing +metamorphosis +metaphor +metaphoric +metaphorical +metaphorically +metaphors +metaphysical +metaphysically +metaphysics +metastases +metastasis +metastasize +metastasized +metastasizes +metastasizing +metastatic +metatarsal +metatarsals +metatarsi +metatarsus +metatheses +metathesis +mete +meted +metempsychosis +meteor +meteoric +meteorically +meteorite +meteorites +meteoroid +meteoroids +meteorologic +meteorological +meteorologist +meteorologists +meteorology +meteors +meter +metered +metering +meters +metes +methadon +methadone +methamphetamine +methane +methanol +methinks +method +methodical +methodically +methodicalness +methodological +methodologically +methodologies +methodology +methods +methought +methyl +meticulous +meticulously +meticulousness +metier +metiers +meting +metre +metres +metric +metrical +metrically +metricate +metricated +metricates +metricating +metrication +metricize +metricized +metricizes +metricizing +metro +metronome +metronomes +metropolis +metropolises +metropolitan +metros +mettle +mettlesome +mewed +mewing +mewl +mewled +mewling +mewls +mews +mezzanine +mezzanines +mezzo +mezzos +miasma +miasmas +miasmata +mica +mice +mickey +mickeys +micra +micro +microbe +microbes +microbial +microbiological +microbiologist +microbiologists +microbiology +microbreweries +microbrewery +microchip +microchips +microcircuit +microcircuits +microcomputer +microcomputers +microcosm +microcosmic +microcosms +microdot +microdots +microeconomics +microelectronic +microelectronics +microfiber +microfibers +microfiche +microfiches +microfilm +microfilmed +microfilming +microfilms +microgroove +microgrooves +microlight +microlights +micromanage +micromanaged +micromanagement +micromanages +micromanaging +micrometeorite +micrometeorites +micrometer +micrometers +micron +microns +microorganism +microorganisms +microphone +microphones +microprocessor +microprocessors +micros +microscope +microscopes +microscopic +microscopical +microscopically +microscopy +microsecond +microseconds +microsurgery +microwavable +microwave +microwaveable +microwaved +microwaves +microwaving +midair +midday +midden +middens +middies +middle +middlebrow +middlebrows +middleman +middlemen +middlemost +middles +middleweight +middleweights +middling +middy +midge +midges +midget +midgets +midi +midis +midland +midlands +midlife +midmost +midnight +midpoint +midpoints +midrib +midribs +midriff +midriffs +midsection +midsections +midshipman +midshipmen +midships +midsize +midst +midstream +midsummer +midterm +midterms +midtown +midway +midways +midweek +midweeks +midwife +midwifed +midwiferies +midwifery +midwifes +midwifing +midwinter +midwived +midwives +midwiving +midyear +midyears +mien +miens +miff +miffed +miffing +miffs +might +mightier +mightiest +mightily +mightiness +mighty +mignonette +mignonettes +migraine +migraines +migrant +migrants +migrate +migrated +migrates +migrating +migration +migrations +migratory +mikado +mikados +mike +miked +mikes +miking +miladies +milady +milch +mild +milder +mildest +mildew +mildewed +mildewing +mildews +mildly +mildness +mile +mileage +mileages +milepost +mileposts +miler +milers +miles +milestone +milestones +milieu +milieus +milieux +militancy +militant +militantly +militants +militaries +militarily +militarism +militarist +militaristic +militarists +militarization +militarize +militarized +militarizes +militarizing +military +militate +militated +militates +militating +militia +militiaman +militiamen +militias +milk +milked +milker +milkers +milkier +milkiest +milkiness +milking +milkmaid +milkmaids +milkman +milkmen +milks +milkshake +milkshakes +milksop +milksops +milkweed +milkweeds +milky +mill +millage +milled +millennia +millennial +millennium +millenniums +millepede +millepedes +miller +millers +millet +milliard +milliards +millibar +millibars +milligram +milligrams +milliliter +milliliters +millilitre +millilitres +millimeter +millimeters +millimetre +millimetres +milliner +milliners +millinery +milling +millings +million +millionaire +millionaires +millionnaire +millionnaires +millions +millionth +millionths +millipede +millipedes +millisecond +milliseconds +millpond +millponds +millrace +millraces +mills +millstone +millstones +millstream +millstreams +millwright +millwrights +milquetoast +milquetoasts +mils +milt +milted +milting +milts +mime +mimed +mimeograph +mimeographed +mimeographing +mimeographs +mimes +mimetic +mimic +mimicked +mimicker +mimickers +mimicking +mimicries +mimicry +mimics +miming +mimosa +mimosas +minaret +minarets +minatory +mince +minced +mincemeat +mincer +mincers +minces +mincing +mind +minded +mindful +mindfully +mindfulness +minding +mindless +mindlessly +mindlessness +minds +mindset +mindsets +mine +mined +minefield +minefields +miner +mineral +mineralogical +mineralogist +mineralogists +mineralogy +minerals +miners +mines +minestrone +minesweeper +minesweepers +mingle +mingled +mingles +mingling +mini +miniature +miniatures +miniaturist +miniaturists +miniaturization +miniaturize +miniaturized +miniaturizes +miniaturizing +minibike +minibikes +minibus +minibuses +minibusses +minicam +minicams +minicomputer +minicomputers +minim +minima +minimal +minimalism +minimalist +minimalists +minimally +minimization +minimize +minimized +minimizes +minimizing +minims +minimum +minimums +mining +minion +minions +minis +miniscule +miniscules +miniseries +miniskirt +miniskirts +minister +ministered +ministerial +ministering +ministers +ministrant +ministrants +ministration +ministrations +ministries +ministry +minivan +minivans +mink +minks +minnesinger +minnesingers +minnow +minnows +minor +minored +minoring +minorities +minority +minors +minoxidil +minster +minsters +minstrel +minstrels +minstrelsy +mint +mintage +minted +minter +minters +mintier +mintiest +minting +mints +minty +minuend +minuends +minuet +minuets +minus +minuscule +minuscules +minuses +minute +minuted +minutely +minuteman +minutemen +minuteness +minuter +minutes +minutest +minutia +minutiae +minuting +minx +minxes +miracle +miracles +miraculous +miraculously +mirage +mirages +mire +mired +mires +mirier +miriest +miring +mirror +mirrored +mirroring +mirrors +mirth +mirthful +mirthfully +mirthfulness +mirthless +mirthlessly +miry +misaddress +misaddressed +misaddresses +misaddressing +misadventure +misadventures +misaligned +misalignment +misalliance +misalliances +misanthrope +misanthropes +misanthropic +misanthropically +misanthropist +misanthropists +misanthropy +misapplication +misapplied +misapplies +misapply +misapplying +misapprehend +misapprehended +misapprehending +misapprehends +misapprehension +misapprehensions +misappropriate +misappropriated +misappropriates +misappropriating +misappropriation +misappropriations +misbegotten +misbehave +misbehaved +misbehaves +misbehaving +misbehavior +miscalculate +miscalculated +miscalculates +miscalculating +miscalculation +miscalculations +miscall +miscalled +miscalling +miscalls +miscarriage +miscarriages +miscarried +miscarries +miscarry +miscarrying +miscast +miscasting +miscasts +miscegenation +miscellaneous +miscellaneously +miscellanies +miscellany +mischance +mischances +mischief +mischievous +mischievously +mischievousness +miscibility +miscible +misconceive +misconceived +misconceives +misconceiving +misconception +misconceptions +misconduct +misconducted +misconducting +misconducts +misconstruction +misconstructions +misconstrue +misconstrued +misconstrues +misconstruing +miscount +miscounted +miscounting +miscounts +miscreant +miscreants +miscue +miscued +miscues +miscuing +misdeal +misdealing +misdeals +misdealt +misdeed +misdeeds +misdemeanor +misdemeanors +misdemeanour +misdemeanours +misdiagnose +misdiagnosed +misdiagnoses +misdiagnosing +misdiagnosis +misdid +misdirect +misdirected +misdirecting +misdirection +misdirects +misdo +misdoes +misdoing +misdoings +misdone +miser +miserable +miserableness +miserably +miseries +miserliness +miserly +misers +misery +misfeasance +misfile +misfiled +misfiles +misfiling +misfire +misfired +misfires +misfiring +misfit +misfits +misfitted +misfitting +misfortune +misfortunes +misgiving +misgivings +misgovern +misgoverned +misgoverning +misgovernment +misgoverns +misguidance +misguide +misguided +misguidedly +misguides +misguiding +mishandle +mishandled +mishandles +mishandling +mishap +mishaps +mishear +misheard +mishearing +mishears +mishmash +mishmashes +misidentified +misidentifies +misidentify +misidentifying +misinform +misinformation +misinformed +misinforming +misinforms +misinterpret +misinterpretation +misinterpretations +misinterpreted +misinterpreting +misinterprets +misjudge +misjudged +misjudges +misjudging +misjudgment +misjudgments +mislabel +mislabeled +mislabeling +mislabelled +mislabelling +mislabels +mislaid +mislay +mislaying +mislays +mislead +misleading +misleadingly +misleads +misled +mismanage +mismanaged +mismanagement +mismanages +mismanaging +mismatch +mismatched +mismatches +mismatching +misname +misnamed +misnames +misnaming +misnomer +misnomers +misogamist +misogamists +misogamy +misogynist +misogynistic +misogynists +misogynous +misogyny +misplace +misplaced +misplacement +misplaces +misplacing +misplay +misplayed +misplaying +misplays +misprint +misprinted +misprinting +misprints +misprision +mispronounce +mispronounced +mispronounces +mispronouncing +mispronunciation +mispronunciations +misquotation +misquotations +misquote +misquoted +misquotes +misquoting +misread +misreading +misreadings +misreads +misreport +misreported +misreporting +misreports +misrepresent +misrepresentation +misrepresentations +misrepresented +misrepresenting +misrepresents +misrule +misruled +misrules +misruling +miss +missal +missals +missed +misses +misshape +misshaped +misshapen +misshapes +misshaping +missile +missilery +missiles +missilry +missing +mission +missionaries +missionary +missioner +missioners +missions +missis +missises +missive +missives +misspeak +misspeaking +misspeaks +misspell +misspelled +misspelling +misspellings +misspells +misspelt +misspend +misspending +misspends +misspent +misspoke +misspoken +misstate +misstated +misstatement +misstatements +misstates +misstating +misstep +missteps +missus +missuses +mist +mistakable +mistake +mistaken +mistakenly +mistakes +mistaking +misted +mister +misters +mistier +mistiest +mistily +mistime +mistimed +mistimes +mistiming +mistiness +misting +mistletoe +mistook +mistral +mistrals +mistreat +mistreated +mistreating +mistreatment +mistreats +mistress +mistresses +mistrial +mistrials +mistrust +mistrusted +mistrustful +mistrustfully +mistrusting +mistrusts +mists +misty +misunderstand +misunderstanding +misunderstandings +misunderstands +misunderstood +misuse +misused +misuses +misusing +mite +miter +mitered +mitering +miters +mites +mitigate +mitigated +mitigates +mitigating +mitigation +mitosis +mitotic +mitre +mitred +mitres +mitring +mitt +mitten +mittens +mitts +mixable +mixed +mixer +mixers +mixes +mixing +mixt +mixture +mixtures +mizzen +mizzenmast +mizzenmasts +mizzens +mnemonic +mnemonically +mnemonics +moan +moaned +moaner +moaners +moaning +moans +moat +moats +mobbed +mobbing +mobile +mobiles +mobility +mobilization +mobilizations +mobilize +mobilized +mobilizer +mobilizers +mobilizes +mobilizing +mobs +mobster +mobsters +moccasin +moccasins +mocha +mochas +mock +mocked +mocker +mockeries +mockers +mockery +mocking +mockingbird +mockingbirds +mockingly +mocks +mockup +mockups +modal +modals +mode +model +modeled +modeler +modelers +modeling +modelled +modelling +models +modem +modems +moderate +moderated +moderately +moderateness +moderates +moderating +moderation +moderator +moderators +modern +modernism +modernist +modernistic +modernists +modernity +modernization +modernize +modernized +modernizer +modernizers +modernizes +modernizing +modernly +modernness +moderns +modes +modest +modestly +modesty +modicum +modicums +modification +modifications +modified +modifier +modifiers +modifies +modify +modifying +modish +modishly +modishness +mods +modular +modulate +modulated +modulates +modulating +modulation +modulations +modulator +modulators +module +modules +mogul +moguls +mohair +moieties +moiety +moil +moiled +moiling +moils +moire +moires +moist +moisten +moistened +moistener +moisteners +moistening +moistens +moister +moistest +moistly +moistness +moisture +moisturize +moisturized +moisturizer +moisturizers +moisturizes +moisturizing +molar +molars +molasses +mold +moldboard +moldboards +molded +molder +moldered +moldering +molders +moldier +moldiest +moldiness +molding +moldings +molds +moldy +mole +molecular +molecularity +molecule +molecules +molehill +molehills +moles +moleskin +molest +molestation +molested +molester +molesters +molesting +molests +moll +mollies +mollification +mollified +mollifies +mollify +mollifying +molls +mollusc +molluscan +molluscans +molluscs +mollusk +molluskan +molluskans +mollusks +molly +mollycoddle +mollycoddled +mollycoddles +mollycoddling +molt +molted +molten +molter +molters +molting +molts +molybdenum +moment +momentarily +momentariness +momentary +momentous +momentously +momentousness +moments +momentum +momma +mommas +mommie +mommies +mommy +moms +monarch +monarchic +monarchical +monarchies +monarchism +monarchist +monarchistic +monarchists +monarchs +monarchy +monasteries +monastery +monastic +monastical +monastically +monasticism +monastics +monaural +monetarily +monetarism +monetarist +monetarists +monetary +monetize +monetized +monetizes +monetizing +money +moneybag +moneybags +moneyed +moneygrubber +moneygrubbers +moneygrubbing +moneylender +moneylenders +moneymaker +moneymakers +moneymaking +mongeese +monger +mongered +mongering +mongers +mongolism +mongoloid +mongoloids +mongoose +mongooses +mongrel +mongrels +monicker +monickers +monied +moniker +monikers +monism +monist +monists +monition +monitions +monitor +monitored +monitoring +monitors +monitory +monk +monkey +monkeyed +monkeying +monkeys +monkeyshine +monkeyshines +monkish +monks +monkshood +monkshoods +mono +monochromatic +monochrome +monochromes +monocle +monocled +monocles +monoclonal +monocotyledon +monocotyledonous +monocotyledons +monocular +monodic +monodies +monodist +monodists +monody +monogamist +monogamists +monogamous +monogamously +monogamy +monogram +monogrammed +monogramming +monograms +monograph +monographs +monolingual +monolinguals +monolith +monolithic +monoliths +monolog +monologist +monologists +monologs +monologue +monologues +monologuist +monologuists +monomania +monomaniac +monomaniacal +monomaniacs +monomer +monomers +mononucleosis +monophonic +monoplane +monoplanes +monopolies +monopolist +monopolistic +monopolists +monopolization +monopolize +monopolized +monopolizer +monopolizers +monopolizes +monopolizing +monopoly +monorail +monorails +monosyllabic +monosyllable +monosyllables +monotheism +monotheist +monotheistic +monotheists +monotone +monotones +monotonous +monotonously +monotonousness +monotony +monounsaturated +monoxide +monoxides +monseigneur +monsieur +monsignor +monsignori +monsignors +monsoon +monsoonal +monsoons +monster +monsters +monstrance +monstrances +monstrosities +monstrosity +monstrous +monstrously +montage +montages +month +monthlies +monthly +months +monument +monumental +monumentally +monuments +mooch +mooched +moocher +moochers +mooches +mooching +mood +moodier +moodiest +moodily +moodiness +moods +moody +mooed +mooing +moon +moonbeam +moonbeams +mooned +mooning +moonless +moonlight +moonlighted +moonlighter +moonlighters +moonlighting +moonlights +moonlit +moons +moonscape +moonscapes +moonshine +moonshiner +moonshiners +moonshot +moonshots +moonstone +moonstones +moonstruck +moonwalk +moonwalks +moor +moored +mooring +moorings +moorland +moors +moos +moose +moot +mooted +mooting +moots +mope +moped +mopeds +moper +mopers +mopes +mopey +mopier +mopiest +moping +mopish +mopped +moppet +moppets +mopping +mops +mopy +moraine +moraines +moral +morale +moralist +moralistic +moralistically +moralists +moralities +morality +moralization +moralize +moralized +moralizer +moralizers +moralizes +moralizing +morally +morals +morass +morasses +moratoria +moratorium +moratoriums +moray +morays +morbid +morbidity +morbidly +morbidness +mordancy +mordant +mordantly +mordants +more +morel +morels +moreover +mores +morgue +morgues +moribund +morn +morning +mornings +morns +morocco +moron +moronic +moronically +morons +morose +morosely +moroseness +morph +morphed +morpheme +morphemes +morphemic +morphia +morphine +morphing +morphological +morphology +morphs +morrow +morrows +morsel +morsels +mortal +mortality +mortally +mortals +mortar +mortarboard +mortarboards +mortared +mortaring +mortars +mortgage +mortgaged +mortgagee +mortgagees +mortgager +mortgagers +mortgages +mortgaging +mortgagor +mortgagors +mortice +morticed +mortices +mortician +morticians +morticing +mortification +mortified +mortifies +mortify +mortifying +mortise +mortised +mortises +mortising +mortuaries +mortuary +mosaic +mosaics +mosey +moseyed +moseying +moseys +mosh +moshed +moshes +moshing +mosque +mosques +mosquito +mosquitoes +mosquitos +moss +mossback +mossbacks +mosses +mossier +mossiest +mossy +most +mostly +mote +motel +motels +motes +motet +motets +moth +mothball +mothballed +mothballing +mothballs +mother +motherboard +motherboards +mothered +motherfucker +motherfuckers +motherfucking +motherhood +mothering +motherland +motherlands +motherless +motherliness +motherly +mothers +moths +motif +motifs +motile +motility +motion +motioned +motioning +motionless +motionlessly +motionlessness +motions +motivate +motivated +motivates +motivating +motivation +motivational +motivations +motivator +motivators +motive +motiveless +motives +motley +motlier +motliest +motocross +motocrosses +motor +motorbike +motorbiked +motorbikes +motorbiking +motorboat +motorboated +motorboating +motorboats +motorcade +motorcades +motorcar +motorcars +motorcycle +motorcycled +motorcycles +motorcycling +motorcyclist +motorcyclists +motored +motoring +motorist +motorists +motorization +motorize +motorized +motorizes +motorizing +motorman +motormen +motormouth +motormouths +motors +mots +mottle +mottled +mottles +mottling +motto +mottoes +mottos +moue +moues +mould +moulded +moulder +mouldered +mouldering +moulders +mouldier +mouldiest +moulding +mouldings +moulds +mouldy +moult +moulted +moulting +moults +mound +mounded +mounding +mounds +mount +mountable +mountain +mountaineer +mountaineered +mountaineering +mountaineers +mountainous +mountains +mountainside +mountainsides +mountaintop +mountaintops +mountebank +mountebanks +mounted +mounter +mounters +mounting +mountings +mounts +mourn +mourned +mourner +mourners +mournful +mournfully +mournfulness +mourning +mourns +mouse +moused +mouser +mousers +mouses +mousetrap +mousetrapped +mousetrapping +mousetraps +mousey +mousier +mousiest +mousiness +mousing +mousse +moussed +mousses +moussing +moustache +moustaches +mousy +mouth +mouthed +mouthful +mouthfuls +mouthier +mouthiest +mouthiness +mouthing +mouthpiece +mouthpieces +mouths +mouthwash +mouthwashes +mouthwatering +mouthy +mouton +movable +movables +move +moveable +moveables +moved +movement +movements +mover +movers +moves +movie +moviegoer +moviegoers +movies +moving +movingly +mowed +mower +mowers +mowing +mown +mows +moxie +mozzarella +much +mucilage +mucilaginous +muck +mucked +muckier +muckiest +mucking +muckrake +muckraked +muckraker +muckrakers +muckrakes +muckraking +mucks +mucky +mucous +mucus +muddied +muddier +muddies +muddiest +muddily +muddiness +muddle +muddled +muddleheaded +muddles +muddling +muddy +muddying +mudflat +mudflats +mudguard +mudguards +mudroom +mudrooms +mudslide +mudslides +mudslinger +mudslingers +mudslinging +muenster +muezzin +muezzins +muff +muffed +muffin +muffing +muffins +muffle +muffled +muffler +mufflers +muffles +muffling +muffs +mufti +muftis +mugful +mugfuls +mugged +mugger +muggers +muggier +muggiest +mugginess +mugging +muggings +muggy +mugs +mugshot +mugshots +mugwump +mugwumps +mujahedin +mukluk +mukluks +mulatto +mulattoes +mulattos +mulberries +mulberry +mulch +mulched +mulches +mulching +mulct +mulcted +mulcting +mulcts +mule +mules +muleskinner +muleskinners +muleteer +muleteers +mulish +mulishly +mulishness +mull +mullah +mullahs +mulled +mullein +mullet +mullets +mulligan +mulligans +mulligatawny +mulling +mullion +mullioned +mullions +mulls +multicolored +multicultural +multiculturalism +multidimensional +multidisciplinary +multifaceted +multifamily +multifarious +multifariously +multifariousness +multiform +multilateral +multilaterally +multilevel +multilingual +multilingualism +multimedia +multimillionaire +multimillionaires +multinational +multinationals +multiple +multiples +multiplex +multiplexed +multiplexer +multiplexers +multiplexes +multiplexing +multiplexor +multiplexors +multiplicand +multiplicands +multiplication +multiplications +multiplicities +multiplicity +multiplied +multiplier +multipliers +multiplies +multiply +multiplying +multiprocessor +multiprocessors +multipurpose +multiracial +multistage +multistory +multitasking +multitude +multitudes +multitudinous +multivitamin +multivitamins +mumble +mumbled +mumbler +mumblers +mumbles +mumbletypeg +mumbling +mummer +mummers +mummery +mummies +mummification +mummified +mummifies +mummify +mummifying +mummy +mumps +mums +munch +munched +munches +munchies +munching +munchkin +munchkins +mundane +mundanely +municipal +municipalities +municipality +municipally +municipals +munificence +munificent +munificently +munition +munitioned +munitioning +munitions +mural +muralist +muralists +murals +murder +murdered +murderer +murderers +murderess +murderesses +murdering +murderous +murderously +murders +murk +murkier +murkiest +murkily +murkiness +murky +murmur +murmured +murmurer +murmurers +murmuring +murmurings +murmurous +murmurs +murrain +muscat +muscatel +muscatels +muscats +muscle +musclebound +muscled +muscles +muscling +muscular +muscularity +muscularly +musculature +muse +mused +muses +musette +musettes +museum +museums +mush +mushed +mushes +mushier +mushiest +mushiness +mushing +mushroom +mushroomed +mushrooming +mushrooms +mushy +music +musical +musicale +musicales +musicality +musically +musicals +musician +musicianly +musicians +musicianship +musicological +musicologist +musicologists +musicology +musing +musingly +musings +musk +muskeg +muskegs +muskellunge +muskellunges +musket +musketeer +musketeers +musketry +muskets +muskie +muskier +muskies +muskiest +muskiness +muskmelon +muskmelons +muskox +muskoxen +muskrat +muskrats +musky +muslin +muss +mussed +mussel +mussels +musses +mussier +mussiest +mussing +mussy +must +mustache +mustached +mustaches +mustachio +mustachios +mustang +mustangs +mustard +muster +mustered +mustering +musters +mustier +mustiest +mustily +mustiness +musts +musty +mutability +mutable +mutably +mutagen +mutagens +mutant +mutants +mutate +mutated +mutates +mutating +mutation +mutational +mutations +mutative +mute +muted +mutely +muteness +muter +mutes +mutest +mutilate +mutilated +mutilates +mutilating +mutilation +mutilations +mutilator +mutilators +mutineer +mutineers +muting +mutinied +mutinies +mutinous +mutinously +mutiny +mutinying +mutt +mutter +muttered +mutterer +mutterers +muttering +mutterings +mutters +mutton +muttonchops +muttony +mutts +mutual +mutuality +mutually +muumuu +muumuus +muzzle +muzzled +muzzles +muzzling +mycologist +mycologists +mycology +myelitis +myna +mynah +mynahs +mynas +myopia +myopic +myopically +myriad +myriads +myrmidon +myrmidons +myrrh +myrtle +myrtles +myself +mysteries +mysterious +mysteriously +mysteriousness +mystery +mystic +mystical +mystically +mysticism +mystics +mystification +mystified +mystifies +mystify +mystifying +mystique +myth +mythic +mythical +mythological +mythologies +mythologist +mythologists +mythologize +mythologized +mythologizes +mythologizing +mythology +myths +nabbed +nabbing +nabob +nabobs +nabs +nacelle +nacelles +nacho +nachos +nacre +nacreous +nadir +nadirs +nagged +nagger +naggers +nagging +nags +naiad +naiades +naiads +naif +naifs +nail +nailbrush +nailbrushes +nailed +nailing +nails +naive +naively +naiver +naivest +naivete +naivety +naked +nakedly +nakedness +name +nameable +named +namedrop +namedropped +namedropping +namedrops +nameless +namelessly +namely +nameplate +nameplates +names +namesake +namesakes +naming +nannies +nanny +nanosecond +nanoseconds +napalm +napalmed +napalming +napalms +nape +napes +naphtha +naphthalene +napkin +napkins +napless +napoleon +napoleons +napped +napper +nappers +nappier +nappies +nappiest +napping +nappy +naps +narc +narcissi +narcissism +narcissist +narcissistic +narcissists +narcissus +narcissuses +narcolepsy +narcosis +narcotic +narcotics +narcotization +narcotize +narcotized +narcotizes +narcotizing +narcs +nark +narks +narrate +narrated +narrates +narrating +narration +narrations +narrative +narratives +narrator +narrators +narrow +narrowed +narrower +narrowest +narrowing +narrowly +narrowness +narrows +narwhal +narwhals +nary +nasal +nasality +nasalization +nasalize +nasalized +nasalizes +nasalizing +nasally +nasals +nascence +nascent +nastier +nastiest +nastily +nastiness +nasturtium +nasturtiums +nasty +natal +natch +nation +national +nationalism +nationalist +nationalistic +nationalistically +nationalists +nationalities +nationality +nationalization +nationalizations +nationalize +nationalized +nationalizes +nationalizing +nationally +nationals +nationhood +nations +nationwide +native +natives +nativities +nativity +natter +nattered +nattering +natters +nattier +nattiest +nattily +nattiness +natty +natural +naturalism +naturalist +naturalistic +naturalists +naturalization +naturalize +naturalized +naturalizes +naturalizing +naturally +naturalness +naturals +nature +natures +naught +naughtier +naughtiest +naughtily +naughtiness +naughts +naughty +nausea +nauseate +nauseated +nauseates +nauseating +nauseatingly +nauseous +nauseously +nauseousness +nautical +nautically +nautili +nautilus +nautiluses +naval +nave +navel +navels +naves +navies +navigability +navigable +navigate +navigated +navigates +navigating +navigation +navigational +navigator +navigators +navy +nays +naysayer +naysayers +neanderthal +neanderthals +neap +neaps +near +nearby +neared +nearer +nearest +nearing +nearly +nearness +nears +nearsighted +nearsightedly +nearsightedness +neat +neaten +neatened +neatening +neatens +neater +neatest +neath +neatly +neatness +nebula +nebulae +nebular +nebulas +nebulous +nebulously +nebulousness +necessaries +necessarily +necessary +necessitate +necessitated +necessitates +necessitating +necessities +necessitous +necessity +neck +necked +neckerchief +neckerchiefs +neckerchieves +necking +necklace +necklaces +neckline +necklines +necks +necktie +neckties +necrology +necromancer +necromancers +necromancy +necropoleis +necropoles +necropoli +necropolis +necropolises +necrosis +necrotic +nectar +nectarine +nectarines +need +needed +needful +needfully +needier +neediest +neediness +needing +needle +needled +needlepoint +needles +needless +needlessly +needlessness +needlewoman +needlewomen +needlework +needling +needs +needy +nefarious +nefariously +nefariousness +negate +negated +negates +negating +negation +negations +negative +negatived +negatively +negativeness +negatives +negativing +negativism +negativity +neglect +neglected +neglectful +neglectfully +neglectfulness +neglecting +neglects +neglige +negligee +negligees +negligence +negligent +negligently +negliges +negligible +negligibly +negotiability +negotiable +negotiate +negotiated +negotiates +negotiating +negotiation +negotiations +negotiator +negotiators +negritude +neigh +neighbor +neighbored +neighborhood +neighborhoods +neighboring +neighborliness +neighborly +neighbors +neighbour +neighboured +neighbouring +neighbours +neighed +neighing +neighs +neither +nelson +nelsons +nematode +nematodes +nemeses +nemesis +neoclassic +neoclassical +neoclassicism +neocolonialism +neocolonialist +neocolonialists +neoconservative +neoconservatives +neodymium +neolithic +neologism +neologisms +neon +neonatal +neonate +neonates +neophyte +neophytes +neoplasm +neoplasms +neoplastic +neoprene +nepenthe +nephew +nephews +nephrite +nephritic +nephritis +nepotism +nepotist +nepotists +neptunium +nerd +nerdier +nerdiest +nerds +nerdy +nerve +nerved +nerveless +nervelessly +nervelessness +nerves +nervier +nerviest +nerviness +nerving +nervous +nervously +nervousness +nervy +nest +nested +nesting +nestle +nestled +nestles +nestling +nestlings +nests +nether +nethermost +netherworld +nets +nett +netted +netting +nettle +nettled +nettles +nettlesome +nettling +netts +network +networked +networking +networks +neural +neuralgia +neuralgic +neurally +neurasthenia +neurasthenic +neurasthenics +neuritic +neuritics +neuritis +neurological +neurologically +neurologist +neurologists +neurology +neuron +neuronal +neurons +neuroses +neurosis +neurosurgeon +neurosurgeons +neurosurgery +neurotic +neurotically +neurotics +neurotransmitter +neurotransmitters +neuter +neutered +neutering +neuters +neutral +neutralism +neutralist +neutralists +neutrality +neutralization +neutralize +neutralized +neutralizer +neutralizers +neutralizes +neutralizing +neutrally +neutrals +neutrino +neutrinos +neutron +neutrons +never +nevermore +nevertheless +nevi +nevus +newbie +newbies +newborn +newborns +newcomer +newcomers +newel +newels +newer +newest +newfangled +newly +newlywed +newlyweds +newness +news +newsboy +newsboys +newscast +newscaster +newscasters +newscasts +newsdealer +newsdealers +newsgirl +newsgirls +newsgroup +newsgroups +newsier +newsiest +newsletter +newsletters +newsman +newsmen +newspaper +newspaperman +newspapermen +newspapers +newspaperwoman +newspaperwomen +newsprint +newsreel +newsreels +newsroom +newsrooms +newsstand +newsstands +newsweeklies +newsweekly +newswoman +newswomen +newsworthiness +newsworthy +newsy +newt +newton +newtons +newts +next +nexus +nexuses +niacin +nibble +nibbled +nibbler +nibblers +nibbles +nibbling +nibs +nice +nicely +niceness +nicer +nicest +niceties +nicety +niche +niches +nick +nicked +nickel +nickelodeon +nickelodeons +nickels +nicker +nickered +nickering +nickers +nicking +nicknack +nicknacks +nickname +nicknamed +nicknames +nicknaming +nicks +nicotine +niece +nieces +niftier +niftiest +nifty +niggard +niggardliness +niggardly +niggards +nigger +niggers +niggle +niggled +niggler +nigglers +niggles +niggling +nigh +nigher +nighest +night +nightcap +nightcaps +nightclothes +nightclub +nightclubbed +nightclubbing +nightclubs +nightdress +nightdresses +nightfall +nightgown +nightgowns +nighthawk +nighthawks +nightie +nighties +nightingale +nightingales +nightlife +nightlong +nightly +nightmare +nightmares +nightmarish +nights +nightshade +nightshades +nightshirt +nightshirts +nightspot +nightspots +nightstand +nightstands +nightstick +nightsticks +nighttime +nightwear +nighty +nihilism +nihilist +nihilistic +nihilists +nimbi +nimble +nimbleness +nimbler +nimblest +nimbly +nimbus +nimbuses +nimrod +nimrods +nincompoop +nincompoops +nine +ninepin +ninepins +nines +nineteen +nineteens +nineteenth +nineteenths +nineties +ninetieth +ninetieths +ninety +ninja +ninjas +ninnies +ninny +ninth +ninths +niobium +nipped +nipper +nippers +nippier +nippiest +nippiness +nipping +nipple +nipples +nippy +nips +nirvana +nisei +niseis +nite +niter +nites +nitpick +nitpicked +nitpicker +nitpickers +nitpicking +nitpicks +nitrate +nitrated +nitrates +nitrating +nitration +nitre +nitrification +nitrite +nitrites +nitrocellulose +nitrogen +nitrogenous +nitroglycerin +nitroglycerine +nits +nitwit +nitwits +nixed +nixes +nixing +nobelium +nobility +noble +nobleman +noblemen +nobleness +nobler +nobles +noblest +noblewoman +noblewomen +nobly +nobodies +nobody +nocturnal +nocturnally +nocturne +nocturnes +nodal +nodded +nodding +noddle +noddles +node +nodes +nods +nodular +nodule +nodules +noel +noels +noes +noggin +noggins +nohow +noise +noised +noiseless +noiselessly +noiselessness +noisemaker +noisemakers +noises +noisier +noisiest +noisily +noisiness +noising +noisome +noisy +nomad +nomadic +nomads +nomenclature +nomenclatures +nominal +nominally +nominate +nominated +nominates +nominating +nomination +nominations +nominative +nominatives +nominator +nominators +nominee +nominees +nonabrasive +nonabsorbent +nonabsorbents +nonacademic +nonacceptance +nonacid +nonactive +nonactives +nonaddictive +nonadhesive +nonadjacent +nonadjustable +nonadministrative +nonage +nonagenarian +nonagenarians +nonages +nonaggression +nonalcoholic +nonaligned +nonalignment +nonallergic +nonappearance +nonappearances +nonassignable +nonathletic +nonattendance +nonautomotive +nonavailability +nonbasic +nonbeliever +nonbelievers +nonbelligerent +nonbelligerents +nonbinding +nonbreakable +nonburnable +noncaloric +noncancerous +nonce +nonchalance +nonchalant +nonchalantly +nonchargeable +nonclerical +nonclericals +nonclinical +noncollectable +noncom +noncombat +noncombatant +noncombatants +noncombustible +noncommercial +noncommercials +noncommittal +noncommittally +noncommunicable +noncompeting +noncompetitive +noncompliance +noncomplying +noncomprehending +noncoms +nonconducting +nonconductor +nonconductors +nonconforming +nonconformist +nonconformists +nonconformity +nonconsecutive +nonconstructive +noncontagious +noncontinuous +noncontributing +noncontributory +noncontroversial +nonconvertible +noncooperation +noncorroding +noncorrosive +noncredit +noncriminal +noncriminals +noncritical +noncrystalline +noncumulative +noncustodial +nondairy +nondeductible +nondeliveries +nondelivery +nondemocratic +nondenominational +nondepartmental +nondepreciating +nondescript +nondestructive +nondetachable +nondisciplinary +nondisclosure +nondiscrimination +nondiscriminatory +nondramatic +nondrinker +nondrinkers +nondrying +none +noneducational +noneffective +nonelastic +nonelectric +nonelectrical +nonenforceable +nonentities +nonentity +nonequivalent +nonequivalents +nonessential +nonesuch +nonesuches +nonetheless +nonevent +nonevents +nonexchangeable +nonexclusive +nonexempt +nonexempts +nonexistence +nonexistent +nonexplosive +nonexplosives +nonfactual +nonfading +nonfat +nonfatal +nonfattening +nonferrous +nonfiction +nonfictional +nonflammable +nonflowering +nonfluctuating +nonflying +nonfood +nonfoods +nonfreezing +nonfunctional +nongovernmental +nongranular +nonhazardous +nonhereditary +nonhuman +nonidentical +noninclusive +nonindependent +nonindustrial +noninfectious +noninflammatory +noninflationary +noninflected +nonintellectual +nonintellectuals +noninterchangeable +noninterference +nonintervention +nonintoxicating +noninvasive +nonirritating +nonjudgmental +nonjudicial +nonlegal +nonlethal +nonlinear +nonliterary +nonliving +nonmagnetic +nonmalignant +nonmember +nonmembers +nonmetal +nonmetallic +nonmetals +nonmigratory +nonmilitant +nonmilitary +nonnarcotic +nonnarcotics +nonnative +nonnatives +nonnegotiable +nonnuclear +nonnumerical +nonobjective +nonobligatory +nonobservance +nonobservant +nonoccupational +nonoccurrence +nonofficial +nonoperational +nonoperative +nonparallel +nonparallels +nonpareil +nonpareils +nonparticipant +nonparticipants +nonparticipating +nonpartisan +nonpartisans +nonpaying +nonpayment +nonpayments +nonperformance +nonperforming +nonperishable +nonperson +nonpersons +nonphysical +nonphysically +nonplus +nonplused +nonpluses +nonplusing +nonplussed +nonplusses +nonplussing +nonpoisonous +nonpolitical +nonpolluting +nonporous +nonpracticing +nonprejudicial +nonprescription +nonproductive +nonprofessional +nonprofessionals +nonprofit +nonprofitable +nonprofits +nonproliferation +nonpublic +nonpunishable +nonracial +nonradioactive +nonrandom +nonreactive +nonreciprocal +nonreciprocals +nonreciprocating +nonrecognition +nonrecoverable +nonrecurring +nonredeemable +nonrefillable +nonrefundable +nonreligious +nonrenewable +nonrepresentational +nonresident +nonresidential +nonresidents +nonresidual +nonresiduals +nonresistance +nonresistant +nonrestrictive +nonreturnable +nonreturnables +nonrhythmic +nonrigid +nonsalaried +nonscheduled +nonscientific +nonscoring +nonseasonal +nonsectarian +nonsecular +nonsegregated +nonsense +nonsensical +nonsensically +nonsensitive +nonsexist +nonsexual +nonskid +nonslip +nonsmoker +nonsmokers +nonsmoking +nonsocial +nonspeaking +nonspecialist +nonspecialists +nonspecializing +nonspecific +nonspiritual +nonspirituals +nonstaining +nonstandard +nonstarter +nonstarters +nonstick +nonstop +nonstrategic +nonstriking +nonstructural +nonsuccessive +nonsupport +nonsupporting +nonsurgical +nonsustaining +nonsympathizer +nonsympathizers +nontarnishable +nontaxable +nontechnical +nontenured +nontheatrical +nonthinking +nonthreatening +nontoxic +nontraditional +nontransferable +nontransparent +nontropical +nonuniform +nonunion +nonuser +nonusers +nonvenomous +nonverbal +nonviable +nonviolence +nonviolent +nonviolently +nonvirulent +nonvocal +nonvocational +nonvolatile +nonvoter +nonvoters +nonvoting +nonwhite +nonwhites +nonworking +nonyielding +noodle +noodled +noodles +noodling +nook +nooks +noon +noonday +noontide +noontime +noose +nooses +nope +norm +normal +normalcy +normality +normalization +normalize +normalized +normalizes +normalizing +normally +normative +norms +north +northbound +northeast +northeaster +northeasterly +northeastern +northeasters +northeastward +northeastwards +norther +northerlies +northerly +northern +northerner +northerners +northernmost +northers +northward +northwards +northwest +northwester +northwesterly +northwestern +northwesters +northwestward +northwestwards +nose +nosebleed +nosebleeds +nosecone +nosecones +nosed +nosedive +nosedived +nosedives +nosediving +nosedove +nosegay +nosegays +noses +nosey +nosh +noshed +nosher +noshers +noshes +noshing +nosier +nosiest +nosily +nosiness +nosing +nostalgia +nostalgic +nostalgically +nostril +nostrils +nostrum +nostrums +nosy +notabilities +notability +notable +notables +notably +notarial +notaries +notarization +notarize +notarized +notarizes +notarizing +notary +notate +notated +notates +notating +notation +notations +notch +notched +notches +notching +note +notebook +notebooks +noted +notepaper +notes +noteworthier +noteworthiest +noteworthiness +noteworthy +nothing +nothingness +nothings +notice +noticeable +noticeably +noticed +notices +noticing +notification +notifications +notified +notifier +notifiers +notifies +notify +notifying +noting +notion +notional +notions +notoriety +notorious +notoriously +notwithstanding +nougat +nougats +nought +noughts +noun +nouns +nourish +nourished +nourishes +nourishing +nourishment +nova +novae +novas +novel +novelette +novelettes +novelist +novelists +novelization +novelizations +novelize +novelized +novelizes +novelizing +novella +novellas +novelle +novels +novelties +novelty +novena +novenae +novenas +novice +novices +novitiate +novitiates +nowadays +noway +noways +nowhere +nowise +noxious +nozzle +nozzles +nuance +nuanced +nuances +nubbier +nubbiest +nubbin +nubbins +nubby +nubile +nubs +nuclear +nucleate +nucleated +nucleates +nucleating +nucleation +nuclei +nucleoli +nucleolus +nucleon +nucleons +nucleus +nucleuses +nude +nuder +nudes +nudest +nudge +nudged +nudges +nudging +nudism +nudist +nudists +nudity +nugatory +nugget +nuggets +nuisance +nuisances +nuke +nuked +nukes +nuking +null +nullification +nullified +nullifies +nullify +nullifying +nullity +numb +numbed +number +numbered +numbering +numberless +numbers +numbest +numbing +numbly +numbness +numbs +numbskull +numbskulls +numerable +numeracy +numeral +numerals +numerate +numerated +numerates +numerating +numeration +numerations +numerator +numerators +numeric +numerical +numerically +numerologist +numerologists +numerology +numerous +numerously +numinous +numismatic +numismatics +numismatist +numismatists +numskull +numskulls +nuncio +nuncios +nunneries +nunnery +nuns +nuptial +nuptials +nurse +nursed +nurseling +nurselings +nursemaid +nursemaids +nurser +nurseries +nursers +nursery +nurseryman +nurserymen +nurses +nursing +nursling +nurslings +nurture +nurtured +nurturer +nurturers +nurtures +nurturing +nutcracker +nutcrackers +nuthatch +nuthatches +nutmeat +nutmeats +nutmeg +nutmegs +nutpick +nutpicks +nutria +nutrias +nutrient +nutrients +nutriment +nutriments +nutrition +nutritional +nutritionally +nutritionist +nutritionists +nutritious +nutritiously +nutritiousness +nutritive +nuts +nutshell +nutshells +nutted +nuttier +nuttiest +nuttiness +nutting +nutty +nuzzle +nuzzled +nuzzler +nuzzlers +nuzzles +nuzzling +nylon +nylons +nymph +nymphet +nymphets +nymphomania +nymphomaniac +nymphomaniacs +nymphs +oafish +oafishly +oafishness +oafs +oaken +oaks +oakum +oared +oaring +oarlock +oarlocks +oars +oarsman +oarsmen +oarswoman +oarswomen +oases +oasis +oatcake +oatcakes +oaten +oath +oaths +oatmeal +oats +obbligati +obbligato +obbligatos +obduracy +obdurate +obdurately +obdurateness +obedience +obedient +obediently +obeisance +obeisances +obeisant +obelisk +obelisks +obese +obesity +obey +obeyed +obeying +obeys +obfuscate +obfuscated +obfuscates +obfuscating +obfuscation +obis +obit +obits +obituaries +obituary +object +objected +objectified +objectifies +objectify +objectifying +objecting +objection +objectionable +objectionably +objections +objective +objectively +objectiveness +objectives +objectivity +objector +objectors +objects +objurgate +objurgated +objurgates +objurgating +objurgation +objurgations +oblate +oblation +oblations +obligate +obligated +obligates +obligating +obligation +obligations +obligatorily +obligatory +oblige +obliged +obliges +obliging +obligingly +oblique +obliquely +obliqueness +obliques +obliquity +obliterate +obliterated +obliterates +obliterating +obliteration +oblivion +oblivious +obliviously +obliviousness +oblong +oblongs +obloquy +obnoxious +obnoxiously +obnoxiousness +oboe +oboes +oboist +oboists +obscene +obscenely +obscener +obscenest +obscenities +obscenity +obscurantism +obscurantist +obscurantists +obscure +obscured +obscurely +obscurer +obscures +obscurest +obscuring +obscurities +obscurity +obsequies +obsequious +obsequiously +obsequiousness +obsequy +observable +observably +observance +observances +observant +observantly +observation +observational +observations +observatories +observatory +observe +observed +observer +observers +observes +observing +obsess +obsessed +obsesses +obsessing +obsession +obsessional +obsessions +obsessive +obsessively +obsessiveness +obsessives +obsidian +obsolesce +obsolesced +obsolescence +obsolescent +obsolesces +obsolescing +obsolete +obsoleted +obsoletes +obsoleting +obstacle +obstacles +obstetric +obstetrical +obstetrician +obstetricians +obstetrics +obstinacy +obstinate +obstinately +obstreperous +obstreperously +obstreperousness +obstruct +obstructed +obstructing +obstruction +obstructionism +obstructionist +obstructionists +obstructions +obstructive +obstructively +obstructiveness +obstructs +obtain +obtainable +obtained +obtaining +obtainment +obtains +obtrude +obtruded +obtrudes +obtruding +obtrusion +obtrusive +obtrusively +obtrusiveness +obtuse +obtusely +obtuseness +obtuser +obtusest +obverse +obverses +obviate +obviated +obviates +obviating +obviation +obvious +obviously +obviousness +ocarina +ocarinas +occasion +occasional +occasionally +occasioned +occasioning +occasions +occidental +occidentals +occlude +occluded +occludes +occluding +occlusion +occlusions +occlusive +occult +occultism +occultist +occultists +occupancy +occupant +occupants +occupation +occupational +occupationally +occupations +occupied +occupier +occupiers +occupies +occupy +occupying +occur +occurred +occurrence +occurrences +occurring +occurs +ocean +oceanfront +oceanfronts +oceangoing +oceanic +oceanographer +oceanographers +oceanographic +oceanography +oceanology +oceans +ocelot +ocelots +ocher +ochre +octagon +octagonal +octagons +octane +octave +octaves +octavo +octavos +octet +octets +octette +octettes +octogenarian +octogenarians +octopi +octopus +octopuses +ocular +oculars +oculist +oculists +odalisque +odalisques +oddball +oddballs +odder +oddest +oddities +oddity +oddly +oddment +oddments +oddness +odds +odes +odious +odiously +odiousness +odium +odometer +odometers +odor +odored +odoriferous +odorless +odorous +odors +odour +odours +odyssey +odysseys +oedipal +oenology +oenophile +oenophiles +oesophagi +oesophagus +oeuvre +oeuvres +offal +offbeat +offbeats +offed +offence +offences +offend +offended +offender +offenders +offending +offends +offense +offenses +offensive +offensively +offensiveness +offensives +offer +offered +offering +offerings +offers +offertories +offertory +offhand +offhanded +offhandedly +offhandedness +office +officeholder +officeholders +officer +officers +offices +official +officialdom +officialism +officially +officials +officiant +officiants +officiate +officiated +officiates +officiating +officiator +officiators +officious +officiously +officiousness +offing +offings +offish +offline +offload +offloaded +offloading +offloads +offprint +offprints +offs +offset +offsets +offsetting +offshoot +offshoots +offshore +offside +offspring +offsprings +offstage +offtrack +often +oftener +oftenest +oftentimes +ofttimes +ogle +ogled +ogler +oglers +ogles +ogling +ogre +ogreish +ogres +ogress +ogresses +ohmmeter +ohmmeters +ohms +oilcloth +oilcloths +oiled +oilier +oiliest +oiliness +oiling +oils +oilskin +oilskins +oily +oink +oinked +oinking +oinks +ointment +ointments +okapi +okapis +okay +okayed +okaying +okays +okra +okras +olden +older +oldest +oldie +oldies +oldish +oldness +oldster +oldsters +oleaginous +oleander +oleanders +oleo +oleomargarin +oleomargarine +oles +olfactories +olfactory +oligarch +oligarchic +oligarchical +oligarchies +oligarchs +oligarchy +oligopolies +oligopoly +olive +olives +ombudsman +ombudsmen +omega +omegas +omelet +omelets +omelette +omelettes +omen +omens +omicron +omicrons +ominous +ominously +ominousness +omission +omissions +omit +omits +omitted +omitting +omnibus +omnibuses +omnibusses +omnipotence +omnipotent +omnipresence +omnipresent +omniscience +omniscient +omnivore +omnivores +omnivorous +omnivorously +omnivorousness +once +oncogene +oncogenes +oncologist +oncologists +oncology +oncoming +oneness +onerous +onerously +onerousness +ones +oneself +onetime +ongoing +onion +onions +onionskin +online +onlooker +onlookers +onlooking +only +onomatopoeia +onomatopoeic +onomatopoetic +onrush +onrushes +onrushing +onscreen +onset +onsets +onshore +onside +onslaught +onslaughts +onstage +onto +ontogeny +ontological +ontology +onus +onuses +onward +onwards +onyx +onyxes +oodles +oohed +oohing +oohs +oops +ooze +oozed +oozes +oozier +ooziest +oozing +oozy +opacity +opal +opalescence +opalescent +opals +opaque +opaqued +opaquely +opaqueness +opaquer +opaques +opaquest +opaquing +oped +open +opened +opener +openers +openest +openhanded +openhandedness +openhearted +opening +openings +openly +openness +opens +openwork +opera +operable +operas +operate +operated +operates +operatic +operatically +operating +operation +operational +operationally +operations +operative +operatives +operator +operators +operetta +operettas +opes +ophthalmic +ophthalmologist +ophthalmologists +ophthalmology +opiate +opiates +opine +opined +opines +oping +opining +opinion +opinionated +opinions +opium +opossum +opossums +opponent +opponents +opportune +opportunely +opportunism +opportunist +opportunistic +opportunistically +opportunists +opportunities +opportunity +oppose +opposed +opposes +opposing +opposite +oppositely +opposites +opposition +oppress +oppressed +oppresses +oppressing +oppression +oppressive +oppressively +oppressiveness +oppressor +oppressors +opprobrious +opprobriously +opprobrium +opted +optic +optical +optically +optician +opticians +optics +optima +optimal +optimally +optimism +optimist +optimistic +optimistically +optimists +optimization +optimize +optimized +optimizes +optimizing +optimum +optimums +opting +option +optional +optionally +optioned +optioning +options +optometrist +optometrists +optometry +opts +opulence +opulent +opulently +opus +opuses +oracle +oracles +oracular +oral +orally +orals +orange +orangeade +orangeades +oranger +orangeries +orangery +oranges +orangest +orangutan +orangutang +orangutangs +orangutans +orate +orated +orates +orating +oration +orations +orator +oratorical +oratorically +oratories +oratorio +oratorios +orators +oratory +orbicular +orbit +orbital +orbitals +orbited +orbiter +orbiters +orbiting +orbits +orbs +orchard +orchards +orchestra +orchestral +orchestras +orchestrate +orchestrated +orchestrates +orchestrating +orchestration +orchestrations +orchid +orchids +ordain +ordained +ordaining +ordainment +ordains +ordeal +ordeals +order +ordered +ordering +orderlies +orderliness +orderly +orders +ordinal +ordinals +ordinance +ordinances +ordinarily +ordinariness +ordinary +ordinate +ordinates +ordination +ordinations +ordnance +ordure +oregano +ores +organ +organdie +organdy +organelle +organelles +organic +organically +organics +organism +organismic +organisms +organist +organists +organization +organizational +organizationally +organizations +organize +organized +organizer +organizers +organizes +organizing +organs +organza +orgasm +orgasmic +orgasms +orgiastic +orgies +orgy +oriel +oriels +orient +oriental +orientals +orientate +orientated +orientates +orientating +orientation +orientations +oriented +orienting +orients +orifice +orifices +origami +origin +original +originality +originally +originals +originate +originated +originates +originating +origination +originator +originators +origins +oriole +orioles +orison +orisons +ormolu +ornament +ornamental +ornamentation +ornamented +ornamenting +ornaments +ornate +ornately +ornateness +ornerier +orneriest +orneriness +ornery +ornithological +ornithologist +ornithologists +ornithology +orotund +orotundities +orotundity +orphan +orphanage +orphanages +orphaned +orphaning +orphans +orris +orrises +orthodontia +orthodontic +orthodontics +orthodontist +orthodontists +orthodox +orthodoxies +orthodoxy +orthographic +orthographically +orthographies +orthography +orthopaedic +orthopaedics +orthopaedist +orthopaedists +orthopedic +orthopedics +orthopedist +orthopedists +orzo +oscillate +oscillated +oscillates +oscillating +oscillation +oscillations +oscillator +oscillators +oscillatory +oscilloscope +oscilloscopes +osculate +osculated +osculates +osculating +osculation +osculations +osier +osiers +osmium +osmosis +osmotic +osprey +ospreys +ossification +ossified +ossifies +ossify +ossifying +ostensible +ostensibly +ostentation +ostentatious +ostentatiously +osteoarthritis +osteopath +osteopathic +osteopaths +osteopathy +osteoporosis +ostracism +ostracize +ostracized +ostracizes +ostracizing +ostrich +ostriches +other +others +otherwise +otherworldly +otiose +otter +otters +ottoman +ottomans +oubliette +oubliettes +ouch +ought +ounce +ounces +ours +ourselves +oust +ousted +ouster +ousters +ousting +ousts +outage +outages +outargue +outargued +outargues +outarguing +outback +outbacks +outbalance +outbalanced +outbalances +outbalancing +outbid +outbidding +outbids +outboard +outboards +outboast +outboasted +outboasting +outboasts +outbound +outbox +outboxed +outboxes +outboxing +outbreak +outbreaks +outbuilding +outbuildings +outburst +outbursts +outcast +outcasts +outclass +outclassed +outclasses +outclassing +outcome +outcomes +outcries +outcrop +outcropped +outcropping +outcroppings +outcrops +outcry +outdated +outdid +outdistance +outdistanced +outdistances +outdistancing +outdo +outdoes +outdoing +outdone +outdoor +outdoors +outdoorsy +outdraw +outdrawing +outdrawn +outdraws +outdrew +outed +outer +outermost +outerwear +outface +outfaced +outfaces +outfacing +outfield +outfielder +outfielders +outfields +outfight +outfighting +outfights +outfit +outfits +outfitted +outfitter +outfitters +outfitting +outflank +outflanked +outflanking +outflanks +outflow +outflows +outfought +outfox +outfoxed +outfoxes +outfoxing +outgo +outgoes +outgoing +outgrew +outgrow +outgrowing +outgrown +outgrows +outgrowth +outgrowths +outguess +outguessed +outguesses +outguessing +outgun +outgunned +outgunning +outguns +outhit +outhits +outhitting +outhouse +outhouses +outing +outings +outlaid +outlandish +outlandishly +outlandishness +outlast +outlasted +outlasting +outlasts +outlaw +outlawed +outlawing +outlaws +outlay +outlaying +outlays +outlet +outlets +outline +outlined +outlines +outlining +outlive +outlived +outlives +outliving +outlook +outlooks +outlying +outmaneuver +outmaneuvered +outmaneuvering +outmaneuvers +outmanoeuvre +outmanoeuvred +outmanoeuvres +outmanoeuvring +outmatch +outmatched +outmatches +outmatching +outmoded +outnumber +outnumbered +outnumbering +outnumbers +outpatient +outpatients +outperform +outperformed +outperforming +outperforms +outplace +outplaced +outplacement +outplaces +outplacing +outplay +outplayed +outplaying +outplays +outpoint +outpointed +outpointing +outpoints +outpost +outposts +outpouring +outpourings +outproduce +outproduced +outproduces +outproducing +output +outputs +outputted +outputting +outrace +outraced +outraces +outracing +outrage +outraged +outrageous +outrageously +outrages +outraging +outran +outrank +outranked +outranking +outranks +outre +outreach +outreached +outreaches +outreaching +outrider +outriders +outrigger +outriggers +outright +outrun +outrunning +outruns +outs +outscore +outscored +outscores +outscoring +outsell +outselling +outsells +outset +outsets +outshine +outshined +outshines +outshining +outshone +outshout +outshouted +outshouting +outshouts +outside +outsider +outsiders +outsides +outsize +outsized +outsizes +outskirt +outskirts +outsmart +outsmarted +outsmarting +outsmarts +outsold +outsource +outsourced +outsources +outsourcing +outspend +outspending +outspends +outspent +outspoken +outspokenly +outspokenness +outspread +outspreading +outspreads +outstanding +outstandingly +outstation +outstations +outstay +outstayed +outstaying +outstays +outstretch +outstretched +outstretches +outstretching +outstrip +outstripped +outstripping +outstrips +outstript +outtake +outtakes +outvote +outvoted +outvotes +outvoting +outward +outwardly +outwards +outwear +outwearing +outwears +outweigh +outweighed +outweighing +outweighs +outwit +outwits +outwitted +outwitting +outwore +outwork +outworked +outworking +outworks +outworn +ouzo +oval +ovals +ovarian +ovaries +ovary +ovate +ovation +ovations +oven +ovenbird +ovenbirds +ovens +over +overabundance +overabundant +overachieve +overachieved +overachiever +overachievers +overachieves +overachieving +overact +overacted +overacting +overactive +overacts +overage +overages +overaggressive +overall +overalls +overambitious +overanxious +overarm +overarmed +overarming +overarms +overate +overattentive +overawe +overawed +overawes +overawing +overbalance +overbalanced +overbalances +overbalancing +overbear +overbearing +overbearingly +overbears +overbid +overbidding +overbids +overbite +overbites +overblown +overboard +overbold +overbook +overbooked +overbooking +overbooks +overbore +overborne +overbought +overbuild +overbuilding +overbuilds +overbuilt +overburden +overburdened +overburdening +overburdens +overbuy +overbuying +overbuys +overcame +overcapacity +overcapitalize +overcapitalized +overcapitalizes +overcapitalizing +overcareful +overcast +overcasting +overcasts +overcautious +overcharge +overcharged +overcharges +overcharging +overcloud +overclouded +overclouding +overclouds +overcoat +overcoats +overcome +overcomes +overcoming +overcompensate +overcompensated +overcompensates +overcompensating +overcompensation +overconfidence +overconfident +overconscientious +overcook +overcooked +overcooking +overcooks +overcritical +overcrowd +overcrowded +overcrowding +overcrowds +overdecorate +overdecorated +overdecorates +overdecorating +overdependent +overdevelop +overdeveloped +overdeveloping +overdevelops +overdid +overdo +overdoes +overdoing +overdone +overdose +overdosed +overdoses +overdosing +overdraft +overdrafts +overdraw +overdrawing +overdrawn +overdraws +overdress +overdressed +overdresses +overdressing +overdrew +overdrive +overdub +overdubbed +overdubbing +overdubs +overdue +overeager +overeat +overeaten +overeating +overeats +overemotional +overemphasis +overemphasize +overemphasized +overemphasizes +overemphasizing +overenthusiastic +overestimate +overestimated +overestimates +overestimating +overestimation +overexcite +overexcited +overexcites +overexciting +overexercise +overexercised +overexercises +overexercising +overexert +overexerted +overexerting +overexertion +overexerts +overexpose +overexposed +overexposes +overexposing +overexposure +overextend +overextended +overextending +overextends +overfed +overfeed +overfeeding +overfeeds +overfill +overfilled +overfilling +overfills +overflew +overflies +overflight +overflights +overflow +overflowed +overflowing +overflown +overflows +overfly +overflying +overfond +overfull +overgeneralize +overgeneralized +overgeneralizes +overgeneralizing +overgenerous +overgraze +overgrazed +overgrazes +overgrazing +overgrew +overgrow +overgrowing +overgrown +overgrows +overgrowth +overhand +overhanded +overhands +overhang +overhanging +overhangs +overhasty +overhaul +overhauled +overhauling +overhauls +overhead +overheads +overhear +overheard +overhearing +overhears +overheat +overheated +overheating +overheats +overhung +overindulge +overindulged +overindulgence +overindulgent +overindulges +overindulging +overjoy +overjoyed +overjoying +overjoys +overkill +overladen +overlaid +overlain +overland +overlap +overlapped +overlapping +overlaps +overlarge +overlay +overlaying +overlays +overleaf +overlie +overlies +overload +overloaded +overloading +overloads +overlong +overlook +overlooked +overlooking +overlooks +overlord +overlords +overly +overlying +overmaster +overmastered +overmastering +overmasters +overmodest +overmuch +overnice +overnight +overnights +overoptimism +overoptimistic +overpaid +overparticular +overpass +overpasses +overpay +overpaying +overpays +overplay +overplayed +overplaying +overplays +overpopulate +overpopulated +overpopulates +overpopulating +overpopulation +overpower +overpowered +overpowering +overpoweringly +overpowers +overpraise +overpraised +overpraises +overpraising +overprecise +overprice +overpriced +overprices +overpricing +overprint +overprinted +overprinting +overprints +overproduce +overproduced +overproduces +overproducing +overproduction +overprotect +overprotected +overprotecting +overprotective +overprotects +overqualified +overran +overrate +overrated +overrates +overrating +overreach +overreached +overreaches +overreaching +overreact +overreacted +overreacting +overreaction +overreactions +overreacts +overrefined +overridden +override +overrides +overriding +overripe +overrode +overrule +overruled +overrules +overruling +overrun +overrunning +overruns +overs +oversaw +oversea +overseas +oversee +overseeing +overseen +overseer +overseers +oversees +oversell +overselling +oversells +oversensitive +oversensitiveness +oversexed +overshadow +overshadowed +overshadowing +overshadows +overshoe +overshoes +overshoot +overshooting +overshoots +overshot +oversight +oversights +oversimple +oversimplification +oversimplifications +oversimplified +oversimplifies +oversimplify +oversimplifying +oversize +oversized +oversleep +oversleeping +oversleeps +overslept +oversold +overspecialization +overspecialize +overspecialized +overspecializes +overspecializing +overspend +overspending +overspends +overspent +overspill +overspilled +overspilling +overspills +overspilt +overspread +overspreading +overspreads +overstate +overstated +overstatement +overstatements +overstates +overstating +overstay +overstayed +overstaying +overstays +overstep +overstepped +overstepping +oversteps +overstimulate +overstimulated +overstimulates +overstimulating +overstock +overstocked +overstocking +overstocks +overstrict +overstrung +overstuffed +oversubscribe +oversubscribed +oversubscribes +oversubscribing +oversubtle +oversupplied +oversupplies +oversupply +oversupplying +oversuspicious +overt +overtake +overtaken +overtakes +overtaking +overtax +overtaxed +overtaxes +overtaxing +overthrew +overthrow +overthrowing +overthrown +overthrows +overtime +overtimes +overtire +overtired +overtires +overtiring +overtly +overtone +overtones +overtook +overture +overtures +overturn +overturned +overturning +overturns +overuse +overused +overuses +overusing +overvalue +overvalued +overvalues +overvaluing +overview +overviews +overweening +overweight +overwhelm +overwhelmed +overwhelming +overwhelmingly +overwhelms +overwinter +overwintered +overwintering +overwinters +overwork +overworked +overworking +overworks +overwrought +overzealous +oviduct +oviducts +oviparous +ovoid +ovoids +ovular +ovulate +ovulated +ovulates +ovulating +ovulation +ovule +ovules +ovum +owed +owes +owing +owlet +owlets +owlish +owlishly +owls +owned +owner +owners +ownership +owning +owns +oxblood +oxbow +oxbows +oxcart +oxcarts +oxen +oxford +oxfords +oxidant +oxidants +oxidation +oxide +oxides +oxidization +oxidize +oxidized +oxidizer +oxidizers +oxidizes +oxidizing +oxyacetylene +oxygen +oxygenate +oxygenated +oxygenates +oxygenating +oxygenation +oxymora +oxymoron +oxymorons +oyster +oysters +ozone +pablum +pabulum +pace +paced +pacemaker +pacemakers +pacer +pacers +paces +pacesetter +pacesetters +pachyderm +pachyderms +pachysandra +pachysandras +pacific +pacifically +pacification +pacified +pacifier +pacifiers +pacifies +pacifism +pacifist +pacifistic +pacifists +pacify +pacifying +pacing +pack +package +packaged +packager +packagers +packages +packaging +packed +packer +packers +packet +packets +packing +packinghouse +packinghouses +packs +packsaddle +packsaddles +pact +pacts +padded +paddies +padding +paddle +paddled +paddler +paddlers +paddles +paddling +paddock +paddocked +paddocking +paddocks +paddy +padlock +padlocked +padlocking +padlocks +padre +padres +pads +paean +paeans +paediatric +paediatrician +paediatricians +paediatrics +paella +pagan +paganism +pagans +page +pageant +pageantry +pageants +pageboy +pageboys +paged +pager +pagers +pages +paginate +paginated +paginates +paginating +pagination +paging +pagoda +pagodas +paid +pail +pailful +pailfuls +pails +pailsful +pain +pained +painful +painfuller +painfullest +painfully +painfulness +paining +painkiller +painkillers +painkilling +painless +painlessly +painlessness +pains +painstaking +painstakingly +paint +paintbox +paintboxes +paintbrush +paintbrushes +painted +painter +painters +painting +paintings +paints +pair +paired +pairing +pairs +paisley +paisleys +pajama +pajamas +palace +palaces +paladin +paladins +palanquin +palanquins +palatable +palatal +palatalization +palatalize +palatalized +palatalizes +palatalizing +palatals +palate +palates +palatial +palatially +palatinate +palatinates +palatine +palatines +palaver +palavered +palavering +palavers +pale +paled +paleface +palefaces +palely +paleness +paleographer +paleographers +paleography +paleolithic +paleontologist +paleontologists +paleontology +paler +pales +palest +palette +palettes +palfrey +palfreys +palimony +palimpsest +palimpsests +palindrome +palindromes +paling +palings +palisade +palisades +palish +pall +palladium +pallbearer +pallbearers +palled +pallet +pallets +palliate +palliated +palliates +palliating +palliation +palliative +palliatives +pallid +pallider +pallidest +pallidly +pallidness +palling +pallor +palls +palm +palmate +palmed +palmetto +palmettoes +palmettos +palmier +palmiest +palming +palmist +palmistry +palmists +palms +palmtop +palmtops +palmy +palomino +palominos +palpable +palpably +palpate +palpated +palpates +palpating +palpation +palpitate +palpitated +palpitates +palpitating +palpitation +palpitations +pals +palsied +palsies +palsy +palsying +paltrier +paltriest +paltriness +paltry +pampas +pamper +pampered +pampering +pampers +pamphlet +pamphleted +pamphleteer +pamphleteers +pamphleting +pamphlets +panacea +panaceas +panache +panama +panamas +pancake +pancaked +pancakes +pancaking +panchromatic +pancreas +pancreases +pancreatic +panda +pandas +pandemic +pandemics +pandemonium +pander +pandered +panderer +panderers +pandering +panders +pane +panegyric +panegyrics +panel +paneled +paneling +panelist +panelists +panelled +panelling +panels +panes +pang +pangs +panhandle +panhandled +panhandler +panhandlers +panhandles +panhandling +panic +panicked +panicking +panicky +panics +panier +paniers +panned +pannier +panniers +panning +panoplies +panoply +panorama +panoramas +panoramic +panpipes +pans +pansies +pansy +pant +pantaloons +panted +pantheism +pantheist +pantheistic +pantheists +pantheon +pantheons +panther +panthers +pantie +panties +panting +pantomime +pantomimed +pantomimes +pantomimic +pantomiming +pantomimist +pantomimists +pantries +pantry +pants +pantsuit +pantsuits +panty +pantyhose +pantyliner +pantyliners +pantywaist +pantywaists +papa +papacies +papacy +papal +paparazzi +papas +papaw +papaws +papaya +papayas +paper +paperback +paperbacks +paperboard +paperboy +paperboys +papered +paperer +paperers +papergirl +papergirls +paperhanger +paperhangers +paperhanging +papering +papers +paperweight +paperweights +paperwork +papery +papilla +papillae +papillary +papist +papists +papoose +papooses +pappies +pappy +paprika +paps +papyri +papyrus +papyruses +para +parable +parables +parabola +parabolas +parabolic +parachute +parachuted +parachutes +parachuting +parachutist +parachutists +parade +paraded +parader +paraders +parades +paradigm +paradigmatic +paradigms +parading +paradisaical +paradise +paradises +paradox +paradoxes +paradoxical +paradoxically +paraffin +paragon +paragons +paragraph +paragraphed +paragraphing +paragraphs +parakeet +parakeets +paralegal +paralegals +parallax +parallaxes +parallel +paralleled +paralleling +parallelism +parallelisms +parallelled +parallelling +parallelogram +parallelograms +parallels +paralyse +paralysed +paralyses +paralysing +paralysis +paralytic +paralytics +paralyze +paralyzed +paralyzes +paralyzing +paralyzingly +paramecia +paramecium +parameciums +paramedic +paramedical +paramedicals +paramedics +parameter +parameters +parametric +paramilitaries +paramilitary +paramount +paramour +paramours +paranoia +paranoiac +paranoiacs +paranoid +paranoids +paranormal +parapet +parapets +paraphernalia +paraphrase +paraphrased +paraphrases +paraphrasing +paraplegia +paraplegic +paraplegics +paraprofessional +paraprofessionals +parapsychologist +parapsychologists +parapsychology +paraquat +paras +parasite +parasites +parasitic +parasitically +parasitism +parasol +parasols +parasympathetic +parathion +parathyroid +parathyroids +paratrooper +paratroopers +paratroops +paratyphoid +parboil +parboiled +parboiling +parboils +parcel +parceled +parceling +parcelled +parcelling +parcels +parch +parched +parches +parching +parchment +parchments +pardon +pardonable +pardonably +pardoned +pardoner +pardoners +pardoning +pardons +pare +pared +paregoric +parent +parentage +parental +parented +parentheses +parenthesis +parenthesize +parenthesized +parenthesizes +parenthesizing +parenthetic +parenthetical +parenthetically +parenthood +parenting +parents +parer +parers +pares +paresis +parfait +parfaits +pariah +pariahs +parietal +parimutuel +parimutuels +paring +parings +parish +parishes +parishioner +parishioners +parity +park +parka +parkas +parked +parking +parks +parkway +parkways +parlance +parlay +parlayed +parlaying +parlays +parley +parleyed +parleying +parleys +parliament +parliamentarian +parliamentarians +parliamentary +parliaments +parlor +parlors +parlour +parlours +parlous +parmigiana +parmigiano +parochial +parochialism +parochially +parodied +parodies +parodist +parodists +parody +parodying +parole +paroled +parolee +parolees +paroles +paroling +paroxysm +paroxysmal +paroxysms +parquet +parqueted +parqueting +parquetry +parquets +parrakeet +parrakeets +parred +parricidal +parricide +parricides +parried +parries +parring +parrot +parroted +parroting +parrots +parry +parrying +pars +parse +parsec +parsecs +parsed +parses +parsimonious +parsimoniously +parsimony +parsing +parsley +parsnip +parsnips +parson +parsonage +parsonages +parsons +part +partake +partaken +partaker +partakers +partakes +partaking +parted +parterre +parterres +parthenogenesis +partial +partiality +partially +partials +participant +participants +participate +participated +participates +participating +participation +participator +participators +participatory +participial +participle +participles +particle +particleboard +particles +particular +particularities +particularity +particularization +particularize +particularized +particularizes +particularizing +particularly +particulars +particulate +particulates +partied +parties +parting +partings +partisan +partisans +partisanship +partition +partitioned +partitioning +partitions +partitive +partitives +partizan +partizans +partly +partner +partnered +partnering +partners +partnership +partnerships +partook +partridge +partridges +parts +parturition +partway +party +partying +parvenu +parvenus +pascal +pascals +paschal +pasha +pashas +pass +passable +passably +passage +passages +passageway +passageways +passbook +passbooks +passe +passed +passel +passels +passenger +passengers +passer +passerby +passers +passersby +passes +passim +passing +passingly +passion +passionate +passionately +passionflower +passionflowers +passionless +passions +passive +passively +passiveness +passives +passivity +passkey +passkeys +passport +passports +password +passwords +past +pasta +pastas +paste +pasteboard +pasted +pastel +pastels +pastern +pasterns +pastes +pasteurization +pasteurize +pasteurized +pasteurizer +pasteurizers +pasteurizes +pasteurizing +pastiche +pastiches +pastier +pasties +pastiest +pastille +pastilles +pastime +pastimes +pastiness +pasting +pastor +pastoral +pastorals +pastorate +pastorates +pastors +pastrami +pastries +pastry +pasts +pasturage +pasture +pastured +pastureland +pastures +pasturing +pasty +patch +patched +patches +patchier +patchiest +patchily +patchiness +patching +patchwork +patchworks +patchy +pate +patella +patellae +patellas +patent +patented +patenting +patently +patents +paterfamilias +paterfamiliases +paternal +paternalism +paternalistic +paternally +paternity +paternoster +paternosters +pates +path +pathetic +pathetically +pathfinder +pathfinders +pathless +pathogen +pathogenic +pathogens +pathological +pathologically +pathologies +pathologist +pathologists +pathology +pathos +paths +pathway +pathways +patience +patient +patienter +patientest +patiently +patients +patina +patinae +patinas +patio +patios +patois +patresfamilias +patriarch +patriarchal +patriarchate +patriarchates +patriarchies +patriarchs +patriarchy +patrician +patricians +patricide +patricides +patrimonial +patrimonies +patrimony +patriot +patriotic +patriotically +patriotism +patriots +patrol +patrolled +patrolling +patrolman +patrolmen +patrols +patrolwoman +patrolwomen +patron +patronage +patroness +patronesses +patronize +patronized +patronizer +patronizers +patronizes +patronizing +patronizingly +patrons +patronymic +patronymically +patronymics +patroon +patroons +pats +patsies +patsy +patted +patter +pattered +pattering +pattern +patterned +patterning +patterns +patters +patties +patting +patty +paucity +paunch +paunches +paunchier +paunchiest +paunchy +pauper +pauperism +pauperize +pauperized +pauperizes +pauperizing +paupers +pause +paused +pauses +pausing +pave +paved +pavement +pavements +paves +pavilion +pavilions +paving +pavings +pawed +pawing +pawl +pawls +pawn +pawnbroker +pawnbrokers +pawnbroking +pawned +pawning +pawns +pawnshop +pawnshops +pawpaw +pawpaws +paws +payable +payback +paybacks +paycheck +paychecks +payday +paydays +payed +payee +payees +payer +payers +paying +payload +payloads +paymaster +paymasters +payment +payments +payoff +payoffs +payola +payout +payouts +payroll +payrolls +pays +payslip +payslips +peace +peaceable +peaceably +peaceful +peacefully +peacefulness +peacekeeper +peacekeepers +peacekeeping +peacemaker +peacemakers +peacemaking +peaces +peacetime +peach +peaches +peachier +peachiest +peachy +peacock +peacocks +peafowl +peafowls +peahen +peahens +peak +peaked +peaking +peaks +peal +pealed +pealing +peals +peanut +peanuts +pear +pearl +pearled +pearlier +pearliest +pearling +pearls +pearly +pears +peas +peasant +peasantry +peasants +pease +peashooter +peashooters +peat +peatier +peatiest +peaty +pebble +pebbled +pebbles +pebblier +pebbliest +pebbling +pebbly +pecan +pecans +peccadillo +peccadilloes +peccadillos +peccaries +peccary +peck +pecked +pecking +pecks +pectic +pectin +pectoral +pectorals +peculate +peculated +peculates +peculating +peculation +peculator +peculators +peculiar +peculiarities +peculiarity +peculiarly +pecuniary +pedagog +pedagogic +pedagogical +pedagogs +pedagogue +pedagogues +pedagogy +pedal +pedaled +pedaling +pedalled +pedalling +pedals +pedant +pedantic +pedantically +pedantry +pedants +peddle +peddled +peddler +peddlers +peddles +peddling +pederast +pederasts +pederasty +pedestal +pedestals +pedestrian +pedestrianize +pedestrianized +pedestrianizes +pedestrianizing +pedestrians +pediatric +pediatrician +pediatricians +pediatrics +pedicab +pedicabs +pedicure +pedicured +pedicures +pedicuring +pedicurist +pedicurists +pedigree +pedigreed +pedigrees +pediment +pediments +pedlar +pedlars +pedometer +pedometers +peduncle +peduncles +peed +peeing +peek +peekaboo +peeked +peeking +peeks +peel +peeled +peeler +peelers +peeling +peelings +peels +peen +peens +peep +peeped +peeper +peepers +peephole +peepholes +peeping +peeps +peepshow +peepshows +peer +peerage +peerages +peered +peeress +peeresses +peering +peerless +peers +pees +peeve +peeved +peeves +peeving +peevish +peevishly +peevishness +peewee +peewees +pegboard +pegboards +pegged +pegging +pegs +peignoir +peignoirs +pejoration +pejorative +pejoratively +pejoratives +peke +pekes +pekinese +pekingese +pekoe +pelagic +pelf +pelican +pelicans +pellagra +pellet +pelleted +pelleting +pellets +pellmell +pellucid +pelt +pelted +pelting +pelts +pelves +pelvic +pelvis +pelvises +pemmican +penal +penalization +penalize +penalized +penalizes +penalizing +penalties +penalty +penance +penances +pence +penchant +penchants +pencil +penciled +penciling +pencilled +pencilling +pencils +pend +pendant +pendants +pended +pendent +pendents +pending +pends +pendulous +pendulum +pendulums +penes +penetrability +penetrable +penetrate +penetrated +penetrates +penetrating +penetratingly +penetration +penetrations +penetrative +penguin +penguins +penicillin +penile +peninsula +peninsular +peninsulas +penis +penises +penitence +penitent +penitential +penitentiaries +penitentiary +penitently +penitents +penknife +penknives +penlight +penlights +penlite +penlites +penman +penmanship +penmen +pennant +pennants +penned +pennies +penniless +penning +pennon +pennons +penny +pennyweight +pennyweights +penologist +penologists +penology +pens +pension +pensionable +pensioned +pensioner +pensioners +pensioning +pensions +pensive +pensively +pensiveness +pent +pentacle +pentacles +pentagon +pentagonal +pentagons +pentagram +pentagrams +pentameter +pentameters +pentathlete +pentathletes +pentathlon +pentathlons +penthouse +penthouses +penuche +penultimate +penultimates +penumbra +penumbrae +penumbras +penurious +penuriously +penuriousness +penury +peon +peonage +peonies +peons +peony +people +peopled +peoples +peopling +pepped +pepper +peppercorn +peppercorns +peppered +peppering +peppermint +peppermints +pepperoni +pepperonis +peppers +peppery +peppier +peppiest +peppiness +pepping +peppy +peps +pepsin +peptic +peptics +peradventure +perambulate +perambulated +perambulates +perambulating +perambulation +perambulations +perambulator +perambulators +percale +percales +perceivable +perceive +perceived +perceives +perceiving +percent +percentage +percentages +percentile +percentiles +percents +perceptible +perceptibly +perception +perceptional +perceptions +perceptive +perceptively +perceptiveness +perceptual +perceptually +perch +perchance +perched +perches +perching +percipience +percipient +percolate +percolated +percolates +percolating +percolation +percolator +percolators +percussion +percussionist +percussionists +perdition +perdurable +peregrinate +peregrinated +peregrinates +peregrinating +peregrination +peregrinations +peregrine +peregrines +peremptorily +peremptory +perennial +perennially +perennials +perestroika +perfect +perfecta +perfectas +perfected +perfecter +perfectest +perfectibility +perfectible +perfecting +perfection +perfectionism +perfectionist +perfectionists +perfections +perfectly +perfectness +perfects +perfidies +perfidious +perfidiously +perfidy +perforate +perforated +perforates +perforating +perforation +perforations +perforce +perform +performance +performances +performed +performer +performers +performing +performs +perfume +perfumed +perfumer +perfumeries +perfumers +perfumery +perfumes +perfuming +perfunctorily +perfunctory +pergola +pergolas +perhaps +pericardia +pericardium +pericardiums +perigee +perigees +perihelia +perihelion +perihelions +peril +periled +periling +perilled +perilling +perilous +perilously +perils +perimeter +perimeters +perinea +perineum +period +periodic +periodical +periodically +periodicals +periodicity +periodontal +periodontics +periodontist +periodontists +periods +peripatetic +peripatetics +peripheral +peripherally +peripherals +peripheries +periphery +periphrases +periphrasis +periphrastic +periscope +periscopes +perish +perishable +perishables +perished +perishes +perishing +peristalsis +peristaltic +peristyle +peristyles +peritonea +peritoneal +peritoneum +peritoneums +peritonitis +periwig +periwigs +periwinkle +periwinkles +perjure +perjured +perjurer +perjurers +perjures +perjuries +perjuring +perjury +perk +perked +perkier +perkiest +perkily +perkiness +perking +perks +perky +perm +permafrost +permanence +permanency +permanent +permanently +permanents +permeability +permeable +permeate +permeated +permeates +permeating +permeation +permed +perming +permissible +permissibly +permission +permissive +permissively +permissiveness +permit +permits +permitted +permitting +perms +permutation +permutations +permute +permuted +permutes +permuting +pernicious +perniciously +perniciousness +pernickety +peroration +perorations +peroxide +peroxided +peroxides +peroxiding +perpendicular +perpendicularity +perpendicularly +perpendiculars +perpetrate +perpetrated +perpetrates +perpetrating +perpetration +perpetrator +perpetrators +perpetual +perpetually +perpetuals +perpetuate +perpetuated +perpetuates +perpetuating +perpetuation +perpetuity +perplex +perplexed +perplexedly +perplexes +perplexing +perplexities +perplexity +perquisite +perquisites +persecute +persecuted +persecutes +persecuting +persecution +persecutions +persecutor +persecutors +perseverance +persevere +persevered +perseveres +persevering +persiflage +persimmon +persimmons +persist +persisted +persistence +persistent +persistently +persisting +persists +persnickety +person +persona +personable +personae +personage +personages +personal +personalities +personality +personalize +personalized +personalizes +personalizing +personally +personals +personalty +personas +personification +personifications +personified +personifies +personify +personifying +personnel +persons +perspective +perspectives +perspicacious +perspicaciously +perspicacity +perspicuity +perspicuous +perspiration +perspire +perspired +perspires +perspiring +persuadable +persuade +persuaded +persuader +persuaders +persuades +persuading +persuasion +persuasions +persuasive +persuasively +persuasiveness +pert +pertain +pertained +pertaining +pertains +perter +pertest +pertinacious +pertinaciously +pertinacity +pertinence +pertinent +pertinently +pertly +pertness +perturb +perturbation +perturbations +perturbed +perturbing +perturbs +pertussis +peruke +perukes +perusal +perusals +peruse +perused +peruses +perusing +pervade +pervaded +pervades +pervading +pervasive +pervasively +pervasiveness +perverse +perversely +perverseness +perversion +perversions +perversity +pervert +perverted +perverting +perverts +peseta +pesetas +peskier +peskiest +peskily +peskiness +pesky +peso +pesos +pessimism +pessimist +pessimistic +pessimistically +pessimists +pest +pester +pestered +pestering +pesters +pesticide +pesticides +pestiferous +pestilence +pestilences +pestilent +pestilential +pestle +pestled +pestles +pestling +pesto +pests +petal +petaled +petals +petard +petards +petcock +petcocks +peter +petered +petering +peters +petiole +petioles +petite +petites +petition +petitioned +petitioner +petitioners +petitioning +petitions +petrel +petrels +petrifaction +petrified +petrifies +petrify +petrifying +petrochemical +petrochemicals +petrodollar +petrodollars +petrol +petrolatum +petroleum +petrologist +petrologists +petrology +pets +petted +petticoat +petticoats +pettier +pettiest +pettifog +pettifogged +pettifogger +pettifoggers +pettifoggery +pettifogging +pettifogs +pettily +pettiness +petting +pettish +pettishly +petty +petulance +petulant +petulantly +petunia +petunias +pewee +pewees +pewit +pewits +pews +pewter +pewters +peyote +pfennig +pfennige +pfennigs +phaeton +phaetons +phagocyte +phagocytes +phalanger +phalangers +phalanges +phalanx +phalanxes +phalli +phallic +phallus +phalluses +phantasied +phantasies +phantasm +phantasmagoria +phantasmagorias +phantasmal +phantasms +phantasy +phantasying +phantom +phantoms +pharaoh +pharaohs +pharisaic +pharisee +pharisees +pharmaceutic +pharmaceutical +pharmaceuticals +pharmaceutics +pharmacies +pharmacist +pharmacists +pharmacological +pharmacologist +pharmacologists +pharmacology +pharmacopeia +pharmacopeias +pharmacopoeia +pharmacopoeias +pharmacy +pharyngeal +pharynges +pharyngitis +pharynx +pharynxes +phase +phased +phaseout +phaseouts +phases +phasing +phat +phatter +phattest +pheasant +pheasants +phenacetin +phenobarbital +phenol +phenom +phenomena +phenomenal +phenomenally +phenomenon +phenomenons +phenoms +pheromone +pheromones +phew +phial +phials +philander +philandered +philanderer +philanderers +philandering +philanders +philanthropic +philanthropically +philanthropies +philanthropist +philanthropists +philanthropy +philatelic +philatelist +philatelists +philately +philharmonic +philharmonics +philippic +philippics +philistine +philistines +philistinism +philodendra +philodendron +philodendrons +philological +philologist +philologists +philology +philosopher +philosophers +philosophic +philosophical +philosophically +philosophies +philosophize +philosophized +philosophizer +philosophizers +philosophizes +philosophizing +philosophy +philter +philters +philtre +philtres +phis +phlebitis +phlegm +phlegmatic +phlegmatically +phloem +phlox +phloxes +phobia +phobias +phobic +phobics +phoebe +phoebes +phoenix +phoenixes +phone +phoned +phoneme +phonemes +phonemic +phonemically +phones +phonetic +phonetically +phonetician +phoneticians +phonetics +phoney +phoneyed +phoneying +phoneys +phonic +phonically +phonics +phonied +phonier +phonies +phoniest +phoniness +phoning +phonograph +phonographic +phonographs +phonological +phonologically +phonologist +phonologists +phonology +phony +phonying +phooey +phosphate +phosphates +phosphor +phosphorescence +phosphorescent +phosphorescently +phosphoric +phosphorous +phosphors +phosphorus +photo +photocell +photocells +photocopied +photocopier +photocopiers +photocopies +photocopy +photocopying +photoed +photoelectric +photoelectrically +photoengrave +photoengraved +photoengraver +photoengravers +photoengraves +photoengraving +photoengravings +photofinishing +photogenic +photogenically +photograph +photographed +photographer +photographers +photographic +photographically +photographing +photographs +photography +photoing +photojournalism +photojournalist +photojournalists +photometer +photometers +photon +photons +photos +photosensitive +photostat +photostated +photostatic +photostating +photostats +photostatted +photostatting +photosynthesis +photosynthesize +photosynthesized +photosynthesizes +photosynthesizing +photosynthetic +phrasal +phrase +phrased +phraseology +phrases +phrasing +phrasings +phrenetic +phrenologist +phrenologists +phrenology +phyla +phylacteries +phylactery +phylogeny +phylum +physic +physical +physically +physicals +physician +physicians +physicist +physicists +physicked +physicking +physics +physiognomies +physiognomy +physiography +physiologic +physiological +physiologically +physiologist +physiologists +physiology +physiotherapist +physiotherapists +physiotherapy +physique +physiques +pianissimi +pianissimo +pianissimos +pianist +pianists +piano +pianoforte +pianofortes +pianos +piaster +piasters +piastre +piastres +piazza +piazzas +piazze +pibroch +pibrochs +pica +picador +picadors +picaresque +picayune +piccalilli +piccolo +piccolos +pick +pickaback +pickabacked +pickabacking +pickabacks +pickax +pickaxe +pickaxed +pickaxes +pickaxing +picked +picker +pickerel +pickerels +pickers +picket +picketed +picketing +pickets +pickier +pickiest +picking +pickings +pickle +pickled +pickles +pickling +pickpocket +pickpockets +picks +pickup +pickups +picky +picnic +picnicked +picnicker +picnickers +picnicking +picnics +picot +picots +pics +pictograph +pictographs +pictorial +pictorially +pictorials +picture +pictured +pictures +picturesque +picturesquely +picturesqueness +picturing +piddle +piddled +piddles +piddling +piddly +pidgin +pidgins +piebald +piebalds +piece +pieced +piecemeal +pieces +piecework +pieceworker +pieceworkers +piecing +pied +pieing +pier +pierce +pierced +pierces +piercing +piercingly +piercings +piers +pies +piety +piffle +piffling +pigeon +pigeonhole +pigeonholed +pigeonholes +pigeonholing +pigeons +pigged +piggier +piggies +piggiest +pigging +piggish +piggishly +piggishness +piggy +piggyback +piggybacked +piggybacking +piggybacks +pigheaded +pigheadedly +pigheadedness +piglet +piglets +pigment +pigmentation +pigments +pigmies +pigmy +pigpen +pigpens +pigs +pigskin +pigskins +pigsties +pigsty +pigtail +pigtails +piing +pike +piked +piker +pikers +pikes +pikestaff +pikestaffs +pikestaves +piking +pilaf +pilaff +pilaffs +pilafs +pilaster +pilasters +pilau +pilaus +pilchard +pilchards +pile +piled +piles +pileup +pileups +pilfer +pilferage +pilfered +pilferer +pilferers +pilfering +pilfers +pilgrim +pilgrimage +pilgrimages +pilgrims +piling +pilings +pill +pillage +pillaged +pillager +pillagers +pillages +pillaging +pillar +pillars +pillbox +pillboxes +pilled +pilling +pillion +pillions +pilloried +pillories +pillory +pillorying +pillow +pillowcase +pillowcases +pillowed +pillowing +pillows +pillowslip +pillowslips +pills +pilot +piloted +pilothouse +pilothouses +piloting +pilots +pimento +pimentos +pimiento +pimientos +pimp +pimped +pimpernel +pimpernels +pimping +pimple +pimpled +pimples +pimplier +pimpliest +pimply +pimps +pinafore +pinafores +pinata +pinatas +pinball +pincer +pincers +pinch +pinched +pinches +pinching +pincushion +pincushions +pine +pineapple +pineapples +pined +pines +piney +pinfeather +pinfeathers +ping +pinged +pinging +pings +pinhead +pinheads +pinhole +pinholes +pinier +piniest +pining +pinion +pinioned +pinioning +pinions +pink +pinked +pinker +pinkest +pinkeye +pinkie +pinkies +pinking +pinkish +pinkness +pinko +pinkoes +pinkos +pinks +pinky +pinnacle +pinnacles +pinnate +pinned +pinning +pinochle +pinocle +pinon +pinones +pinons +pinpoint +pinpointed +pinpointing +pinpoints +pinprick +pinpricks +pins +pinsetter +pinsetters +pinstripe +pinstriped +pinstripes +pint +pinto +pintoes +pintos +pints +pinup +pinups +pinwheel +pinwheeled +pinwheeling +pinwheels +piny +pinyin +pinyon +pinyons +pioneer +pioneered +pioneering +pioneers +pious +piously +piousness +pipe +piped +pipeline +pipelines +piper +pipers +pipes +pipette +pipettes +piping +pipit +pipits +pipped +pippin +pipping +pippins +pips +pipsqueak +pipsqueaks +piquancy +piquant +piquantly +pique +piqued +piques +piquing +piracy +piranha +piranhas +pirate +pirated +pirates +piratical +piratically +pirating +pirogi +piroshki +pirouette +pirouetted +pirouettes +pirouetting +piscatorial +pismire +pismires +piss +pissed +pisses +pissing +pistachio +pistachios +pistil +pistillate +pistils +pistol +pistols +piston +pistons +pita +pitapat +pitapats +pitch +pitchblende +pitched +pitcher +pitchers +pitches +pitchfork +pitchforked +pitchforking +pitchforks +pitching +pitchman +pitchmen +piteous +piteously +piteousness +pitfall +pitfalls +pith +pithier +pithiest +pithily +pithiness +pithy +pitiable +pitiably +pitied +pities +pitiful +pitifully +pitiless +pitilessly +pitilessness +piton +pitons +pits +pittance +pittances +pitted +pitting +pituitaries +pituitary +pity +pitying +pivot +pivotal +pivoted +pivoting +pivots +pixel +pixels +pixie +pixies +pixy +pizazz +pizza +pizzas +pizzazz +pizzeria +pizzerias +pizzicati +pizzicato +placard +placarded +placarding +placards +placate +placated +placates +placating +placation +placatory +place +placebo +placeboes +placebos +placed +placeholder +placeholders +placekick +placekicked +placekicker +placekickers +placekicking +placekicks +placement +placements +placenta +placentae +placental +placentas +placer +placers +places +placid +placidity +placidly +placing +placket +plackets +plagiarism +plagiarisms +plagiarist +plagiarists +plagiarize +plagiarized +plagiarizer +plagiarizers +plagiarizes +plagiarizing +plagiary +plague +plagued +plagues +plaguing +plaid +plaids +plain +plainclothes +plainclothesman +plainclothesmen +plainer +plainest +plainly +plainness +plains +plainsman +plainsmen +plainsong +plainspoken +plaint +plaintiff +plaintiffs +plaintive +plaintively +plaints +plait +plaited +plaiting +plaits +plan +plane +planed +planeload +planeloads +planer +planers +planes +planet +planetaria +planetarium +planetariums +planetary +planets +plangency +plangent +planing +plank +planked +planking +planks +plankton +planned +planner +planners +planning +plans +plant +plantain +plantains +plantar +plantation +plantations +planted +planter +planters +planting +plantings +plantlike +plants +plaque +plaques +plash +plashed +plashes +plashing +plasma +plaster +plasterboard +plastered +plasterer +plasterers +plastering +plasters +plastic +plasticity +plasticize +plasticized +plasticizes +plasticizing +plastics +plat +plate +plateau +plateaued +plateauing +plateaus +plateaux +plated +plateful +platefuls +platelet +platelets +platen +platens +plates +platform +platformed +platforming +platforms +platies +plating +platinum +platitude +platitudes +platitudinous +platonic +platoon +platooned +platooning +platoons +plats +platted +platter +platters +platting +platy +platypi +platypus +platypuses +platys +plaudit +plaudits +plausibility +plausible +plausibly +play +playable +playact +playacted +playacting +playacts +playback +playbacks +playbill +playbills +playbook +playbooks +playboy +playboys +played +player +players +playfellow +playfellows +playful +playfully +playfulness +playgirl +playgirls +playgoer +playgoers +playground +playgrounds +playhouse +playhouses +playing +playmate +playmates +playoff +playoffs +playpen +playpens +playroom +playrooms +plays +plaything +playthings +playtime +playwright +playwrights +plaza +plazas +plea +plead +pleaded +pleader +pleaders +pleading +pleadings +pleads +pleas +pleasant +pleasanter +pleasantest +pleasantly +pleasantness +pleasantries +pleasantry +please +pleased +pleases +pleasing +pleasingly +pleasurable +pleasurably +pleasure +pleasured +pleasureful +pleasures +pleasuring +pleat +pleated +pleating +pleats +plebe +plebeian +plebeians +plebes +plebiscite +plebiscites +plectra +plectrum +plectrums +pled +pledge +pledged +pledges +pledging +plenaries +plenary +plenipotentiaries +plenipotentiary +plenitude +plenitudes +plenteous +plentiful +plentifully +plenty +pleonasm +pleonasms +plethora +pleura +pleurae +pleurisy +plexiglass +plexus +plexuses +pliability +pliable +pliancy +pliant +plied +pliers +plies +plight +plighted +plighting +plights +plinth +plinths +plod +plodded +plodder +plodders +plodding +plods +plop +plopped +plopping +plops +plot +plots +plotted +plotter +plotters +plotting +plough +ploughed +ploughing +ploughs +plover +plovers +plow +plowed +plowing +plowman +plowmen +plows +plowshare +plowshares +ploy +ploys +pluck +plucked +pluckier +pluckiest +pluckily +pluckiness +plucking +plucks +plucky +plug +plugged +plugging +plugs +plum +plumage +plumb +plumbed +plumber +plumbers +plumbing +plumbs +plume +plumed +plumes +plumier +plumiest +pluming +plummet +plummeted +plummeting +plummets +plump +plumped +plumper +plumpest +plumping +plumply +plumpness +plumps +plums +plumy +plunder +plundered +plunderer +plunderers +plundering +plunders +plunge +plunged +plunger +plungers +plunges +plunging +plunk +plunked +plunking +plunks +pluperfect +pluperfects +plural +pluralism +pluralist +pluralistic +pluralists +pluralities +plurality +pluralization +pluralize +pluralized +pluralizes +pluralizing +plurals +plus +pluses +plush +plusher +plushest +plushier +plushiest +plushly +plushness +plushy +plusses +plutocracies +plutocracy +plutocrat +plutocratic +plutocrats +plutonium +pluvial +plying +plywood +pneumatic +pneumatically +pneumonia +poach +poached +poacher +poachers +poaches +poaching +pock +pocked +pocket +pocketbook +pocketbooks +pocketed +pocketful +pocketfuls +pocketing +pocketknife +pocketknives +pockets +pocketsful +pockmark +pockmarked +pockmarking +pockmarks +pocks +podded +podding +podia +podiatrist +podiatrists +podiatry +podium +podiums +pods +poem +poems +poesy +poet +poetaster +poetasters +poetess +poetesses +poetic +poetical +poetically +poetry +poets +pogrom +pogroms +poignancy +poignant +poignantly +poinciana +poincianas +poinsettia +poinsettias +point +pointblank +pointed +pointedly +pointer +pointers +pointier +pointiest +pointillism +pointillist +pointillists +pointing +pointless +pointlessly +pointlessness +points +pointy +poise +poised +poises +poising +poison +poisoned +poisoner +poisoners +poisoning +poisonings +poisonous +poisonously +poisons +poke +poked +poker +pokers +pokes +pokey +pokeys +pokier +pokiest +poking +poky +polar +polarities +polarity +polarization +polarize +polarized +polarizes +polarizing +pole +polecat +polecats +poled +polemic +polemical +polemically +polemicist +polemicists +polemics +poles +polestar +polestars +police +policed +policeman +policemen +polices +policewoman +policewomen +policies +policing +policy +policyholder +policyholders +poling +polio +poliomyelitis +polish +polished +polisher +polishers +polishes +polishing +politburo +politburos +polite +politely +politeness +politer +politesse +politest +politic +political +politically +politician +politicians +politicization +politicize +politicized +politicizes +politicizing +politicking +politico +politicoes +politicos +politics +polities +polity +polka +polkaed +polkaing +polkas +poll +pollack +pollacks +polled +pollen +pollinate +pollinated +pollinates +pollinating +pollination +pollinator +pollinators +polling +polliwog +polliwogs +pollock +pollocks +polls +pollster +pollsters +pollutant +pollutants +pollute +polluted +polluter +polluters +pollutes +polluting +pollution +pollywog +pollywogs +polo +polonaise +polonaises +polonium +pols +poltergeist +poltergeists +poltroon +poltroons +polyandrous +polyandry +polyclinic +polyclinics +polyester +polyesters +polyethylene +polygamist +polygamists +polygamous +polygamy +polyglot +polyglots +polygon +polygonal +polygons +polygraph +polygraphed +polygraphing +polygraphs +polyhedra +polyhedral +polyhedron +polyhedrons +polymath +polymaths +polymer +polymeric +polymerization +polymerize +polymerized +polymerizes +polymerizing +polymers +polynomial +polynomials +polyp +polyphonic +polyphony +polypropylene +polyps +polystyrene +polysyllabic +polysyllable +polysyllables +polytechnic +polytechnics +polytheism +polytheist +polytheistic +polytheists +polyunsaturated +polyurethane +polyurethanes +polyvinyl +pomade +pomaded +pomades +pomading +pomander +pomanders +pomegranate +pomegranates +pommel +pommeled +pommeling +pommelled +pommelling +pommels +pomp +pompadour +pompadours +pompano +pompanos +pompom +pompoms +pompon +pompons +pomposity +pompous +pompously +pompousness +poncho +ponchos +pond +ponder +pondered +ponderer +ponderers +pondering +ponderous +ponderously +ponderousness +ponders +ponds +pone +pones +pongee +poniard +poniards +ponies +pontiff +pontiffs +pontifical +pontifically +pontificate +pontificated +pontificates +pontificating +pontoon +pontoons +pony +ponytail +ponytails +pooch +pooches +poodle +poodles +poof +poofs +pooh +poohed +poohing +poohs +pool +pooled +pooling +poolroom +poolrooms +pools +poop +pooped +pooping +poops +poor +poorboy +poorboys +poorer +poorest +poorhouse +poorhouses +poorly +poorness +popcorn +pope +popes +popgun +popguns +popinjay +popinjays +poplar +poplars +poplin +popover +popovers +poppa +poppas +popped +popper +poppers +poppies +popping +poppy +poppycock +pops +populace +populaces +popular +popularity +popularization +popularize +popularized +popularizes +popularizing +popularly +populate +populated +populates +populating +population +populations +populism +populist +populists +populous +populousness +porcelain +porch +porches +porcine +porcupine +porcupines +pore +pored +pores +porgies +porgy +poring +pork +porker +porkers +porkier +porkies +porkiest +porky +porn +porno +pornographer +pornographers +pornographic +pornographically +pornography +porosity +porous +porousness +porphyritic +porphyry +porpoise +porpoised +porpoises +porpoising +porridge +porringer +porringers +port +portability +portable +portables +portage +portaged +portages +portaging +portal +portals +portcullis +portcullises +ported +portend +portended +portending +portends +portent +portentous +portentously +portents +porter +porterhouse +porterhouses +porters +portfolio +portfolios +porthole +portholes +portico +porticoes +porticos +portiere +portieres +porting +portion +portioned +portioning +portions +portlier +portliest +portliness +portly +portmanteau +portmanteaus +portmanteaux +portrait +portraitist +portraitists +portraits +portraiture +portray +portrayal +portrayals +portrayed +portraying +portrays +ports +portulaca +pose +posed +poser +posers +poses +poseur +poseurs +posh +posher +poshest +posies +posing +posit +posited +positing +position +positioned +positioning +positions +positive +positively +positiveness +positives +positron +positrons +posits +posse +posses +possess +possessed +possesses +possessing +possession +possessions +possessive +possessively +possessiveness +possessives +possessor +possessors +possibilities +possibility +possible +possibles +possibly +possum +possums +post +postage +postal +postcard +postcards +postconsonantal +postdate +postdated +postdates +postdating +postdoctoral +posted +poster +posterior +posteriors +posterity +posters +postgraduate +postgraduates +posthaste +posthumous +posthumously +posthypnotic +postilion +postilions +postillion +postillions +postindustrial +posting +postings +postlude +postludes +postman +postmark +postmarked +postmarking +postmarks +postmaster +postmasters +postmen +postmenopausal +postmeridian +postmistress +postmistresses +postmodern +postmodernism +postmodernist +postmodernists +postmortem +postmortems +postnasal +postnatal +postoperative +postpaid +postpartum +postpone +postponed +postponement +postponements +postpones +postponing +postprandial +posts +postscript +postscripts +postseason +postseasons +postulate +postulated +postulates +postulating +postulation +postulations +posture +postured +postures +posturing +posturings +postwar +posy +potability +potable +potables +potash +potassium +potato +potatoes +potbellied +potbellies +potbelly +potboiler +potboilers +potency +potent +potentate +potentates +potential +potentialities +potentiality +potentially +potently +potful +potfuls +pothead +potheads +pother +potherb +potherbs +pothered +pothering +pothers +potholder +potholders +pothole +potholed +potholes +potholing +pothook +pothooks +potion +potions +potluck +potlucks +potpie +potpies +potpourri +potpourris +pots +potsherd +potsherds +potshot +potshots +pottage +potted +potter +pottered +potteries +pottering +potters +pottery +pottier +potties +pottiest +potting +potty +pouch +pouched +pouches +pouching +poulterer +poulterers +poultice +poulticed +poultices +poulticing +poultry +pounce +pounced +pounces +pouncing +pound +poundage +pounded +pounding +poundings +pounds +pour +poured +pouring +pours +pout +pouted +pouter +pouters +pouting +pouts +poverty +powder +powdered +powdering +powders +powdery +power +powerboat +powerboats +powered +powerful +powerfully +powerhouse +powerhouses +powering +powerless +powerlessly +powerlessness +powers +powwow +powwowed +powwowing +powwows +poxes +practicability +practicable +practicably +practical +practicalities +practicality +practically +practicals +practice +practiced +practices +practicing +practicum +practicums +practise +practised +practises +practising +practitioner +practitioners +praetor +praetorian +praetors +pragmatic +pragmatical +pragmatically +pragmatics +pragmatism +pragmatist +pragmatists +prairie +prairies +praise +praised +praises +praiseworthier +praiseworthiest +praiseworthiness +praiseworthy +praising +praline +pralines +pram +prams +prance +pranced +prancer +prancers +prances +prancing +prancingly +prank +pranks +prankster +pranksters +praseodymium +prate +prated +prater +praters +prates +pratfall +pratfalls +prating +prattle +prattled +prattler +prattlers +prattles +prattling +prawn +prawned +prawning +prawns +pray +prayed +prayer +prayerful +prayerfully +prayers +praying +prays +preach +preached +preacher +preachers +preaches +preachier +preachiest +preaching +preachment +preachy +preadolescence +preadolescences +preamble +preambles +prearrange +prearranged +prearrangement +prearranges +prearranging +preassigned +precancel +precanceled +precanceling +precancelled +precancelling +precancels +precancerous +precarious +precariously +precariousness +precaution +precautionary +precautions +precede +preceded +precedence +precedent +precedents +precedes +preceding +precept +preceptor +preceptors +precepts +precinct +precincts +preciosity +precious +preciously +preciousness +precipice +precipices +precipitant +precipitants +precipitate +precipitated +precipitately +precipitates +precipitating +precipitation +precipitations +precipitous +precipitously +precis +precise +precised +precisely +preciseness +preciser +precises +precisest +precising +precision +preclude +precluded +precludes +precluding +preclusion +precocious +precociously +precociousness +precocity +precognition +precognitive +precolonial +preconceive +preconceived +preconceives +preconceiving +preconception +preconceptions +precondition +preconditioned +preconditioning +preconditions +precook +precooked +precooking +precooks +precursor +precursors +precursory +predate +predated +predates +predating +predator +predators +predatory +predawn +predecease +predeceased +predeceases +predeceasing +predecessor +predecessors +predesignate +predesignated +predesignates +predesignating +predestination +predestine +predestined +predestines +predestining +predetermination +predetermine +predetermined +predeterminer +predeterminers +predetermines +predetermining +predicable +predicament +predicaments +predicate +predicated +predicates +predicating +predication +predicative +predict +predictability +predictable +predictably +predicted +predicting +prediction +predictions +predictive +predictor +predictors +predicts +predigest +predigested +predigesting +predigests +predilection +predilections +predispose +predisposed +predisposes +predisposing +predisposition +predispositions +predominance +predominant +predominantly +predominate +predominated +predominates +predominating +preemie +preemies +preeminence +preeminent +preeminently +preempt +preempted +preempting +preemption +preemptive +preempts +preen +preened +preening +preens +preexist +preexisted +preexistence +preexisting +preexists +prefab +prefabbed +prefabbing +prefabricate +prefabricated +prefabricates +prefabricating +prefabrication +prefabs +preface +prefaced +prefaces +prefacing +prefatory +prefect +prefects +prefecture +prefectures +prefer +preferable +preferably +preference +preferences +preferential +preferentially +preferment +preferred +preferring +prefers +prefigure +prefigured +prefigures +prefiguring +prefix +prefixed +prefixes +prefixing +preform +preformed +preforming +preforms +pregame +pregames +pregnancies +pregnancy +pregnant +preheat +preheated +preheating +preheats +prehensile +prehistoric +prehistorical +prehistorically +prehistory +prejudge +prejudged +prejudges +prejudging +prejudgment +prejudgments +prejudice +prejudiced +prejudices +prejudicial +prejudicing +prekindergarten +prekindergartens +prelacy +prelate +prelates +prelim +preliminaries +preliminary +prelims +preliterate +prelude +preludes +premarital +premature +prematurely +premed +premedical +premeditate +premeditated +premeditates +premeditating +premeditation +premeds +premenstrual +premier +premiere +premiered +premieres +premiering +premiers +premiership +premierships +premise +premised +premises +premising +premiss +premisses +premium +premiums +premix +premixed +premixes +premixing +premolar +premolars +premonition +premonitions +premonitory +prenatal +prenatally +prenuptial +preoccupation +preoccupations +preoccupied +preoccupies +preoccupy +preoccupying +preoperative +preordain +preordained +preordaining +preordains +prep +prepackage +prepackaged +prepackages +prepackaging +prepaid +preparation +preparations +preparatory +prepare +prepared +preparedness +prepares +preparing +prepay +prepaying +prepayment +prepayments +prepays +preponderance +preponderances +preponderant +preponderantly +preponderate +preponderated +preponderates +preponderating +preposition +prepositional +prepositionally +prepositions +prepossess +prepossessed +prepossesses +prepossessing +prepossession +prepossessions +preposterous +preposterously +prepped +preppie +preppier +preppies +preppiest +prepping +preppy +preps +prepubescence +prepubescent +prepubescents +prepuce +prepuces +prequel +prequels +prerecord +prerecorded +prerecording +prerecords +preregister +preregistered +preregistering +preregisters +preregistration +prerequisite +prerequisites +prerogative +prerogatives +presage +presaged +presages +presaging +presbyopia +presbyter +presbyteries +presbyters +presbytery +preschool +preschooler +preschoolers +preschools +prescience +prescient +presciently +prescribe +prescribed +prescribes +prescribing +prescript +prescription +prescriptions +prescriptive +prescriptively +prescripts +preseason +preseasons +presence +presences +present +presentable +presentably +presentation +presentations +presented +presenter +presenters +presentiment +presentiments +presenting +presently +presentment +presentments +presents +preservable +preservation +preservationist +preservationists +preservative +preservatives +preserve +preserved +preserver +preservers +preserves +preserving +preset +presets +presetting +preshrank +preshrink +preshrinking +preshrinks +preshrunk +preshrunken +preside +presided +presidencies +presidency +president +presidential +presidents +presides +presidia +presiding +presidium +presidiums +presort +presorted +presorting +presorts +press +pressed +presser +pressers +presses +pressing +pressingly +pressings +pressman +pressmen +pressure +pressured +pressures +pressuring +pressurization +pressurize +pressurized +pressurizer +pressurizers +pressurizes +pressurizing +prestidigitation +prestige +prestigious +presto +prestos +presumable +presumably +presume +presumed +presumes +presuming +presumption +presumptions +presumptive +presumptuous +presumptuously +presumptuousness +presuppose +presupposed +presupposes +presupposing +presupposition +presuppositions +pretax +preteen +preteens +pretence +pretences +pretend +pretended +pretender +pretenders +pretending +pretends +pretense +pretenses +pretension +pretensions +pretentious +pretentiously +pretentiousness +preterit +preterite +preterites +preterits +preterm +preternatural +preternaturally +pretest +pretested +pretesting +pretests +pretext +pretexts +pretrial +prettied +prettier +pretties +prettiest +prettified +prettifies +prettify +prettifying +prettily +prettiness +pretty +prettying +pretzel +pretzels +prevail +prevailed +prevailing +prevails +prevalence +prevalent +prevaricate +prevaricated +prevaricates +prevaricating +prevarication +prevarications +prevaricator +prevaricators +prevent +preventable +preventative +preventatives +prevented +preventible +preventing +prevention +preventive +preventives +prevents +preview +previewed +previewing +previews +previous +previously +prevision +previsions +prevue +prevues +prewar +prey +preyed +preying +preys +priapic +price +priced +priceless +prices +pricey +pricier +priciest +pricing +prick +pricked +pricker +prickers +pricking +prickle +prickled +prickles +pricklier +prickliest +prickliness +prickling +prickly +pricks +pricy +pride +prided +prideful +pridefully +prides +priding +pried +prier +priers +pries +priest +priestess +priestesses +priesthood +priesthoods +priestlier +priestliest +priestliness +priestly +priests +prig +priggish +priggishness +prigs +prim +primacy +primaeval +primal +primaries +primarily +primary +primate +primates +prime +primed +primer +primers +primes +primeval +priming +primitive +primitively +primitiveness +primitives +primly +primmer +primmest +primness +primogenitor +primogenitors +primogeniture +primordial +primordially +primp +primped +primping +primps +primrose +primroses +prince +princedom +princedoms +princelier +princeliest +princeliness +princely +princes +princess +princesses +principal +principalities +principality +principally +principals +principle +principled +principles +print +printable +printed +printer +printers +printing +printings +printout +printouts +prints +prior +prioress +prioresses +priories +priorities +prioritize +prioritized +prioritizes +prioritizing +priority +priors +priory +prise +prised +prises +prising +prism +prismatic +prisms +prison +prisoner +prisoners +prisons +prissier +prissiest +prissily +prissiness +prissy +pristine +prithee +privacy +private +privateer +privateers +privately +privater +privates +privatest +privation +privations +privatization +privatizations +privatize +privatized +privatizes +privatizing +privet +privets +privier +privies +priviest +privilege +privileged +privileges +privileging +privily +privy +prize +prized +prizefight +prizefighter +prizefighters +prizefighting +prizefights +prizes +prizewinner +prizewinners +prizewinning +prizing +proactive +probabilities +probability +probable +probables +probably +probate +probated +probates +probating +probation +probational +probationary +probationer +probationers +probe +probed +probes +probing +probity +problem +problematic +problematical +problematically +problems +proboscides +proboscis +proboscises +procaine +procedural +procedure +procedures +proceed +proceeded +proceeding +proceedings +proceeds +process +processed +processes +processing +procession +processional +processionals +processioned +processioning +processions +processor +processors +proclaim +proclaimed +proclaiming +proclaims +proclamation +proclamations +proclivities +proclivity +proconsul +proconsular +proconsuls +procrastinate +procrastinated +procrastinates +procrastinating +procrastination +procrastinator +procrastinators +procreate +procreated +procreates +procreating +procreation +procreative +proctor +proctored +proctoring +proctors +procurable +procurator +procurators +procure +procured +procurement +procurer +procurers +procures +procuring +prod +prodded +prodding +prodigal +prodigality +prodigals +prodigies +prodigious +prodigiously +prodigy +prods +produce +produced +producer +producers +produces +producible +producing +product +production +productions +productive +productively +productiveness +productivity +products +prof +profanation +profanations +profane +profaned +profanely +profaneness +profanes +profaning +profanities +profanity +profess +professed +professedly +professes +professing +profession +professional +professionalism +professionalize +professionalized +professionalizes +professionalizing +professionally +professionals +professions +professor +professorial +professorially +professors +professorship +professorships +proffer +proffered +proffering +proffers +proficiency +proficient +proficiently +proficients +profile +profiled +profiles +profiling +profit +profitability +profitable +profitably +profited +profiteer +profiteered +profiteering +profiteers +profiterole +profiteroles +profiting +profitless +profits +profligacy +profligate +profligately +profligates +profound +profounder +profoundest +profoundly +profoundness +profs +profundities +profundity +profuse +profusely +profuseness +profuser +profusest +profusion +profusions +progenitor +progenitors +progeny +progesterone +prognathous +prognoses +prognosis +prognostic +prognosticate +prognosticated +prognosticates +prognosticating +prognostication +prognostications +prognosticator +prognosticators +prognostics +program +programed +programer +programers +programing +programmable +programmables +programmatic +programme +programmed +programmer +programmers +programmes +programming +programs +progress +progressed +progresses +progressing +progression +progressions +progressive +progressively +progressiveness +progressives +prohibit +prohibited +prohibiting +prohibition +prohibitionist +prohibitionists +prohibitions +prohibitive +prohibitively +prohibitory +prohibits +project +projected +projectile +projectiles +projecting +projection +projectionist +projectionists +projections +projector +projectors +projects +prolapse +prolapsed +prolapses +prolapsing +proletarian +proletarians +proletariat +proliferate +proliferated +proliferates +proliferating +proliferation +prolific +prolifically +prolix +prolixity +prolixly +prolog +prologs +prologue +prologues +prolong +prolongation +prolongations +prolonged +prolonging +prolongs +prom +promenade +promenaded +promenades +promenading +promethium +prominence +prominent +prominently +promiscuity +promiscuous +promiscuously +promise +promised +promises +promising +promisingly +promissory +promo +promoed +promoing +promontories +promontory +promos +promote +promoted +promoter +promoters +promotes +promoting +promotion +promotional +promotions +prompt +prompted +prompter +prompters +promptest +prompting +promptings +promptitude +promptly +promptness +prompts +proms +promulgate +promulgated +promulgates +promulgating +promulgation +promulgator +promulgators +prone +proneness +proner +pronest +prong +pronged +pronghorn +pronghorns +prongs +pronominal +pronominals +pronoun +pronounce +pronounceable +pronounced +pronouncement +pronouncements +pronounces +pronouncing +pronouns +pronto +pronuclear +pronunciation +pronunciations +proof +proofed +proofing +proofread +proofreader +proofreaders +proofreading +proofreads +proofs +prop +propaganda +propagandist +propagandists +propagandize +propagandized +propagandizes +propagandizing +propagate +propagated +propagates +propagating +propagation +propagator +propagators +propane +propel +propellant +propellants +propelled +propellent +propellents +propeller +propellers +propelling +propels +propensities +propensity +proper +properer +properest +properly +propertied +properties +property +prophecies +prophecy +prophesied +prophesier +prophesiers +prophesies +prophesy +prophesying +prophet +prophetess +prophetesses +prophetic +prophetical +prophetically +prophets +prophylactic +prophylactics +prophylaxis +propinquity +propitiate +propitiated +propitiates +propitiating +propitiation +propitiatory +propitious +propitiously +proponent +proponents +proportion +proportional +proportionally +proportionate +proportionately +proportioned +proportioning +proportions +proposal +proposals +propose +proposed +proposer +proposers +proposes +proposing +proposition +propositional +propositioned +propositioning +propositions +propound +propounded +propounding +propounds +propped +propping +proprietaries +proprietary +proprieties +proprietor +proprietorial +proprietors +proprietorship +proprietress +proprietresses +propriety +props +propulsion +propulsive +prorate +prorated +prorates +prorating +prorogation +prorogue +prorogued +prorogues +proroguing +pros +prosaic +prosaically +proscenia +proscenium +prosceniums +prosciutto +proscribe +proscribed +proscribes +proscribing +proscription +proscriptions +prose +prosecute +prosecuted +prosecutes +prosecuting +prosecution +prosecutions +prosecutor +prosecutors +proselyte +proselyted +proselytes +proselyting +proselytism +proselytize +proselytized +proselytizer +proselytizers +proselytizes +proselytizing +prosier +prosiest +prosodies +prosody +prospect +prospected +prospecting +prospective +prospectively +prospector +prospectors +prospects +prospectus +prospectuses +prosper +prospered +prospering +prosperity +prosperous +prosperously +prospers +prostate +prostates +prostheses +prosthesis +prosthetic +prostitute +prostituted +prostitutes +prostituting +prostitution +prostrate +prostrated +prostrates +prostrating +prostration +prostrations +prosy +protactinium +protagonist +protagonists +protean +protect +protected +protecting +protection +protectionism +protectionist +protectionists +protections +protective +protectively +protectiveness +protector +protectorate +protectorates +protectors +protects +protege +proteges +protein +proteins +protest +protestation +protestations +protested +protester +protesters +protesting +protestor +protestors +protests +protocol +protocols +proton +protons +protoplasm +protoplasmic +prototype +prototypes +prototypical +protozoa +protozoan +protozoans +protozoic +protozoon +protract +protracted +protracting +protraction +protractor +protractors +protracts +protrude +protruded +protrudes +protruding +protrusile +protrusion +protrusions +protuberance +protuberances +protuberant +proud +prouder +proudest +proudly +provability +provable +prove +proved +proven +provenance +provender +provenience +proverb +proverbial +proverbially +proverbs +proves +provide +provided +providence +provident +providential +providentially +providently +provider +providers +provides +providing +province +provinces +provincial +provincialism +provincially +provincials +proving +provision +provisional +provisionally +provisioned +provisioning +provisions +proviso +provisoes +provisos +provocation +provocations +provocative +provocatively +provocativeness +provoke +provoked +provoker +provokers +provokes +provoking +provokingly +provolone +provost +provosts +prow +prowess +prowl +prowled +prowler +prowlers +prowling +prowls +prows +proxies +proximate +proximity +proxy +prude +prudence +prudent +prudential +prudentially +prudently +prudery +prudes +prudish +prudishly +prudishness +prune +pruned +pruner +pruners +prunes +pruning +prurience +prurient +pryer +pryers +prying +psalm +psalmist +psalmists +psalms +psalteries +psaltery +pseudo +pseudonym +pseudonymous +pseudonyms +pseudoscience +pseudosciences +pshaw +pshaws +psis +psittacosis +psoriasis +psst +psych +psyche +psyched +psychedelic +psychedelically +psychedelics +psyches +psychiatric +psychiatrist +psychiatrists +psychiatry +psychic +psychical +psychically +psychics +psyching +psycho +psychoactive +psychoanalysis +psychoanalyst +psychoanalysts +psychoanalytic +psychoanalytical +psychoanalyze +psychoanalyzed +psychoanalyzes +psychoanalyzing +psychobabble +psychodrama +psychodramas +psychogenic +psychological +psychologically +psychologies +psychologist +psychologists +psychology +psychoneurosis +psychopath +psychopathic +psychopaths +psychopathy +psychos +psychoses +psychosis +psychosomatic +psychotherapies +psychotherapist +psychotherapists +psychotherapy +psychotic +psychotically +psychotics +psychotropic +psychotropics +psychs +ptarmigan +ptarmigans +pterodactyl +pterodactyls +ptomaine +ptomaines +pubertal +puberty +pubes +pubescence +pubescent +pubic +pubis +public +publican +publicans +publication +publications +publicist +publicists +publicity +publicize +publicized +publicizes +publicizing +publicly +publish +published +publisher +publishers +publishes +publishing +pubs +puce +puck +pucker +puckered +puckering +puckers +puckish +puckishly +puckishness +pucks +pudding +puddings +puddle +puddled +puddles +puddling +pudenda +pudendum +pudgier +pudgiest +pudginess +pudgy +pueblo +pueblos +puerile +puerility +puerperal +puff +puffball +puffballs +puffed +puffer +puffers +puffier +puffiest +puffin +puffiness +puffing +puffins +puffs +puffy +pugilism +pugilist +pugilistic +pugilists +pugnacious +pugnaciously +pugnaciousness +pugnacity +pugs +puke +puked +pukes +puking +pukka +pulchritude +pulchritudinous +pule +puled +pules +puling +pull +pullback +pullbacks +pulled +puller +pullers +pullet +pullets +pulley +pulleys +pulling +pullout +pullouts +pullover +pullovers +pulls +pullup +pullups +pulmonary +pulp +pulped +pulpier +pulpiest +pulpiness +pulping +pulpit +pulpits +pulps +pulpwood +pulpy +pulsar +pulsars +pulsate +pulsated +pulsates +pulsating +pulsation +pulsations +pulse +pulsed +pulses +pulsing +pulverization +pulverize +pulverized +pulverizes +pulverizing +puma +pumas +pumice +pummel +pummeled +pummeling +pummelled +pummelling +pummels +pump +pumped +pumper +pumpernickel +pumpers +pumping +pumpkin +pumpkins +pumps +punch +punched +puncheon +puncheons +puncher +punchers +punches +punchier +punchiest +punching +punchy +punctilio +punctilious +punctiliously +punctiliousness +punctual +punctuality +punctually +punctuate +punctuated +punctuates +punctuating +punctuation +puncture +punctured +punctures +puncturing +pundit +punditry +pundits +pungency +pungent +pungently +punier +puniest +puniness +punish +punishable +punished +punishes +punishing +punishment +punishments +punitive +punitively +punk +punker +punkest +punkin +punkins +punks +punned +punning +puns +punster +punsters +punt +punted +punter +punters +punting +punts +puny +pupa +pupae +pupal +pupas +pupil +pupils +pupped +puppet +puppeteer +puppeteers +puppetry +puppets +puppies +pupping +puppy +pups +purblind +purchasable +purchase +purchased +purchaser +purchasers +purchases +purchasing +purdah +pure +purebred +purebreds +puree +pureed +pureeing +purees +purely +pureness +purer +purest +purgative +purgatives +purgatorial +purgatories +purgatory +purge +purged +purger +purgers +purges +purging +purification +purified +purifier +purifiers +purifies +purify +purifying +purine +purines +purism +purist +puristic +purists +puritan +puritanical +puritanically +puritanism +puritans +purity +purl +purled +purlieu +purlieus +purling +purloin +purloined +purloining +purloins +purls +purple +purpler +purples +purplest +purplish +purport +purported +purportedly +purporting +purports +purpose +purposed +purposeful +purposefully +purposefulness +purposeless +purposelessly +purposely +purposes +purposing +purr +purred +purring +purrs +purse +pursed +purser +pursers +purses +pursing +pursuance +pursuant +pursue +pursued +pursuer +pursuers +pursues +pursuing +pursuit +pursuits +purulence +purulent +purvey +purveyance +purveyed +purveying +purveyor +purveyors +purveys +purview +push +pushcart +pushcarts +pushed +pusher +pushers +pushes +pushier +pushiest +pushily +pushiness +pushing +pushover +pushovers +pushup +pushups +pushy +pusillanimity +pusillanimous +pusillanimously +puss +pusses +pussier +pussies +pussiest +pussy +pussycat +pussycats +pussyfoot +pussyfooted +pussyfooting +pussyfoots +pustular +pustule +pustules +putative +putdown +putdowns +putout +putouts +putrefaction +putrefactive +putrefied +putrefies +putrefy +putrefying +putrescence +putrescent +putrid +puts +putsch +putsches +putt +putted +puttee +puttees +putter +puttered +putterer +putterers +puttering +putters +puttied +putties +putting +putts +putty +puttying +puzzle +puzzled +puzzlement +puzzler +puzzlers +puzzles +puzzling +pygmies +pygmy +pyjamas +pylon +pylons +pylori +pyloric +pylorus +pyorrhea +pyorrhoea +pyramid +pyramidal +pyramided +pyramiding +pyramids +pyre +pyres +pyrimidine +pyrimidines +pyrite +pyrites +pyromania +pyromaniac +pyromaniacs +pyrotechnic +pyrotechnical +pyrotechnics +python +pythons +pyxes +quack +quacked +quackery +quacking +quacks +quad +quadrangle +quadrangles +quadrangular +quadrant +quadrants +quadraphonic +quadratic +quadratics +quadrennia +quadrennial +quadrennium +quadrenniums +quadriceps +quadricepses +quadrilateral +quadrilaterals +quadrille +quadrilles +quadrillion +quadrillions +quadriplegia +quadriplegic +quadriplegics +quadrivium +quadruped +quadrupedal +quadrupeds +quadruple +quadrupled +quadruples +quadruplet +quadruplets +quadruplicate +quadruplicated +quadruplicates +quadruplicating +quadruplication +quadrupling +quads +quaff +quaffed +quaffing +quaffs +quagmire +quagmires +quahaug +quahaugs +quahog +quahogs +quail +quailed +quailing +quails +quaint +quainter +quaintest +quaintly +quaintness +quake +quaked +quakes +quakier +quakiest +quaking +quaky +qualification +qualifications +qualified +qualifier +qualifiers +qualifies +qualify +qualifying +qualitative +qualitatively +qualities +quality +qualm +qualmish +qualms +quandaries +quandary +quanta +quantifiable +quantification +quantified +quantifier +quantifiers +quantifies +quantify +quantifying +quantitative +quantitatively +quantities +quantity +quantum +quarantine +quarantined +quarantines +quarantining +quark +quarks +quarrel +quarreled +quarreler +quarrelers +quarreling +quarrelled +quarrelling +quarrels +quarrelsome +quarrelsomeness +quarried +quarries +quarry +quarrying +quart +quarter +quarterback +quarterbacked +quarterbacking +quarterbacks +quarterdeck +quarterdecks +quartered +quarterfinal +quarterfinals +quartering +quarterlies +quarterly +quartermaster +quartermasters +quarters +quarterstaff +quarterstaffs +quarterstaves +quartet +quartets +quartette +quartettes +quarto +quartos +quarts +quartz +quasar +quasars +quash +quashed +quashes +quashing +quasi +quatrain +quatrains +quaver +quavered +quavering +quavers +quavery +quay +quays +queasier +queasiest +queasily +queasiness +queasy +queen +queened +queening +queenlier +queenliest +queenly +queens +queer +queered +queerer +queerest +queering +queerly +queerness +queers +quell +quelled +quelling +quells +quench +quenchable +quenched +quencher +quenchers +quenches +quenching +quenchless +queried +queries +querulous +querulously +querulousness +query +querying +quest +quested +questing +question +questionable +questionably +questioned +questioner +questioners +questioning +questioningly +questionings +questionnaire +questionnaires +questions +quests +queue +queued +queueing +queues +queuing +quibble +quibbled +quibbler +quibblers +quibbles +quibbling +quiche +quiches +quick +quicken +quickened +quickening +quickens +quicker +quickest +quickie +quickies +quicklime +quickly +quickness +quicksand +quicksands +quicksilver +quickstep +quicksteps +quid +quids +quiescence +quiescent +quiescently +quiet +quieted +quieter +quietest +quieting +quietly +quietness +quiets +quietude +quietus +quietuses +quill +quills +quilt +quilted +quilter +quilters +quilting +quilts +quince +quinces +quinine +quinsy +quint +quintessence +quintessences +quintessential +quintessentially +quintet +quintets +quintette +quintettes +quints +quintuple +quintupled +quintuples +quintuplet +quintuplets +quintupling +quip +quipped +quipping +quips +quipster +quipsters +quire +quires +quirk +quirked +quirkier +quirkiest +quirkiness +quirking +quirks +quirky +quirt +quirts +quisling +quislings +quit +quitclaim +quitclaims +quite +quits +quittance +quitted +quitter +quitters +quitting +quiver +quivered +quivering +quivers +quivery +quixotic +quixotically +quiz +quizzed +quizzer +quizzers +quizzes +quizzical +quizzically +quizzing +quoin +quoins +quoit +quoited +quoiting +quoits +quondam +quorum +quorums +quota +quotability +quotable +quotas +quotation +quotations +quote +quoted +quotes +quoth +quotidian +quotient +quotients +quoting +qwerty +rabbet +rabbeted +rabbeting +rabbets +rabbi +rabbinate +rabbinic +rabbinical +rabbis +rabbit +rabbits +rabble +rabbles +rabid +rabidly +rabidness +rabies +raccoon +raccoons +race +racecourse +racecourses +raced +racehorse +racehorses +raceme +racemes +racer +racers +races +racetrack +racetracks +raceway +raceways +racial +racialism +racialist +racialists +racially +racier +raciest +racily +raciness +racing +racism +racist +racists +rack +racked +racket +racketed +racketeer +racketeered +racketeering +racketeers +racketing +rackets +racking +racks +raconteur +raconteurs +racoon +racoons +racquet +racquetball +racquetballs +racquets +racy +radar +radars +radarscope +radarscopes +radial +radially +radials +radiance +radiant +radiantly +radiate +radiated +radiates +radiating +radiation +radiations +radiator +radiators +radical +radicalism +radicalization +radicalize +radicalized +radicalizes +radicalizing +radically +radicals +radicchio +radii +radio +radioactive +radioactively +radioactivity +radiocarbon +radioed +radiogram +radiograms +radiographer +radiographers +radiography +radioing +radioisotope +radioisotopes +radiologist +radiologists +radiology +radioman +radiomen +radiometer +radiometers +radiometric +radiometry +radiophone +radiophones +radios +radioscopy +radiosonde +radiosondes +radiotelegraph +radiotelegraphed +radiotelegraphing +radiotelegraphs +radiotelegraphy +radiotelephone +radiotelephones +radiotherapist +radiotherapists +radiotherapy +radish +radishes +radium +radius +radiuses +radon +rads +raffia +raffish +raffishly +raffishness +raffle +raffled +raffles +raffling +raft +rafted +rafter +rafters +rafting +rafts +raga +ragamuffin +ragamuffins +ragas +ragbag +rage +raged +rages +ragged +raggeder +raggedest +raggedier +raggediest +raggedly +raggedness +raggedy +ragging +raging +ragingly +raglan +raglans +ragout +ragouts +rags +ragtag +ragtime +ragweed +raid +raided +raider +raiders +raiding +raids +rail +railed +railing +railings +railleries +raillery +railroad +railroaded +railroader +railroaders +railroading +railroads +rails +railway +railways +raiment +rain +rainbow +rainbows +raincoat +raincoats +raindrop +raindrops +rained +rainfall +rainfalls +rainier +rainiest +raining +rainmaker +rainmakers +rainmaking +rainproof +rains +rainstorm +rainstorms +rainwater +rainy +raise +raised +raiser +raisers +raises +raisin +raising +raisins +raja +rajah +rajahs +rajas +rake +raked +rakes +raking +rakish +rakishly +rakishness +rallied +rallies +rally +rallying +ramble +rambled +rambler +ramblers +rambles +rambling +rambunctious +rambunctiously +rambunctiousness +ramekin +ramekins +ramequin +ramequins +ramie +ramification +ramifications +ramified +ramifies +ramify +ramifying +ramjet +ramjets +rammed +ramming +ramp +rampage +rampaged +rampages +rampaging +rampancy +rampant +rampantly +rampart +ramparts +ramps +ramrod +ramrodded +ramrodding +ramrods +rams +ramshackle +ranch +ranched +rancher +ranchers +ranches +ranching +rancid +rancidity +rancidness +rancor +rancorous +rancorously +rancour +rand +randier +randiest +randiness +random +randomization +randomize +randomized +randomizes +randomizing +randomly +randomness +randy +ranee +ranees +rang +range +ranged +ranger +rangers +ranges +rangier +rangiest +ranginess +ranging +rangy +rani +ranis +rank +ranked +ranker +rankest +ranking +rankings +rankle +rankled +rankles +rankling +rankly +rankness +ranks +ransack +ransacked +ransacking +ransacks +ransom +ransomed +ransomer +ransomers +ransoming +ransoms +rant +ranted +ranter +ranters +ranting +rants +rapacious +rapaciously +rapaciousness +rapacity +rape +raped +raper +rapers +rapes +rapeseed +rapid +rapider +rapidest +rapidity +rapidly +rapidness +rapids +rapier +rapiers +rapine +raping +rapist +rapists +rapped +rappel +rappelled +rappelling +rappels +rapper +rappers +rapping +rapport +rapports +rapprochement +rapprochements +raps +rapscallion +rapscallions +rapt +raptly +raptness +rapture +raptures +rapturous +rapturously +rare +rarebit +rarebits +rared +rarefaction +rarefied +rarefies +rarefy +rarefying +rarely +rareness +rarer +rares +rarest +raring +rarities +rarity +rascal +rascally +rascals +rash +rasher +rashers +rashes +rashest +rashly +rashness +rasp +raspberries +raspberry +rasped +raspier +raspiest +rasping +rasps +raspy +ratatouille +ratchet +ratcheted +ratcheting +ratchets +rate +rated +rater +raters +rates +rather +rathskeller +rathskellers +ratification +ratified +ratifier +ratifiers +ratifies +ratify +ratifying +rating +ratings +ratio +ratiocinate +ratiocinated +ratiocinates +ratiocinating +ratiocination +ration +rational +rationale +rationales +rationalism +rationalist +rationalistic +rationalists +rationality +rationalization +rationalizations +rationalize +rationalized +rationalizes +rationalizing +rationally +rationals +rationed +rationing +rations +ratios +ratlike +ratlin +ratline +ratlines +ratlins +rats +rattan +rattans +ratted +ratter +ratters +rattier +rattiest +ratting +rattle +rattlebrain +rattlebrained +rattlebrains +rattled +rattler +rattlers +rattles +rattlesnake +rattlesnakes +rattletrap +rattletraps +rattling +rattly +rattrap +rattraps +ratty +raucous +raucously +raucousness +raunchier +raunchiest +raunchily +raunchiness +raunchy +ravage +ravaged +ravager +ravagers +ravages +ravaging +rave +raved +ravel +raveled +raveling +ravelled +ravelling +ravels +raven +ravened +ravening +ravenous +ravenously +ravens +raves +ravine +ravines +raving +ravings +ravioli +raviolis +ravish +ravished +ravisher +ravishers +ravishes +ravishing +ravishingly +ravishment +rawboned +rawer +rawest +rawhide +rawness +rayon +rays +raze +razed +razes +razing +razor +razorback +razorbacks +razors +razz +razzed +razzes +razzing +razzmatazz +reabsorb +reabsorbed +reabsorbing +reabsorbs +reach +reachable +reached +reaches +reaching +reacquaint +reacquainted +reacquainting +reacquaints +reacquire +reacquired +reacquires +reacquiring +react +reactant +reactants +reacted +reacting +reaction +reactionaries +reactionary +reactions +reactivate +reactivated +reactivates +reactivating +reactivation +reactive +reactor +reactors +reacts +read +readabilities +readability +readable +readdress +readdressed +readdresses +readdressing +reader +readers +readership +readerships +readied +readier +readies +readiest +readily +readiness +reading +readings +readjust +readjusted +readjusting +readjustment +readjustments +readjusts +readmission +readmit +readmits +readmitted +readmitting +readopt +readopted +readopting +readopts +readout +readouts +reads +ready +readying +reaffirm +reaffirmation +reaffirmations +reaffirmed +reaffirming +reaffirms +reagent +reagents +real +realer +reales +realest +realign +realigned +realigning +realignment +realignments +realigns +realism +realist +realistic +realistically +realists +realities +reality +realizable +realization +realize +realized +realizes +realizing +reallocate +reallocated +reallocates +reallocating +reallocation +really +realm +realms +realness +realpolitik +realtor +realtors +realty +ream +reamed +reamer +reamers +reaming +reams +reanalyses +reanalysis +reanalyze +reanalyzed +reanalyzes +reanalyzing +reanimate +reanimated +reanimates +reanimating +reanimation +reap +reaped +reaper +reapers +reaping +reappear +reappearance +reappearances +reappeared +reappearing +reappears +reapplication +reapplications +reapplied +reapplies +reapply +reapplying +reappoint +reappointed +reappointing +reappointment +reappoints +reapportion +reapportioned +reapportioning +reapportionment +reapportions +reappraisal +reappraisals +reappraise +reappraised +reappraises +reappraising +reaps +rear +reared +rearguard +rearguards +rearing +rearm +rearmament +rearmed +rearming +rearmost +rearms +rearrange +rearranged +rearrangement +rearrangements +rearranges +rearranging +rearrest +rearrested +rearresting +rearrests +rears +rearward +rearwards +reascend +reascended +reascending +reascends +reason +reasonable +reasonableness +reasonably +reasoned +reasoner +reasoners +reasoning +reasons +reassemble +reassembled +reassembles +reassembling +reassembly +reassert +reasserted +reasserting +reassertion +reasserts +reassess +reassessed +reassesses +reassessing +reassessment +reassessments +reassign +reassigned +reassigning +reassignment +reassignments +reassigns +reassurance +reassurances +reassure +reassured +reassures +reassuring +reassuringly +reattach +reattached +reattaches +reattaching +reattachment +reattain +reattained +reattaining +reattains +reattempt +reattempted +reattempting +reattempts +reauthorize +reauthorized +reauthorizes +reauthorizing +reawaken +reawakened +reawakening +reawakens +rebate +rebated +rebates +rebating +rebel +rebelled +rebelling +rebellion +rebellions +rebellious +rebelliously +rebelliousness +rebels +rebid +rebidding +rebids +rebind +rebinding +rebinds +rebirth +rebirths +reboil +reboiled +reboiling +reboils +reboot +rebooted +rebooting +reboots +reborn +rebound +rebounded +rebounding +rebounds +rebroadcast +rebroadcasted +rebroadcasting +rebroadcasts +rebuff +rebuffed +rebuffing +rebuffs +rebuild +rebuilding +rebuilds +rebuilt +rebuke +rebuked +rebukes +rebuking +rebukingly +reburial +reburials +reburied +reburies +rebury +reburying +rebus +rebuses +rebut +rebuts +rebuttal +rebuttals +rebutted +rebutting +recalcitrance +recalcitrant +recalculate +recalculated +recalculates +recalculating +recalculation +recalculations +recall +recalled +recalling +recalls +recant +recantation +recantations +recanted +recanting +recants +recap +recapitalize +recapitalized +recapitalizes +recapitalizing +recapitulate +recapitulated +recapitulates +recapitulating +recapitulation +recapitulations +recapped +recapping +recaps +recapture +recaptured +recaptures +recapturing +recast +recasting +recastings +recasts +recede +receded +recedes +receding +receipt +receipted +receipting +receipts +receivable +receivables +receive +received +receiver +receivers +receivership +receives +receiving +recent +recenter +recentest +recently +recentness +receptacle +receptacles +reception +receptionist +receptionists +receptions +receptive +receptively +receptiveness +receptivity +receptor +receptors +recess +recessed +recesses +recessing +recession +recessional +recessionals +recessionary +recessions +recessive +recessives +recharge +rechargeable +recharged +recharges +recharging +recharter +rechartered +rechartering +recharters +recheck +rechecked +rechecking +rechecks +recherche +rechristen +rechristened +rechristening +rechristens +recidivism +recidivist +recidivists +recipe +recipes +recipient +recipients +reciprocal +reciprocally +reciprocals +reciprocate +reciprocated +reciprocates +reciprocating +reciprocation +reciprocity +recirculate +recirculated +recirculates +recirculating +recital +recitalist +recitalists +recitals +recitation +recitations +recitative +recitatives +recite +recited +reciter +reciters +recites +reciting +reckless +recklessly +recklessness +reckon +reckoned +reckoning +reckonings +reckons +reclaim +reclaimable +reclaimed +reclaiming +reclaims +reclamation +reclassification +reclassified +reclassifies +reclassify +reclassifying +recline +reclined +recliner +recliners +reclines +reclining +recluse +recluses +reclusive +recognition +recognizable +recognizably +recognizance +recognize +recognized +recognizes +recognizing +recoil +recoiled +recoiling +recoils +recollect +recollected +recollecting +recollection +recollections +recollects +recolonization +recolonize +recolonized +recolonizes +recolonizing +recolor +recolored +recoloring +recolors +recombine +recombined +recombines +recombining +recommence +recommenced +recommencement +recommences +recommencing +recommend +recommendable +recommendation +recommendations +recommended +recommending +recommends +recommission +recommissioned +recommissioning +recommissions +recommit +recommits +recommitted +recommitting +recompense +recompensed +recompenses +recompensing +recompose +recomposed +recomposes +recomposing +recompute +recomputed +recomputes +recomputing +reconcilable +reconcile +reconciled +reconciles +reconciliation +reconciliations +reconciling +recondite +recondition +reconditioned +reconditioning +reconditions +reconfirm +reconfirmation +reconfirmations +reconfirmed +reconfirming +reconfirms +reconnaissance +reconnaissances +reconnect +reconnected +reconnecting +reconnects +reconnoiter +reconnoitered +reconnoitering +reconnoiters +reconnoitre +reconnoitred +reconnoitres +reconnoitring +reconquer +reconquered +reconquering +reconquers +reconquest +reconsecrate +reconsecrated +reconsecrates +reconsecrating +reconsecration +reconsider +reconsideration +reconsidered +reconsidering +reconsiders +reconsign +reconsigned +reconsigning +reconsigns +reconstitute +reconstituted +reconstitutes +reconstituting +reconstitution +reconstruct +reconstructed +reconstructing +reconstruction +reconstructions +reconstructs +recontact +recontacted +recontacting +recontacts +recontaminate +recontaminated +recontaminates +recontaminating +reconvene +reconvened +reconvenes +reconvening +reconvert +reconverted +reconverting +reconverts +recook +recooked +recooking +recooks +recopied +recopies +recopy +recopying +record +recorded +recorder +recorders +recording +recordings +records +recount +recounted +recounting +recounts +recoup +recouped +recouping +recoups +recourse +recover +recoverable +recovered +recoveries +recovering +recovers +recovery +recreant +recreants +recreate +recreated +recreates +recreating +recreation +recreational +recreations +recriminate +recriminated +recriminates +recriminating +recrimination +recriminations +recriminatory +recross +recrossed +recrosses +recrossing +recrudesce +recrudesced +recrudescence +recrudescent +recrudesces +recrudescing +recruit +recruited +recruiter +recruiters +recruiting +recruitment +recruits +recrystallize +recrystallized +recrystallizes +recrystallizing +recta +rectal +rectally +rectangle +rectangles +rectangular +rectifiable +rectification +rectifications +rectified +rectifier +rectifiers +rectifies +rectify +rectifying +rectilinear +rectitude +recto +rector +rectories +rectors +rectory +rectos +rectum +rectums +recumbent +recuperate +recuperated +recuperates +recuperating +recuperation +recuperative +recur +recurred +recurrence +recurrences +recurrent +recurrently +recurring +recurs +recyclable +recyclables +recycle +recycled +recycles +recycling +redact +redacted +redacting +redaction +redactor +redactors +redacts +redbird +redbirds +redbreast +redbreasts +redcap +redcaps +redcoat +redcoats +redden +reddened +reddening +reddens +redder +reddest +reddish +redecorate +redecorated +redecorates +redecorating +redecoration +rededicate +rededicated +rededicates +rededicating +redeem +redeemable +redeemed +redeemer +redeemers +redeeming +redeems +redefine +redefined +redefines +redefining +redefinition +redeliver +redelivered +redelivering +redelivers +redemption +redemptive +redeploy +redeployed +redeploying +redeployment +redeploys +redeposit +redeposited +redepositing +redeposits +redesign +redesigned +redesigning +redesigns +redetermine +redetermined +redetermines +redetermining +redevelop +redeveloped +redeveloping +redevelopment +redevelopments +redevelops +redhead +redheaded +redheads +redial +redialed +redialing +redialled +redialling +redials +redid +redirect +redirected +redirecting +redirects +rediscover +rediscovered +rediscovering +rediscovers +rediscovery +redissolve +redissolved +redissolves +redissolving +redistribute +redistributed +redistributes +redistributing +redistribution +redistrict +redistricted +redistricting +redistricts +redivide +redivided +redivides +redividing +redlining +redneck +rednecks +redness +redo +redoes +redoing +redolence +redolent +redone +redouble +redoubled +redoubles +redoubling +redoubt +redoubtable +redoubtably +redoubts +redound +redounded +redounding +redounds +redraft +redrafted +redrafting +redrafts +redraw +redrawing +redrawn +redraws +redress +redressed +redresses +redressing +redrew +reds +redskin +redskins +reduce +reduced +reducer +reducers +reduces +reducible +reducing +reduction +reductions +reductive +redundancies +redundancy +redundant +redundantly +reduplicate +reduplicated +reduplicates +reduplicating +reduplication +redwood +redwoods +redye +redyed +redyeing +redyes +reecho +reechoed +reechoes +reechoing +reed +reedier +reediest +reediness +reedit +reedited +reediting +reedits +reeds +reeducate +reeducated +reeducates +reeducating +reeducation +reedy +reef +reefed +reefer +reefers +reefing +reefs +reek +reeked +reeking +reeks +reel +reelect +reelected +reelecting +reelection +reelections +reelects +reeled +reeling +reels +reembark +reembarked +reembarking +reembarks +reembodied +reembodies +reembody +reembodying +reemerge +reemerged +reemergence +reemerges +reemerging +reemphasize +reemphasized +reemphasizes +reemphasizing +reemploy +reemployed +reemploying +reemployment +reemploys +reenact +reenacted +reenacting +reenactment +reenactments +reenacts +reenforce +reenforced +reenforces +reenforcing +reengage +reengaged +reengages +reengaging +reenlist +reenlisted +reenlisting +reenlistment +reenlists +reenter +reentered +reentering +reenters +reentries +reentry +reequip +reequipped +reequipping +reequips +reestablish +reestablished +reestablishes +reestablishing +reestablishment +reevaluate +reevaluated +reevaluates +reevaluating +reevaluation +reevaluations +reeve +reeved +reeves +reeving +reexamination +reexaminations +reexamine +reexamined +reexamines +reexamining +reexplain +reexplained +reexplaining +reexplains +reexport +reexported +reexporting +reexports +reface +refaced +refaces +refacing +refashion +refashioned +refashioning +refashions +refasten +refastened +refastening +refastens +refection +refectories +refectory +refer +referable +referee +refereed +refereeing +referees +reference +referenced +references +referencing +referenda +referendum +referendums +referent +referents +referral +referrals +referred +referrer +referrers +referring +refers +reffed +reffing +refile +refiled +refiles +refiling +refill +refillable +refilled +refilling +refills +refinance +refinanced +refinances +refinancing +refine +refined +refinement +refinements +refiner +refineries +refiners +refinery +refines +refining +refinish +refinished +refinishes +refinishing +refit +refits +refitted +refitting +reflect +reflected +reflecting +reflection +reflections +reflective +reflectively +reflector +reflectors +reflects +reflex +reflexes +reflexion +reflexions +reflexive +reflexively +reflexives +refocus +refocused +refocuses +refocusing +refocussed +refocusses +refocussing +refold +refolded +refolding +refolds +reforest +reforestation +reforested +reforesting +reforests +reforge +reforged +reforges +reforging +reform +reformation +reformations +reformative +reformatories +reformatory +reformed +reformer +reformers +reforming +reforms +reformulate +reformulated +reformulates +reformulating +reformulation +reformulations +refortified +refortifies +refortify +refortifying +refract +refracted +refracting +refraction +refractive +refractories +refractory +refracts +refrain +refrained +refraining +refrains +refreeze +refreezes +refreezing +refresh +refreshed +refresher +refreshers +refreshes +refreshing +refreshingly +refreshment +refreshments +refrigerant +refrigerants +refrigerate +refrigerated +refrigerates +refrigerating +refrigeration +refrigerator +refrigerators +refroze +refrozen +refs +refuel +refueled +refueling +refuelled +refuelling +refuels +refuge +refugee +refugees +refuges +refulgence +refulgent +refund +refundable +refunded +refunding +refunds +refurbish +refurbished +refurbishes +refurbishing +refurbishment +refurbishments +refurnish +refurnished +refurnishes +refurnishing +refusal +refusals +refuse +refused +refuses +refusing +refutable +refutation +refutations +refute +refuted +refuter +refuters +refutes +refuting +regain +regained +regaining +regains +regal +regale +regaled +regalement +regales +regalia +regaling +regally +regard +regarded +regarding +regardless +regards +regather +regathered +regathering +regathers +regatta +regattas +regencies +regency +regeneracy +regenerate +regenerated +regenerates +regenerating +regeneration +regenerative +regent +regents +reggae +regicide +regicides +regime +regimen +regimens +regiment +regimental +regimentation +regimented +regimenting +regiments +regimes +region +regional +regionalism +regionalisms +regionally +regions +register +registered +registering +registers +registrant +registrants +registrar +registrars +registration +registrations +registries +registry +regnant +regrade +regraded +regrades +regrading +regress +regressed +regresses +regressing +regression +regressions +regressive +regret +regretful +regretfully +regrets +regrettable +regrettably +regretted +regretting +regrew +regrind +regrinding +regrinds +reground +regroup +regrouped +regrouping +regroups +regrow +regrowing +regrown +regrows +regrowth +regular +regularise +regularised +regularises +regularising +regularity +regularization +regularize +regularized +regularizes +regularizing +regularly +regulars +regulate +regulated +regulates +regulating +regulation +regulations +regulative +regulator +regulators +regulatory +regurgitate +regurgitated +regurgitates +regurgitating +regurgitation +rehab +rehabbed +rehabbing +rehabilitate +rehabilitated +rehabilitates +rehabilitating +rehabilitation +rehabilitative +rehabs +rehang +rehanged +rehanging +rehangs +rehash +rehashed +rehashes +rehashing +rehear +reheard +rehearing +rehearings +rehears +rehearsal +rehearsals +rehearse +rehearsed +rehearses +rehearsing +reheat +reheated +reheating +reheats +rehire +rehired +rehires +rehiring +rehouse +rehoused +rehouses +rehousing +rehung +reign +reigned +reigning +reignite +reignited +reignites +reigniting +reigns +reimbursable +reimburse +reimbursed +reimbursement +reimbursements +reimburses +reimbursing +reimpose +reimposed +reimposes +reimposing +rein +reincarnate +reincarnated +reincarnates +reincarnating +reincarnation +reincarnations +reincorporate +reincorporated +reincorporates +reincorporating +reincorporation +reindeer +reindeers +reined +reinfect +reinfected +reinfecting +reinfection +reinfections +reinfects +reinforce +reinforced +reinforcement +reinforcements +reinforces +reinforcing +reining +reinoculate +reinoculated +reinoculates +reinoculating +reins +reinsert +reinserted +reinserting +reinsertion +reinserts +reinspect +reinspected +reinspecting +reinspects +reinstate +reinstated +reinstatement +reinstates +reinstating +reintegrate +reintegrated +reintegrates +reintegrating +reintegration +reinterpret +reinterpretation +reinterpretations +reinterpreted +reinterpreting +reinterprets +reintroduce +reintroduced +reintroduces +reintroducing +reintroduction +reinvent +reinvented +reinventing +reinvention +reinventions +reinvents +reinvest +reinvested +reinvesting +reinvestment +reinvests +reinvigorate +reinvigorated +reinvigorates +reinvigorating +reissue +reissued +reissues +reissuing +reiterate +reiterated +reiterates +reiterating +reiteration +reiterations +reiterative +reject +rejected +rejecting +rejection +rejections +rejects +rejoice +rejoiced +rejoices +rejoicing +rejoicings +rejoin +rejoinder +rejoinders +rejoined +rejoining +rejoins +rejudge +rejudged +rejudges +rejudging +rejuvenate +rejuvenated +rejuvenates +rejuvenating +rejuvenation +rekindle +rekindled +rekindles +rekindling +relabel +relabeled +relabeling +relabelled +relabelling +relabels +relaid +relapse +relapsed +relapses +relapsing +relate +related +relatedness +relater +relaters +relates +relating +relation +relational +relations +relationship +relationships +relative +relatively +relatives +relativism +relativity +relaunch +relaunched +relaunches +relaunching +relax +relaxant +relaxants +relaxation +relaxations +relaxed +relaxer +relaxers +relaxes +relaxing +relay +relayed +relaying +relays +relearn +relearned +relearning +relearns +release +released +releases +releasing +relegate +relegated +relegates +relegating +relegation +relent +relented +relenting +relentless +relentlessly +relentlessness +relents +relevance +relevancy +relevant +relevantly +reliability +reliable +reliably +reliance +reliant +relic +relics +relied +relief +reliefs +relies +relieve +relieved +reliever +relievers +relieves +relieving +relight +relighted +relighting +relights +religion +religions +religious +religiously +religiousness +reline +relined +relines +relining +relinquish +relinquished +relinquishes +relinquishing +relinquishment +reliquaries +reliquary +relish +relished +relishes +relishing +relit +relivable +relive +relived +relives +reliving +reload +reloaded +reloading +reloads +relocate +relocated +relocates +relocating +relocation +reluctance +reluctant +reluctantly +rely +relying +remade +remain +remainder +remainders +remained +remaining +remains +remake +remakes +remaking +remand +remanded +remanding +remands +remap +remapped +remapping +remaps +remark +remarkable +remarkableness +remarkably +remarked +remarking +remarks +remarriage +remarriages +remarried +remarries +remarry +remarrying +rematch +rematches +remeasure +remeasured +remeasures +remeasuring +remediable +remedial +remedially +remediation +remedied +remedies +remedy +remedying +remelt +remelted +remelting +remelts +remember +remembered +remembering +remembers +remembrance +remembrances +remigrate +remigrated +remigrates +remigrating +remind +reminded +reminder +reminders +reminding +reminds +reminisce +reminisced +reminiscence +reminiscences +reminiscent +reminiscently +reminisces +reminiscing +remiss +remission +remissions +remissly +remissness +remit +remits +remittance +remittances +remitted +remitting +remix +remixed +remixes +remixing +remnant +remnants +remodel +remodeled +remodeling +remodelled +remodelling +remodels +remold +remolded +remolding +remolds +remonstrance +remonstrances +remonstrant +remonstrants +remonstrate +remonstrated +remonstrates +remonstrating +remorse +remorseful +remorsefully +remorseless +remorselessly +remorselessness +remote +remotely +remoteness +remoter +remotes +remotest +remount +remounted +remounting +remounts +removable +removal +removals +remove +removed +remover +removers +removes +removing +rems +remunerate +remunerated +remunerates +remunerating +remuneration +remunerations +remunerative +renaissance +renaissances +renal +rename +renamed +renames +renaming +renascence +renascences +renascent +rend +render +rendered +rendering +renderings +renders +rendezvous +rendezvoused +rendezvouses +rendezvousing +rending +rendition +renditions +rends +renegade +renegades +renege +reneged +reneger +renegers +reneges +reneging +renegotiable +renegotiate +renegotiated +renegotiates +renegotiating +renegotiation +renew +renewable +renewal +renewals +renewed +renewing +renews +rennet +rennin +renominate +renominated +renominates +renominating +renomination +renounce +renounced +renouncement +renounces +renouncing +renovate +renovated +renovates +renovating +renovation +renovations +renovator +renovators +renown +renowned +rent +rental +rentals +rented +renter +renters +renting +rents +renumber +renumbered +renumbering +renumbers +renunciation +renunciations +reoccupation +reoccupied +reoccupies +reoccupy +reoccupying +reoccur +reoccurred +reoccurring +reoccurs +reopen +reopened +reopening +reopens +reorder +reordered +reordering +reorders +reorganization +reorganizations +reorganize +reorganized +reorganizes +reorganizing +reorient +reorientation +reoriented +reorienting +reorients +repack +repackage +repackaged +repackages +repackaging +repacked +repacking +repacks +repaid +repaint +repainted +repainting +repaints +repair +repairable +repaired +repairer +repairers +repairing +repairman +repairmen +repairs +reparable +reparation +reparations +repartee +repast +repasts +repatriate +repatriated +repatriates +repatriating +repatriation +repave +repaved +repaves +repaving +repay +repayable +repaying +repayment +repayments +repays +repeal +repealed +repealing +repeals +repeat +repeatable +repeated +repeatedly +repeater +repeaters +repeating +repeats +repel +repellant +repellants +repelled +repellent +repellents +repelling +repels +repent +repentance +repentant +repentantly +repented +repenting +repents +repercussion +repercussions +repertoire +repertoires +repertories +repertory +repetition +repetitions +repetitious +repetitiously +repetitiousness +repetitive +repetitively +repetitiveness +rephotograph +rephotographed +rephotographing +rephotographs +rephrase +rephrased +rephrases +rephrasing +repine +repined +repines +repining +replace +replaceable +replaced +replacement +replacements +replaces +replacing +replant +replanted +replanting +replants +replay +replayed +replaying +replays +replenish +replenished +replenishes +replenishing +replenishment +replete +repleteness +repletion +replica +replicas +replicate +replicated +replicates +replicating +replication +replications +replied +replies +reply +replying +repopulate +repopulated +repopulates +repopulating +report +reportage +reported +reportedly +reporter +reporters +reporting +reportorial +reports +repose +reposed +reposeful +reposes +reposing +repositories +repository +repossess +repossessed +repossesses +repossessing +repossession +repossessions +reprehend +reprehended +reprehending +reprehends +reprehensibility +reprehensible +reprehensibly +reprehension +represent +representation +representational +representations +representative +representatives +represented +representing +represents +repress +repressed +represses +repressing +repression +repressions +repressive +repressively +reprice +repriced +reprices +repricing +reprieve +reprieved +reprieves +reprieving +reprimand +reprimanded +reprimanding +reprimands +reprint +reprinted +reprinting +reprints +reprisal +reprisals +reprise +reprised +reprises +reprising +reproach +reproachable +reproached +reproaches +reproachful +reproachfully +reproaching +reprobate +reprobates +reprocess +reprocessed +reprocesses +reprocessing +reproduce +reproduced +reproducer +reproducers +reproduces +reproducible +reproducing +reproduction +reproductions +reproductive +reprogram +reprogramed +reprograming +reprogrammed +reprogramming +reprograms +reproof +reproofs +reprove +reproved +reproves +reproving +reprovingly +reps +reptile +reptiles +reptilian +reptilians +republic +republican +republicanism +republicans +republication +republications +republics +republish +republished +republishes +republishing +repudiate +repudiated +repudiates +repudiating +repudiation +repudiations +repudiator +repudiators +repugnance +repugnant +repulse +repulsed +repulses +repulsing +repulsion +repulsive +repulsively +repulsiveness +repurchase +repurchased +repurchases +repurchasing +reputability +reputable +reputably +reputation +reputations +repute +reputed +reputedly +reputes +reputing +request +requested +requesting +requests +requiem +requiems +require +required +requirement +requirements +requires +requiring +requisite +requisites +requisition +requisitioned +requisitioning +requisitions +requital +requite +requited +requiter +requiters +requites +requiting +reran +reread +rereading +rereads +rerecord +rerecorded +rerecording +rerecords +reroute +rerouted +reroutes +rerouting +rerun +rerunning +reruns +resalable +resale +resales +reschedule +rescheduled +reschedules +rescheduling +rescind +rescinded +rescinding +rescinds +rescission +rescue +rescued +rescuer +rescuers +rescues +rescuing +reseal +resealable +resealed +resealing +reseals +research +researched +researcher +researchers +researches +researching +resection +resections +reseed +reseeded +reseeding +reseeds +resell +reselling +resells +resemblance +resemblances +resemble +resembled +resembles +resembling +resent +resented +resentful +resentfully +resentfulness +resenting +resentment +resentments +resents +reserpine +reservation +reservations +reserve +reserved +reservedly +reservedness +reserves +reserving +reservist +reservists +reservoir +reservoirs +reset +resets +resetting +resettle +resettled +resettlement +resettles +resettling +resew +resewed +resewing +resewn +resews +reshape +reshaped +reshapes +reshaping +resharpen +resharpened +resharpening +resharpens +reship +reshipment +reshipped +reshipping +reships +reshuffle +reshuffled +reshuffles +reshuffling +reside +resided +residence +residences +residencies +residency +resident +residential +residents +resides +residing +residual +residuals +residue +residues +residuum +resign +resignation +resignations +resigned +resignedly +resigning +resigns +resilience +resiliency +resilient +resiliently +resin +resinous +resins +resist +resistance +resistances +resistant +resisted +resister +resisters +resistible +resisting +resistless +resistor +resistors +resists +resold +resole +resoled +resoles +resoling +resolute +resolutely +resoluteness +resolution +resolutions +resolvable +resolve +resolved +resolves +resolving +resonance +resonances +resonant +resonantly +resonate +resonated +resonates +resonating +resonator +resonators +resorption +resort +resorted +resorting +resorts +resound +resounded +resounding +resoundingly +resounds +resource +resourced +resourceful +resourcefully +resourcefulness +resources +resourcing +resow +resowed +resowing +resown +resows +respect +respectability +respectable +respectably +respected +respecter +respecters +respectful +respectfully +respectfulness +respecting +respective +respectively +respects +respell +respelled +respelling +respells +respelt +respiration +respirator +respirators +respiratory +respire +respired +respires +respiring +respite +respites +resplendence +resplendent +resplendently +respond +responded +respondent +respondents +responding +responds +response +responses +responsibilities +responsibility +responsible +responsibly +responsive +responsively +responsiveness +respray +resprayed +respraying +resprays +rest +restaff +restaffed +restaffing +restaffs +restart +restarted +restarting +restarts +restate +restated +restatement +restatements +restates +restating +restaurant +restauranteur +restauranteurs +restaurants +restaurateur +restaurateurs +rested +restful +restfuller +restfullest +restfully +restfulness +resting +restitch +restitched +restitches +restitching +restitution +restive +restively +restiveness +restless +restlessly +restlessness +restock +restocked +restocking +restocks +restoration +restorations +restorative +restoratives +restore +restored +restorer +restorers +restores +restoring +restrain +restrained +restrainer +restrainers +restraining +restrains +restraint +restraints +restrengthen +restrengthened +restrengthening +restrengthens +restrict +restricted +restricting +restriction +restrictions +restrictive +restrictively +restrictiveness +restricts +restring +restringing +restrings +restroom +restrooms +restructure +restructured +restructures +restructuring +restructurings +restrung +rests +restudied +restudies +restudy +restudying +restyle +restyled +restyles +restyling +resubmit +resubmits +resubmitted +resubmitting +resubscribe +resubscribed +resubscribes +resubscribing +result +resultant +resultants +resulted +resulting +results +resume +resumed +resumes +resuming +resumption +resumptions +resupplied +resupplies +resupply +resupplying +resurface +resurfaced +resurfaces +resurfacing +resurgence +resurgences +resurgent +resurrect +resurrected +resurrecting +resurrection +resurrections +resurrects +resurvey +resurveyed +resurveying +resurveys +resuscitate +resuscitated +resuscitates +resuscitating +resuscitation +resuscitator +resuscitators +retail +retailed +retailer +retailers +retailing +retails +retain +retained +retainer +retainers +retaining +retains +retake +retaken +retakes +retaking +retaliate +retaliated +retaliates +retaliating +retaliation +retaliations +retaliative +retaliatory +retard +retardant +retardants +retardation +retarded +retarder +retarders +retarding +retards +retaught +retch +retched +retches +retching +reteach +reteaches +reteaching +retell +retelling +retells +retention +retentive +retentively +retentiveness +retest +retested +retesting +retests +rethink +rethinking +rethinks +rethought +reticence +reticent +reticently +reticulation +reticulations +retie +retied +reties +retina +retinae +retinal +retinas +retinue +retinues +retire +retired +retiree +retirees +retirement +retirements +retires +retiring +retold +retook +retool +retooled +retooling +retools +retort +retorted +retorting +retorts +retouch +retouched +retouches +retouching +retrace +retraced +retraces +retracing +retract +retractable +retracted +retractile +retracting +retraction +retractions +retracts +retrain +retrained +retraining +retrains +retread +retreaded +retreading +retreads +retreat +retreated +retreating +retreats +retrench +retrenched +retrenches +retrenching +retrenchment +retrenchments +retrial +retrials +retribution +retributions +retributive +retried +retries +retrievable +retrieval +retrievals +retrieve +retrieved +retriever +retrievers +retrieves +retrieving +retro +retroactive +retroactively +retrod +retrodden +retrofire +retrofired +retrofires +retrofiring +retrofit +retrofits +retrofitted +retrofitting +retrograde +retrograded +retrogrades +retrograding +retrogress +retrogressed +retrogresses +retrogressing +retrogression +retrogressive +retrorocket +retrorockets +retros +retrospect +retrospected +retrospecting +retrospection +retrospective +retrospectively +retrospectives +retrospects +retrovirus +retroviruses +retry +retrying +retsina +return +returnable +returnables +returned +returnee +returnees +returner +returners +returning +returns +retying +retype +retyped +retypes +retyping +reunification +reunified +reunifies +reunify +reunifying +reunion +reunions +reunite +reunited +reunites +reuniting +reupholster +reupholstered +reupholstering +reupholsters +reusable +reuse +reused +reuses +reusing +revaluation +revaluations +revalue +revalued +revalues +revaluing +revamp +revamped +revamping +revampings +revamps +reveal +revealed +revealing +revealingly +reveals +reveille +revel +revelation +revelations +reveled +reveler +revelers +reveling +revelled +reveller +revellers +revelling +revelries +revelry +revels +revenge +revenged +revengeful +revengefully +revenges +revenging +revenue +revenuer +revenuers +revenues +reverberate +reverberated +reverberates +reverberating +reverberation +reverberations +revere +revered +reverence +reverenced +reverences +reverencing +reverend +reverends +reverent +reverential +reverentially +reverently +reveres +reverie +reveries +revering +revers +reversal +reversals +reverse +reversed +reversely +reverses +reversible +reversibly +reversing +reversion +revert +reverted +revertible +reverting +reverts +revery +revetment +revetments +review +reviewed +reviewer +reviewers +reviewing +reviews +revile +reviled +revilement +reviler +revilers +reviles +reviling +revise +revised +reviser +revisers +revises +revising +revision +revisionism +revisionist +revisionists +revisions +revisit +revisited +revisiting +revisits +revitalization +revitalize +revitalized +revitalizes +revitalizing +revival +revivalism +revivalist +revivalists +revivals +revive +revived +revives +revivification +revivified +revivifies +revivify +revivifying +reviving +revocable +revocation +revocations +revokable +revoke +revoked +revokes +revoking +revolt +revolted +revolting +revoltingly +revolts +revolution +revolutionaries +revolutionary +revolutionise +revolutionised +revolutionises +revolutionising +revolutionist +revolutionists +revolutionize +revolutionized +revolutionizes +revolutionizing +revolutions +revolvable +revolve +revolved +revolver +revolvers +revolves +revolving +revs +revue +revues +revulsion +revved +revving +reward +rewarded +rewarding +rewards +rewarm +rewarmed +rewarming +rewarms +rewash +rewashed +rewashes +rewashing +reweave +reweaved +reweaves +reweaving +rewed +rewedded +rewedding +reweds +reweigh +reweighed +reweighing +reweighs +rewind +rewinding +rewinds +rewire +rewired +rewires +rewiring +reword +reworded +rewording +rewords +rework +reworked +reworking +reworks +rewound +rewove +rewoven +rewrite +rewrites +rewriting +rewritten +rewrote +rezone +rezoned +rezones +rezoning +rhapsodic +rhapsodical +rhapsodies +rhapsodize +rhapsodized +rhapsodizes +rhapsodizing +rhapsody +rhea +rheas +rhenium +rheostat +rheostats +rhesus +rhesuses +rhetoric +rhetorical +rhetorically +rhetorician +rhetoricians +rheum +rheumatic +rheumatically +rheumatics +rheumatism +rheumatoid +rheumier +rheumiest +rheumy +rhinestone +rhinestones +rhinitis +rhino +rhinoceri +rhinoceros +rhinoceroses +rhinos +rhizome +rhizomes +rhodium +rhododendron +rhododendrons +rhombi +rhomboid +rhomboidal +rhomboids +rhombus +rhombuses +rhos +rhubarb +rhubarbs +rhyme +rhymed +rhymer +rhymers +rhymes +rhymester +rhymesters +rhyming +rhythm +rhythmic +rhythmical +rhythmically +rhythms +rial +rials +ribald +ribaldry +ribbed +ribber +ribbers +ribbing +ribbon +ribbons +riboflavin +ribs +rice +riced +ricer +ricers +rices +rich +richer +riches +richest +richly +richness +ricing +rick +ricked +ricketier +ricketiest +rickets +rickety +ricking +rickrack +ricks +ricksha +rickshas +rickshaw +rickshaws +ricochet +ricocheted +ricocheting +ricochets +ricochetted +ricochetting +ricotta +riddance +ridded +ridden +ridding +riddle +riddled +riddles +riddling +ride +rider +riderless +riders +ridership +rides +ridge +ridged +ridgepole +ridgepoles +ridges +ridgier +ridgiest +ridging +ridgy +ridicule +ridiculed +ridicules +ridiculing +ridiculous +ridiculously +ridiculousness +riding +rids +rife +rifer +rifest +riff +riffed +riffing +riffle +riffled +riffles +riffling +riffraff +riffs +rifle +rifled +rifleman +riflemen +rifler +riflers +rifles +rifling +rift +rifted +rifting +rifts +rigamarole +rigamaroles +rigatoni +rigged +rigger +riggers +rigging +right +righted +righteous +righteously +righteousness +righter +rightest +rightful +rightfully +rightfulness +righting +rightism +rightist +rightists +rightly +rightmost +rightness +rights +rightsize +rightsized +rightsizes +rightsizing +rightward +rightwards +rigid +rigidity +rigidly +rigidness +rigmarole +rigmaroles +rigor +rigorous +rigorously +rigorousness +rigors +rigour +rigours +rigs +rile +riled +riles +riling +rill +rills +rime +rimed +rimes +riming +rimless +rimmed +rimming +rims +rind +rinds +ring +ringed +ringer +ringers +ringgit +ringgits +ringing +ringleader +ringleaders +ringlet +ringlets +ringlike +ringmaster +ringmasters +rings +ringside +ringworm +rink +rinks +rinse +rinsed +rinses +rinsing +riot +rioted +rioter +rioters +rioting +riotous +riotously +riots +riparian +ripcord +ripcords +ripe +ripely +ripen +ripened +ripeness +ripening +ripens +riper +ripest +ripoff +ripoffs +ripost +riposte +riposted +ripostes +riposting +riposts +ripped +ripper +rippers +ripping +ripple +rippled +ripples +rippling +ripply +rips +ripsaw +ripsaws +riptide +riptides +rise +risen +riser +risers +rises +risibility +risible +rising +risings +risk +risked +riskier +riskiest +riskily +riskiness +risking +risks +risky +risotto +risottos +risque +rite +rites +ritual +ritualism +ritualistic +ritualistically +ritually +rituals +ritzier +ritziest +ritzy +rival +rivaled +rivaling +rivalled +rivalling +rivalries +rivalry +rivals +rive +rived +riven +river +riverbank +riverbanks +riverbed +riverbeds +riverboat +riverboats +rivers +riverside +riversides +rives +rivet +riveted +riveter +riveters +riveting +rivets +rivetted +rivetting +riving +rivulet +rivulets +riyal +riyals +roach +roaches +road +roadbed +roadbeds +roadblock +roadblocked +roadblocking +roadblocks +roadhouse +roadhouses +roadie +roadies +roadkill +roadrunner +roadrunners +roads +roadshow +roadshows +roadside +roadsides +roadster +roadsters +roadway +roadways +roadwork +roadworthy +roam +roamed +roamer +roamers +roaming +roams +roan +roans +roar +roared +roarer +roarers +roaring +roars +roast +roasted +roaster +roasters +roasting +roastings +roasts +robbed +robber +robberies +robbers +robbery +robbing +robe +robed +robes +robin +robing +robins +robot +robotic +robotics +robotize +robotized +robotizes +robotizing +robots +robs +robust +robuster +robustest +robustly +robustness +rock +rockabilly +rockbound +rocked +rocker +rockers +rocket +rocketed +rocketing +rocketry +rockets +rockfall +rockfalls +rockier +rockiest +rockiness +rocking +rocks +rocky +rococo +rode +rodent +rodents +rodeo +rodeos +rods +roebuck +roebucks +roentgen +roentgens +roes +roger +rogered +rogering +rogers +rogue +roguery +rogues +roguish +roguishly +roguishness +roil +roiled +roiling +roils +roister +roistered +roisterer +roisterers +roistering +roisters +role +roles +roll +rollback +rollbacks +rolled +roller +rollers +rollerskating +rollick +rollicked +rollicking +rollicks +rolling +rollover +rollovers +rolls +romaine +roman +romance +romanced +romancer +romancers +romances +romancing +romantic +romantically +romanticism +romanticist +romanticists +romanticize +romanticized +romanticizes +romanticizing +romantics +romeo +romeos +romp +romped +romper +rompers +romping +romps +rondo +rondos +rood +roods +roof +roofed +roofer +roofers +roofing +roofless +roofs +rooftop +rooftops +rook +rooked +rookeries +rookery +rookie +rookies +rooking +rooks +room +roomed +roomer +roomers +roomette +roomettes +roomful +roomfuls +roomier +roomiest +roominess +rooming +roommate +roommates +rooms +roomy +roost +roosted +rooster +roosters +roosting +roosts +root +rooted +rooter +rooters +rooting +rootless +rootlet +rootlets +roots +rope +roped +roper +ropers +ropes +ropier +ropiest +roping +ropy +rosaries +rosary +rose +roseate +rosebud +rosebuds +rosebush +rosebushes +rosemary +roses +rosette +rosettes +rosewater +rosewood +rosewoods +rosier +rosiest +rosily +rosin +rosined +rosiness +rosining +rosins +roster +rosters +rostra +rostrum +rostrums +rosy +rotaries +rotary +rotate +rotated +rotates +rotating +rotation +rotations +rotatory +rote +rotgut +rotisserie +rotisseries +rotogravure +rotogravures +rotor +rotors +rototiller +rototillers +rots +rotted +rotten +rottener +rottenest +rottenly +rottenness +rotting +rotund +rotunda +rotundas +rotundity +rotundness +rouble +roubles +roue +roues +rouge +rouged +rouges +rough +roughage +roughed +roughen +roughened +roughening +roughens +rougher +roughest +roughhouse +roughhoused +roughhouses +roughhousing +roughing +roughly +roughneck +roughnecked +roughnecking +roughnecks +roughness +roughs +roughshod +rouging +roulette +round +roundabout +roundabouts +rounded +roundelay +roundelays +rounder +roundest +roundhouse +roundhouses +rounding +roundish +roundly +roundness +rounds +roundtable +roundtables +roundup +roundups +roundworm +roundworms +rouse +roused +rouses +rousing +roust +roustabout +roustabouts +rousted +rousting +rousts +rout +route +routed +router +routers +routes +routine +routinely +routines +routing +routinize +routinized +routinizes +routinizing +routs +rove +roved +rover +rovers +roves +roving +rowboat +rowboats +rowdier +rowdies +rowdiest +rowdily +rowdiness +rowdy +rowdyism +rowed +rowel +roweled +roweling +rowelled +rowelling +rowels +rower +rowers +rowing +rows +royal +royalist +royalists +royally +royals +royalties +royalty +rubati +rubato +rubatos +rubbed +rubber +rubberier +rubberiest +rubberize +rubberized +rubberizes +rubberizing +rubberneck +rubbernecked +rubbernecker +rubberneckers +rubbernecking +rubbernecks +rubbers +rubbery +rubbing +rubbish +rubbishy +rubble +rubdown +rubdowns +rube +rubella +rubes +rubicund +rubidium +rubier +rubies +rubiest +ruble +rubles +rubric +rubrics +rubs +ruby +rucksack +rucksacks +ruckus +ruckuses +rudder +rudderless +rudders +ruddier +ruddiest +ruddiness +ruddy +rude +rudely +rudeness +ruder +rudest +rudiment +rudimentary +rudiments +rued +rueful +ruefully +ruefulness +rueing +rues +ruff +ruffed +ruffian +ruffianly +ruffians +ruffing +ruffle +ruffled +ruffles +ruffling +ruffly +ruffs +rugby +rugged +ruggeder +ruggedest +ruggedly +ruggedness +rugs +ruin +ruination +ruined +ruing +ruining +ruinous +ruinously +ruins +rule +ruled +ruler +rulers +rules +ruling +rulings +rumba +rumbaed +rumbaing +rumbas +rumble +rumbled +rumbles +rumbling +rumblings +ruminant +ruminants +ruminate +ruminated +ruminates +ruminating +rumination +ruminations +ruminative +rummage +rummaged +rummages +rummaging +rummer +rummest +rummy +rumor +rumored +rumoring +rumormonger +rumormongers +rumors +rumour +rumoured +rumouring +rumours +rump +rumple +rumpled +rumples +rumpling +rumply +rumps +rumpus +rumpuses +rums +runabout +runabouts +runaround +runarounds +runaway +runaways +rundown +rundowns +rune +runes +rung +rungs +runic +runlet +runlets +runnel +runnels +runner +runners +runnier +runniest +running +runny +runoff +runoffs +runs +runt +runtier +runtiest +runts +runty +runway +runways +rupee +rupees +rupiah +rupiahs +rupture +ruptured +ruptures +rupturing +rural +ruse +ruses +rush +rushed +rusher +rushers +rushes +rushing +rushy +rusk +rusks +russet +russets +rust +rusted +rustic +rustically +rusticate +rusticated +rusticates +rusticating +rustication +rusticity +rustics +rustier +rustiest +rustiness +rusting +rustle +rustled +rustler +rustlers +rustles +rustling +rustproof +rustproofed +rustproofing +rustproofs +rusts +rusty +rutabaga +rutabagas +ruthenium +rutherfordium +ruthless +ruthlessly +ruthlessness +ruts +rutted +ruttier +ruttiest +rutting +rutty +sabbath +sabbaths +sabbatical +sabbaticals +saber +sabers +sable +sables +sabot +sabotage +sabotaged +sabotages +sabotaging +saboteur +saboteurs +sabots +sabra +sabras +sabre +sabres +saccharin +saccharine +sacerdotal +sachem +sachems +sachet +sachets +sack +sackcloth +sacked +sacker +sackers +sackful +sackfuls +sacking +sacks +sacra +sacrament +sacramental +sacraments +sacred +sacredly +sacredness +sacrifice +sacrificed +sacrifices +sacrificial +sacrificially +sacrificing +sacrilege +sacrileges +sacrilegious +sacrilegiously +sacristan +sacristans +sacristies +sacristy +sacroiliac +sacroiliacs +sacrosanct +sacrosanctness +sacrum +sacs +sadden +saddened +saddening +saddens +sadder +saddest +saddle +saddlebag +saddlebags +saddled +saddles +saddling +sadism +sadist +sadistic +sadistically +sadists +sadly +sadness +sadomasochism +sadomasochist +sadomasochistic +sadomasochists +safari +safaried +safariing +safaris +safe +safeguard +safeguarded +safeguarding +safeguards +safekeeping +safely +safeness +safer +safes +safest +safeties +safety +safflower +safflowers +saffron +saffrons +saga +sagacious +sagaciously +sagacity +sagas +sage +sagebrush +sagely +sager +sages +sagest +sagged +saggier +saggiest +sagging +saggy +sago +sags +saguaro +saguaros +sahib +sahibs +said +sail +sailboard +sailboarder +sailboarders +sailboarding +sailboards +sailboat +sailboats +sailcloth +sailed +sailfish +sailfishes +sailing +sailings +sailor +sailors +sailplane +sailplanes +sails +saint +sainted +sainthood +saintlier +saintliest +saintlike +saintliness +saintly +saints +saith +sake +saki +salaam +salaamed +salaaming +salaams +salable +salacious +salaciously +salaciousness +salacity +salad +salads +salamander +salamanders +salami +salamis +salaried +salaries +salary +sale +saleable +sales +salesclerk +salesclerks +salesgirl +salesgirls +salesladies +saleslady +salesman +salesmanship +salesmen +salespeople +salesperson +salespersons +saleswoman +saleswomen +salience +salient +saliently +salients +saline +salines +salinity +saliva +salivary +salivate +salivated +salivates +salivating +salivation +sallied +sallies +sallow +sallower +sallowest +sallowness +sally +sallying +salmon +salmonella +salmonellae +salmonellas +salmons +salon +salons +saloon +saloons +salsa +salsas +salt +saltbox +saltboxes +saltcellar +saltcellars +salted +salter +saltest +saltier +saltiest +saltine +saltines +saltiness +salting +saltpeter +saltpetre +salts +saltshaker +saltshakers +saltwater +salty +salubrious +salutary +salutation +salutations +salutatorian +salutatorians +salutatory +salute +saluted +salutes +saluting +salvage +salvageable +salvaged +salvages +salvaging +salvation +salve +salved +salver +salvers +salves +salving +salvo +salvoes +salvos +samarium +samba +sambaed +sambaing +sambas +same +sameness +samovar +samovars +sampan +sampans +sample +sampled +sampler +samplers +samples +sampling +samurai +sanatoria +sanatorium +sanatoriums +sancta +sanctification +sanctified +sanctifies +sanctify +sanctifying +sanctimonious +sanctimoniously +sanctimoniousness +sanctimony +sanction +sanctioned +sanctioning +sanctions +sanctity +sanctuaries +sanctuary +sanctum +sanctums +sand +sandal +sandals +sandalwood +sandbag +sandbagged +sandbagging +sandbags +sandbank +sandbanks +sandbar +sandbars +sandblast +sandblasted +sandblaster +sandblasters +sandblasting +sandblasts +sandbox +sandboxes +sandcastle +sandcastles +sanded +sander +sanders +sandhog +sandhogs +sandier +sandiest +sandiness +sanding +sandlot +sandlots +sandlotter +sandlotters +sandman +sandmen +sandpaper +sandpapered +sandpapering +sandpapers +sandpiper +sandpipers +sands +sandstone +sandstorm +sandstorms +sandwich +sandwiched +sandwiches +sandwiching +sandy +sane +sanely +saneness +saner +sanest +sang +sangfroid +sangria +sanguinary +sanguine +sanguinely +sanitaria +sanitarian +sanitarians +sanitarium +sanitariums +sanitary +sanitation +sanitize +sanitized +sanitizes +sanitizing +sanity +sank +sans +sapience +sapient +sapless +sapling +saplings +sapped +sapphire +sapphires +sappier +sappiest +sappiness +sapping +sappy +saprophyte +saprophytes +saprophytic +saps +sapsucker +sapsuckers +sapwood +saran +sarape +sarapes +sarcasm +sarcasms +sarcastic +sarcastically +sarcoma +sarcomas +sarcomata +sarcophagi +sarcophagus +sarcophaguses +sardine +sardines +sardonic +sardonically +saree +sarees +sarge +sarges +sari +saris +sarong +sarongs +sarsaparilla +sarsaparillas +sartorial +sartorially +sash +sashay +sashayed +sashaying +sashays +sashes +sass +sassafras +sassafrases +sassed +sasses +sassier +sassiest +sassing +sassy +satanic +satanical +satanically +satanism +satanist +satanists +satchel +satchels +sate +sated +sateen +satellite +satellites +sates +satiable +satiate +satiated +satiates +satiating +satiation +satiety +satin +sating +satinwood +satinwoods +satiny +satire +satires +satiric +satirical +satirically +satirist +satirists +satirize +satirized +satirizes +satirizing +satisfaction +satisfactions +satisfactorily +satisfactory +satisfied +satisfies +satisfy +satisfying +satisfyingly +satori +satrap +satraps +saturate +saturated +saturates +saturating +saturation +saturnine +satyr +satyriasis +satyric +satyrs +sauce +sauced +saucepan +saucepans +saucer +saucers +sauces +saucier +sauciest +saucily +sauciness +saucing +saucy +sauerkraut +sauna +saunaed +saunaing +saunas +saunter +sauntered +sauntering +saunters +saurian +sauropod +sauropods +sausage +sausages +saute +sauted +sauteed +sauteing +sauterne +sauternes +sautes +savable +savage +savaged +savagely +savageness +savager +savageries +savagery +savages +savagest +savaging +savanna +savannah +savannahs +savannas +savant +savants +save +saveable +saved +saver +savers +saves +saving +savings +savior +saviors +saviour +saviours +savor +savored +savorier +savories +savoriest +savoriness +savoring +savors +savory +savour +savoured +savourier +savouries +savouriest +savouring +savours +savoury +savoy +savoys +savvied +savvier +savvies +savviest +savvy +savvying +sawbones +sawboneses +sawbuck +sawbucks +sawdust +sawed +sawflies +sawfly +sawhorse +sawhorses +sawing +sawmill +sawmills +sawn +saws +sawyer +sawyers +saxes +saxifrage +saxifrages +saxophone +saxophones +saxophonist +saxophonists +saying +sayings +says +scab +scabbard +scabbards +scabbed +scabbier +scabbiest +scabbiness +scabbing +scabby +scabies +scabrous +scabs +scad +scads +scaffold +scaffolding +scaffolds +scalawag +scalawags +scald +scalded +scalding +scalds +scale +scaled +scaleless +scalene +scales +scalier +scaliest +scaliness +scaling +scallion +scallions +scallop +scalloped +scalloping +scallops +scalp +scalped +scalpel +scalpels +scalper +scalpers +scalping +scalps +scaly +scam +scammed +scamming +scamp +scamper +scampered +scampering +scampers +scampi +scampies +scamps +scams +scan +scandal +scandalize +scandalized +scandalizes +scandalizing +scandalmonger +scandalmongers +scandalous +scandalously +scandals +scandium +scanned +scanner +scanners +scanning +scans +scansion +scant +scanted +scanter +scantest +scantier +scantiest +scantily +scantiness +scanting +scantly +scantness +scants +scanty +scapegoat +scapegoated +scapegoating +scapegoats +scapegrace +scapegraces +scapula +scapulae +scapular +scapulars +scapulas +scar +scarab +scarabs +scarce +scarcely +scarceness +scarcer +scarcest +scarcity +scare +scarecrow +scarecrows +scared +scareder +scaredest +scaremonger +scaremongers +scares +scarf +scarfed +scarfing +scarfs +scarier +scariest +scarification +scarified +scarifies +scarify +scarifying +scarily +scariness +scaring +scarlatina +scarlet +scarp +scarped +scarping +scarps +scarred +scarring +scars +scarves +scary +scat +scathing +scathingly +scatological +scatology +scats +scatted +scatter +scatterbrain +scatterbrained +scatterbrains +scattered +scattering +scatterings +scatters +scatting +scavenge +scavenged +scavenger +scavengers +scavenges +scavenging +scenario +scenarios +scenarist +scenarists +scene +scenery +scenes +scenic +scenically +scent +scented +scenting +scentless +scents +scepter +scepters +sceptic +sceptical +scepticism +sceptics +sceptre +sceptres +schedule +scheduled +schedules +scheduling +schematic +schematically +schematics +scheme +schemed +schemer +schemers +schemes +scheming +scherzi +scherzo +scherzos +schilling +schillings +schism +schismatic +schismatics +schisms +schist +schizo +schizoid +schizoids +schizophrenia +schizophrenic +schizophrenics +schizos +schlemiel +schlemiels +schlep +schlepp +schlepped +schlepping +schlepps +schleps +schlock +schlocky +schmaltz +schmaltzier +schmaltziest +schmaltzy +schmalz +schmo +schmoe +schmoes +schmooze +schmoozed +schmoozes +schmoozing +schmos +schmuck +schmucks +schnapps +schnaps +schnauzer +schnauzers +schnitzel +schnitzels +schnook +schnooks +schnoz +schnozes +schnozzle +schnozzles +scholar +scholarly +scholars +scholarship +scholarships +scholastic +scholastically +school +schoolbag +schoolbags +schoolbook +schoolbooks +schoolboy +schoolboys +schoolchild +schoolchildren +schooled +schoolfellow +schoolfellows +schoolgirl +schoolgirls +schoolhouse +schoolhouses +schooling +schoolmarm +schoolmarmish +schoolmarms +schoolmaster +schoolmasters +schoolmate +schoolmates +schoolmistress +schoolmistresses +schoolroom +schoolrooms +schools +schoolteacher +schoolteachers +schoolwork +schoolyard +schoolyards +schooner +schooners +schrod +schrods +schuss +schussboomer +schussboomers +schussed +schusses +schussing +schwa +schwas +sciatic +sciatica +science +sciences +scientific +scientifically +scientist +scientists +scimitar +scimitars +scintilla +scintillas +scintillate +scintillated +scintillates +scintillating +scintillation +scion +scions +scissor +scissored +scissoring +scissors +sclerosis +sclerotic +scoff +scoffed +scoffer +scoffers +scoffing +scofflaw +scofflaws +scoffs +scold +scolded +scolding +scoldings +scolds +scoliosis +scollop +scolloped +scolloping +scollops +sconce +sconces +scone +scones +scoop +scooped +scoopful +scoopfuls +scooping +scoops +scoot +scooted +scooter +scooters +scooting +scoots +scope +scoped +scopes +scoping +scorbutic +scorch +scorched +scorcher +scorchers +scorches +scorching +score +scoreboard +scoreboards +scorecard +scorecards +scored +scorekeeper +scorekeepers +scoreless +scorer +scorers +scores +scoring +scorn +scorned +scorner +scorners +scornful +scornfully +scorning +scorns +scorpion +scorpions +scotch +scotched +scotches +scotching +scoundrel +scoundrels +scour +scoured +scourer +scourers +scourge +scourged +scourges +scourging +scouring +scours +scout +scouted +scouting +scoutmaster +scoutmasters +scouts +scow +scowl +scowled +scowling +scowls +scows +scrabble +scrabbled +scrabbler +scrabblers +scrabbles +scrabbling +scrag +scraggier +scraggiest +scragglier +scraggliest +scraggly +scraggy +scrags +scram +scramble +scrambled +scrambler +scramblers +scrambles +scrambling +scrammed +scramming +scrams +scrap +scrapbook +scrapbooks +scrape +scraped +scraper +scrapers +scrapes +scrapheap +scrapheaps +scraping +scrapped +scrapper +scrappers +scrappier +scrappiest +scrapping +scrappy +scraps +scrapyard +scrapyards +scratch +scratched +scratches +scratchier +scratchiest +scratchily +scratchiness +scratching +scratchy +scrawl +scrawled +scrawling +scrawls +scrawly +scrawnier +scrawniest +scrawniness +scrawny +scream +screamed +screamer +screamers +screaming +screams +scree +screech +screeched +screeches +screechier +screechiest +screeching +screechy +screen +screened +screening +screenings +screenplay +screenplays +screens +screenwriter +screenwriters +screenwriting +screes +screw +screwball +screwballs +screwdriver +screwdrivers +screwed +screwier +screwiest +screwiness +screwing +screws +screwworm +screwworms +screwy +scribal +scribble +scribbled +scribbler +scribblers +scribbles +scribbling +scribe +scribes +scrim +scrimmage +scrimmaged +scrimmages +scrimmaging +scrimp +scrimped +scrimping +scrimps +scrims +scrimshaw +scrimshawed +scrimshawing +scrimshaws +scrip +scrips +script +scripted +scripting +scripts +scriptural +scripture +scriptures +scriptwriter +scriptwriters +scrivener +scriveners +scrod +scrods +scrofula +scrofulous +scroll +scrolled +scrolling +scrolls +scrooge +scrooges +scrota +scrotal +scrotum +scrotums +scrounge +scrounged +scrounger +scroungers +scrounges +scroungier +scroungiest +scrounging +scroungy +scrub +scrubbed +scrubber +scrubbers +scrubbier +scrubbiest +scrubbing +scrubby +scrubs +scruff +scruffier +scruffiest +scruffily +scruffiness +scruffs +scruffy +scrumptious +scrumptiously +scrunch +scrunched +scrunches +scrunchie +scrunchies +scrunching +scrunchy +scruple +scrupled +scruples +scrupling +scrupulosity +scrupulous +scrupulously +scrupulousness +scrutinize +scrutinized +scrutinizes +scrutinizing +scrutiny +scuba +scubaed +scubaing +scubas +scud +scudded +scudding +scuds +scuff +scuffed +scuffing +scuffle +scuffled +scuffles +scuffling +scuffs +scull +sculled +sculler +sculleries +scullers +scullery +sculling +scullion +scullions +sculls +sculpt +sculpted +sculpting +sculptor +sculptors +sculptress +sculptresses +sculpts +sculptural +sculpture +sculptured +sculptures +sculpturing +scum +scumbag +scumbags +scummed +scummier +scummiest +scumming +scummy +scums +scupper +scuppered +scuppering +scuppers +scurf +scurfier +scurfiest +scurfy +scurried +scurries +scurrility +scurrilous +scurrilously +scurrilousness +scurry +scurrying +scurvier +scurviest +scurvily +scurvy +scutcheon +scutcheons +scuttle +scuttlebutt +scuttled +scuttles +scuttling +scuzzier +scuzziest +scuzzy +scythe +scythed +scythes +scything +seabed +seabeds +seabird +seabirds +seaboard +seaboards +seaborne +seacoast +seacoasts +seafarer +seafarers +seafaring +seafloor +seafloors +seafood +seafront +seafronts +seagoing +seagull +seagulls +seahorse +seahorses +seal +sealant +sealants +sealed +sealer +sealers +sealing +seals +sealskin +seam +seaman +seamanship +seamed +seamen +seamier +seamiest +seaming +seamless +seamlessly +seams +seamstress +seamstresses +seamy +seance +seances +seaplane +seaplanes +seaport +seaports +sear +search +searched +searcher +searchers +searches +searching +searchingly +searchlight +searchlights +seared +searing +sears +seas +seascape +seascapes +seashell +seashells +seashore +seashores +seasick +seasickness +seaside +seasides +season +seasonable +seasonably +seasonal +seasonally +seasoned +seasoning +seasonings +seasons +seat +seated +seating +seatmate +seatmates +seats +seawall +seawalls +seaward +seawards +seawater +seaway +seaways +seaweed +seaworthier +seaworthiest +seaworthiness +seaworthy +sebaceous +seborrhea +seborrhoea +secant +secants +secede +seceded +secedes +seceding +secession +secessionist +secessionists +seclude +secluded +secludes +secluding +seclusion +seclusive +second +secondaries +secondarily +secondary +seconded +seconder +seconders +secondhand +seconding +secondly +seconds +secrecy +secret +secretarial +secretariat +secretariats +secretaries +secretary +secretaryship +secrete +secreted +secretes +secreting +secretion +secretions +secretive +secretively +secretiveness +secretly +secretory +secrets +secs +sect +sectarian +sectarianism +sectarians +sectaries +sectary +section +sectional +sectionalism +sectionals +sectioned +sectioning +sections +sector +sectors +sects +secular +secularism +secularist +secularists +secularization +secularize +secularized +secularizes +secularizing +secure +secured +securely +securer +secures +securest +securing +securities +security +sedan +sedans +sedate +sedated +sedately +sedateness +sedater +sedates +sedatest +sedating +sedation +sedative +sedatives +sedentary +sedge +sedgy +sediment +sedimentary +sedimentation +sediments +sedition +seditious +seduce +seduced +seducer +seducers +seduces +seducing +seduction +seductions +seductive +seductively +seductiveness +seductress +seductresses +sedulous +sedulously +seed +seedbed +seedbeds +seedcase +seedcases +seeded +seeder +seeders +seedier +seediest +seediness +seeding +seedless +seedling +seedlings +seedpod +seedpods +seeds +seedy +seeing +seek +seeker +seekers +seeking +seeks +seem +seemed +seeming +seemingly +seemlier +seemliest +seemliness +seemly +seems +seen +seep +seepage +seeped +seeping +seeps +seer +seers +seersucker +sees +seesaw +seesawed +seesawing +seesaws +seethe +seethed +seethes +seething +segment +segmentation +segmented +segmenting +segments +segregate +segregated +segregates +segregating +segregation +segregationist +segregationists +segue +segued +segueing +segues +seigneur +seigneurs +seignior +seigniorial +seigniors +seine +seined +seiner +seiners +seines +seining +seismic +seismically +seismograph +seismographer +seismographers +seismographic +seismographs +seismography +seismologic +seismological +seismologist +seismologists +seismology +seize +seized +seizes +seizing +seizure +seizures +seldom +select +selected +selecting +selection +selections +selective +selectively +selectivity +selectman +selectmen +selectness +selector +selectors +selects +selenium +selenographer +selenographers +selenography +self +selfish +selfishly +selfishness +selfless +selflessly +selflessness +selfsame +sell +seller +sellers +selling +sellout +sellouts +sells +seltzer +selvage +selvages +selvedge +selvedges +selves +semantic +semantically +semanticist +semanticists +semantics +semaphore +semaphored +semaphores +semaphoring +semblance +semblances +semen +semester +semesters +semi +semiannual +semiannually +semiarid +semiautomatic +semiautomatics +semicircle +semicircles +semicircular +semicolon +semicolons +semiconducting +semiconductor +semiconductors +semiconscious +semidarkness +semidetached +semifinal +semifinalist +semifinalists +semifinals +semigloss +semimonthlies +semimonthly +seminal +seminar +seminarian +seminarians +seminaries +seminars +seminary +semiofficial +semiotic +semiotics +semipermeable +semiprecious +semiprivate +semipro +semiprofessional +semiprofessionals +semipros +semiretired +semis +semiskilled +semisolid +semisweet +semitone +semitones +semitrailer +semitrailers +semitransparent +semitropical +semivowel +semivowels +semiweeklies +semiweekly +semiyearly +semolina +sempstress +sempstresses +senate +senates +senator +senatorial +senators +send +sender +senders +sending +sendoff +sendoffs +sends +senescence +senescent +senile +senility +senior +seniority +seniors +senna +senor +senora +senoras +senorita +senoritas +senors +sensation +sensational +sensationalism +sensationalist +sensationalists +sensationalize +sensationalized +sensationalizes +sensationalizing +sensationally +sensations +sense +sensed +senseless +senselessly +senselessness +senses +sensibilities +sensibility +sensible +sensibleness +sensibly +sensing +sensitive +sensitively +sensitiveness +sensitives +sensitivities +sensitivity +sensitization +sensitize +sensitized +sensitizes +sensitizing +sensor +sensors +sensory +sensual +sensualist +sensualists +sensuality +sensually +sensuous +sensuously +sensuousness +sent +sentence +sentenced +sentences +sentencing +sententious +sententiously +sentience +sentient +sentiment +sentimental +sentimentalism +sentimentalist +sentimentalists +sentimentality +sentimentalization +sentimentalize +sentimentalized +sentimentalizes +sentimentalizing +sentimentally +sentiments +sentinel +sentinels +sentries +sentry +sepal +sepals +separability +separable +separably +separate +separated +separately +separateness +separates +separating +separation +separations +separatism +separatist +separatists +separative +separator +separators +sepia +sepsis +septa +septet +septets +septette +septettes +septic +septicemia +septicemic +septuagenarian +septuagenarians +septum +septums +sepulcher +sepulchered +sepulchering +sepulchers +sepulchral +sepulchre +sepulchred +sepulchres +sepulchring +sequel +sequels +sequence +sequenced +sequences +sequencing +sequential +sequentially +sequester +sequestered +sequestering +sequesters +sequestrate +sequestrated +sequestrates +sequestrating +sequestration +sequestrations +sequin +sequined +sequinned +sequins +sequoia +sequoias +sera +seraglio +seraglios +serape +serapes +seraph +seraphic +seraphim +seraphs +sere +serenade +serenaded +serenades +serenading +serendipitous +serendipity +serene +serenely +sereneness +serener +serenest +serenity +serer +serest +serf +serfdom +serfs +serge +sergeant +sergeants +serial +serialization +serialize +serialized +serializes +serializing +serially +serials +series +serif +serifs +serigraph +serigraphs +serious +seriously +seriousness +sermon +sermonize +sermonized +sermonizes +sermonizing +sermons +serology +serous +serpent +serpentine +serpents +serrate +serrated +serration +serrations +serried +serum +serums +servant +servants +serve +served +server +servers +serves +service +serviceability +serviceable +serviced +serviceman +servicemen +services +servicewoman +servicewomen +servicing +serviette +serviettes +servile +servility +serving +servings +servitor +servitors +servitude +servo +servomechanism +servomechanisms +servomotor +servomotors +servos +sesame +sesames +sesquicentennial +sesquicentennials +session +sessions +setback +setbacks +sets +setscrew +setscrews +settee +settees +setter +setters +setting +settings +settle +settled +settlement +settlements +settler +settlers +settles +settling +setup +setups +seven +sevens +seventeen +seventeens +seventeenth +seventeenths +seventh +sevenths +seventies +seventieth +seventieths +seventy +sever +several +severally +severance +severances +severe +severed +severely +severeness +severer +severest +severing +severity +severs +sewage +sewed +sewer +sewerage +sewers +sewing +sewn +sews +sexagenarian +sexagenarians +sexed +sexes +sexier +sexiest +sexily +sexiness +sexing +sexism +sexist +sexists +sexless +sexologist +sexologists +sexology +sexpot +sexpots +sextant +sextants +sextet +sextets +sextette +sextettes +sexton +sextons +sextuplet +sextuplets +sexual +sexuality +sexually +sexy +shabbier +shabbiest +shabbily +shabbiness +shabby +shack +shackle +shackled +shackles +shackling +shacks +shad +shade +shaded +shades +shadier +shadiest +shadily +shadiness +shading +shadings +shadow +shadowbox +shadowboxed +shadowboxes +shadowboxing +shadowed +shadowier +shadowiest +shadowing +shadows +shadowy +shads +shady +shaft +shafted +shafting +shafts +shag +shagged +shaggier +shaggiest +shagginess +shagging +shaggy +shags +shah +shahs +shake +shakedown +shakedowns +shaken +shakeout +shakeouts +shaker +shakers +shakes +shakeup +shakeups +shakier +shakiest +shakily +shakiness +shaking +shaky +shale +shall +shallot +shallots +shallow +shallower +shallowest +shallowly +shallowness +shallows +shalom +shalt +sham +shaman +shamanic +shamans +shamble +shambled +shambles +shambling +shame +shamed +shamefaced +shamefacedly +shameful +shamefully +shamefulness +shameless +shamelessly +shamelessness +shames +shaming +shammed +shammies +shamming +shammy +shampoo +shampooed +shampooer +shampooers +shampooing +shampoos +shamrock +shamrocks +shams +shanghai +shanghaied +shanghaiing +shanghais +shank +shanks +shanties +shantung +shanty +shantytown +shantytowns +shape +shaped +shapeless +shapelessly +shapelessness +shapelier +shapeliest +shapeliness +shapely +shapes +shaping +shard +shards +share +sharecrop +sharecropped +sharecropper +sharecroppers +sharecropping +sharecrops +shared +shareholder +shareholders +sharer +sharers +shares +shareware +sharia +sharing +shark +sharks +sharkskin +sharp +sharped +sharpen +sharpened +sharpener +sharpeners +sharpening +sharpens +sharper +sharpers +sharpest +sharpie +sharpies +sharping +sharply +sharpness +sharps +sharpshooter +sharpshooters +sharpshooting +sharpy +shat +shatter +shattered +shattering +shatterproof +shatters +shave +shaved +shaven +shaver +shavers +shaves +shaving +shavings +shawl +shawls +shay +shays +sheaf +shear +sheared +shearer +shearers +shearing +shears +sheath +sheathe +sheathed +sheathes +sheathing +sheathings +sheaths +sheave +sheaved +sheaves +sheaving +shebang +shebangs +shed +shedding +sheds +sheen +sheenier +sheeniest +sheeny +sheep +sheepdog +sheepdogs +sheepfold +sheepfolds +sheepherder +sheepherders +sheepish +sheepishly +sheepishness +sheepskin +sheepskins +sheer +sheered +sheerer +sheerest +sheering +sheerness +sheers +sheet +sheeting +sheetlike +sheets +sheik +sheikdom +sheikdoms +sheikh +sheikhdom +sheikhdoms +sheikhs +sheiks +shekel +shekels +shelf +shell +shellac +shellack +shellacked +shellacking +shellackings +shellacks +shellacs +shelled +shellfire +shellfish +shellfishes +shelling +shells +shelter +sheltered +sheltering +shelters +shelve +shelved +shelves +shelving +shenanigan +shenanigans +shepherd +shepherded +shepherdess +shepherdesses +shepherding +shepherds +sherbert +sherberts +sherbet +sherbets +sherd +sherds +sheriff +sheriffs +sherries +sherry +shes +shew +shewed +shewing +shewn +shews +shiatsu +shibboleth +shibboleths +shied +shield +shielded +shielding +shields +shier +shies +shiest +shift +shifted +shiftier +shiftiest +shiftily +shiftiness +shifting +shiftless +shiftlessly +shiftlessness +shifts +shifty +shill +shillalah +shillalahs +shilled +shillelagh +shillelaghs +shilling +shillings +shills +shim +shimmed +shimmer +shimmered +shimmering +shimmers +shimmery +shimmied +shimmies +shimming +shimmy +shimmying +shims +shin +shinbone +shinbones +shindig +shindigs +shine +shined +shiner +shiners +shines +shingle +shingled +shingles +shingling +shinguard +shinguards +shinier +shiniest +shininess +shining +shinned +shinnied +shinnies +shinning +shinny +shinnying +shins +shinsplints +shiny +ship +shipboard +shipboards +shipbuilder +shipbuilders +shipbuilding +shipload +shiploads +shipmate +shipmates +shipment +shipments +shipowner +shipowners +shipped +shipper +shippers +shipping +ships +shipshape +shipwreck +shipwrecked +shipwrecking +shipwrecks +shipwright +shipwrights +shipyard +shipyards +shire +shires +shirk +shirked +shirker +shirkers +shirking +shirks +shirr +shirred +shirring +shirrings +shirrs +shirt +shirtfront +shirtfronts +shirting +shirtless +shirts +shirtsleeve +shirtsleeves +shirttail +shirttails +shirtwaist +shirtwaists +shit +shits +shittier +shittiest +shitting +shitty +shiv +shiver +shivered +shivering +shivers +shivery +shivs +shlemiel +shlemiels +shlep +shlepp +shlepped +shlepping +shlepps +shleps +shlock +shmaltz +shoal +shoaled +shoaling +shoals +shoat +shoats +shock +shocked +shocker +shockers +shocking +shockingly +shockproof +shocks +shod +shodden +shoddier +shoddiest +shoddily +shoddiness +shoddy +shoe +shoed +shoehorn +shoehorned +shoehorning +shoehorns +shoeing +shoelace +shoelaces +shoemaker +shoemakers +shoes +shoeshine +shoeshines +shoestring +shoestrings +shoetree +shoetrees +shogun +shogunate +shoguns +shone +shoo +shooed +shooing +shook +shoon +shoos +shoot +shooter +shooters +shooting +shootings +shootout +shootouts +shoots +shop +shopkeeper +shopkeepers +shoplift +shoplifted +shoplifter +shoplifters +shoplifting +shoplifts +shoppe +shopped +shopper +shoppers +shoppes +shopping +shops +shoptalk +shopworn +shore +shorebird +shorebirds +shored +shoreline +shorelines +shores +shoring +shorn +short +shortage +shortages +shortbread +shortcake +shortcakes +shortchange +shortchanged +shortchanges +shortchanging +shortcoming +shortcomings +shortcut +shortcuts +shorted +shorten +shortened +shortening +shortenings +shortens +shorter +shortest +shortfall +shortfalls +shorthand +shorthanded +shorthorn +shorthorns +shortie +shorties +shorting +shortly +shortness +shorts +shortsighted +shortsightedly +shortsightedness +shortstop +shortstops +shortwave +shortwaves +shorty +shot +shotgun +shotgunned +shotgunning +shotguns +shots +should +shoulder +shouldered +shouldering +shoulders +shout +shouted +shouter +shouters +shouting +shouts +shove +shoved +shovel +shoveled +shovelful +shovelfuls +shoveling +shovelled +shovelling +shovels +shoves +shoving +show +showbiz +showboat +showboated +showboating +showboats +showcase +showcased +showcases +showcasing +showdown +showdowns +showed +shower +showered +showering +showers +showery +showgirl +showgirls +showier +showiest +showily +showiness +showing +showings +showman +showmanship +showmen +shown +showoff +showoffs +showpiece +showpieces +showplace +showplaces +showroom +showrooms +shows +showstopper +showstoppers +showy +shrank +shrapnel +shred +shredded +shredder +shredders +shredding +shreds +shrew +shrewd +shrewder +shrewdest +shrewdly +shrewdness +shrewish +shrews +shriek +shrieked +shrieking +shrieks +shrift +shrike +shrikes +shrill +shrilled +shriller +shrillest +shrilling +shrillness +shrills +shrilly +shrimp +shrimped +shrimping +shrimps +shrine +shrines +shrink +shrinkable +shrinkage +shrinking +shrinks +shrive +shrived +shrivel +shriveled +shriveling +shrivelled +shrivelling +shrivels +shriven +shrives +shriving +shroud +shrouded +shrouding +shrouds +shrove +shrub +shrubberies +shrubbery +shrubbier +shrubbiest +shrubby +shrubs +shrug +shrugged +shrugging +shrugs +shrunk +shrunken +shtick +shticks +shuck +shucked +shucking +shucks +shudder +shuddered +shuddering +shudders +shuffle +shuffleboard +shuffleboards +shuffled +shuffler +shufflers +shuffles +shuffling +shun +shunned +shunning +shuns +shunt +shunted +shunting +shunts +shush +shushed +shushes +shushing +shut +shutdown +shutdowns +shuteye +shutoff +shutoffs +shutout +shutouts +shuts +shutter +shutterbug +shutterbugs +shuttered +shuttering +shutters +shutting +shuttle +shuttlecock +shuttlecocked +shuttlecocking +shuttlecocks +shuttled +shuttles +shuttling +shyer +shyest +shying +shyly +shyness +shyster +shysters +sibilant +sibilants +sibling +siblings +sibyl +sibylline +sibyls +sicced +siccing +sick +sickbed +sickbeds +sicked +sicken +sickened +sickening +sickeningly +sickens +sicker +sickest +sickie +sickies +sicking +sickish +sickle +sickles +sicklier +sickliest +sickly +sickness +sicknesses +sicko +sickos +sickout +sickouts +sickroom +sickrooms +sicks +sics +side +sidearm +sidearms +sidebar +sidebars +sideboard +sideboards +sideburns +sidecar +sidecars +sided +sidekick +sidekicks +sidelight +sidelights +sideline +sidelined +sidelines +sidelining +sidelong +sideman +sidemen +sidepiece +sidepieces +sidereal +sides +sidesaddle +sidesaddles +sideshow +sideshows +sidesplitting +sidestep +sidestepped +sidestepping +sidesteps +sidestroke +sidestroked +sidestrokes +sidestroking +sideswipe +sideswiped +sideswipes +sideswiping +sidetrack +sidetracked +sidetracking +sidetracks +sidewalk +sidewalks +sidewall +sidewalls +sideways +sidewinder +sidewinders +sidewise +siding +sidings +sidle +sidled +sidles +sidling +siege +sieges +sienna +sierra +sierras +siesta +siestas +sieve +sieved +sieves +sieving +sift +sifted +sifter +sifters +sifting +sifts +sigh +sighed +sighing +sighs +sight +sighted +sighting +sightings +sightless +sightlier +sightliest +sightly +sightread +sightreading +sightreads +sights +sightseeing +sightseer +sightseers +sigma +sigmas +sign +signage +signal +signaled +signaler +signalers +signaling +signalization +signalize +signalized +signalizes +signalizing +signalled +signalling +signally +signalman +signalmen +signals +signatories +signatory +signature +signatures +signboard +signboards +signed +signer +signers +signet +signets +significance +significant +significantly +signification +significations +signified +signifies +signify +signifying +signing +signings +signor +signora +signoras +signore +signori +signorina +signorinas +signorine +signors +signpost +signposted +signposting +signposts +signs +silage +silence +silenced +silencer +silencers +silences +silencing +silent +silenter +silentest +silently +silents +silhouette +silhouetted +silhouettes +silhouetting +silica +silicate +silicates +siliceous +silicious +silicon +silicone +silicosis +silk +silken +silkier +silkiest +silkily +silkiness +silks +silkscreen +silkscreened +silkscreening +silkscreens +silkworm +silkworms +silky +sill +sillier +sillies +silliest +silliness +sills +silly +silo +silos +silt +silted +siltier +siltiest +silting +silts +silty +silvan +silver +silvered +silverfish +silverfishes +silvering +silvers +silversmith +silversmiths +silverware +silvery +simian +simians +similar +similarities +similarity +similarly +simile +similes +similitude +simmer +simmered +simmering +simmers +simonize +simonized +simonizes +simonizing +simony +simpatico +simper +simpered +simpering +simpers +simple +simpleminded +simpleness +simpler +simplest +simpleton +simpletons +simplicity +simplification +simplifications +simplified +simplifies +simplify +simplifying +simplistic +simplistically +simply +simulate +simulated +simulates +simulating +simulation +simulations +simulator +simulators +simulcast +simulcasted +simulcasting +simulcasts +simultaneity +simultaneous +simultaneously +since +sincere +sincerely +sincerer +sincerest +sincerity +sine +sinecure +sinecures +sines +sinew +sinews +sinewy +sinful +sinfully +sinfulness +sing +singable +singe +singed +singeing +singer +singers +singes +singing +single +singled +singleness +singles +singleton +singletons +singletree +singletrees +singling +singly +sings +singsong +singsongs +singular +singularities +singularity +singularly +singulars +sinister +sink +sinkable +sinker +sinkers +sinkhole +sinkholes +sinking +sinks +sinless +sinned +sinner +sinners +sinning +sins +sinuosity +sinuous +sinus +sinuses +sinusitis +siphon +siphoned +siphoning +siphons +sipped +sipper +sippers +sipping +sips +sire +sired +siree +siren +sirens +sires +siring +sirloin +sirloins +sirocco +siroccos +sirree +sirs +sirup +sirups +sisal +sises +sissier +sissies +sissiest +sissified +sissy +sister +sisterhood +sisterhoods +sisterliness +sisterly +sisters +sitar +sitarist +sitarists +sitars +sitcom +sitcoms +site +sited +sites +siting +sits +sitter +sitters +sitting +sittings +situate +situated +situates +situating +situation +situations +situp +situps +sixes +sixfold +sixpence +sixpences +sixshooter +sixshooters +sixteen +sixteens +sixteenth +sixteenths +sixth +sixths +sixties +sixtieth +sixtieths +sixty +sizable +size +sizeable +sized +sizes +sizing +sizzle +sizzled +sizzles +sizzling +skate +skateboard +skateboarded +skateboarder +skateboarders +skateboarding +skateboards +skated +skater +skaters +skates +skating +skedaddle +skedaddled +skedaddles +skedaddling +skeet +skein +skeins +skeletal +skeleton +skeletons +skeptic +skeptical +skeptically +skepticism +skeptics +sketch +sketched +sketcher +sketchers +sketches +sketchier +sketchiest +sketchily +sketchiness +sketching +sketchy +skew +skewed +skewer +skewered +skewering +skewers +skewing +skews +skid +skidded +skidding +skids +skied +skier +skiers +skies +skiff +skiffs +skiing +skilful +skill +skilled +skillet +skillets +skillful +skillfully +skillfulness +skills +skim +skimmed +skimmer +skimmers +skimming +skimp +skimped +skimpier +skimpiest +skimpily +skimpiness +skimping +skimps +skimpy +skims +skin +skincare +skinflick +skinflicks +skinflint +skinflints +skinhead +skinheads +skinless +skinned +skinnier +skinniest +skinniness +skinning +skinny +skins +skintight +skip +skipped +skipper +skippered +skippering +skippers +skipping +skips +skirmish +skirmished +skirmishes +skirmishing +skirt +skirted +skirting +skirts +skis +skit +skits +skitter +skittered +skittering +skitters +skittish +skittishly +skittishness +skivvied +skivvies +skivvy +skivvying +skoal +skoals +skulduggery +skulk +skulked +skulker +skulkers +skulking +skulks +skull +skullcap +skullcaps +skullduggery +skulls +skunk +skunked +skunking +skunks +skycap +skycaps +skydive +skydived +skydiver +skydivers +skydives +skydiving +skydove +skyed +skying +skyjack +skyjacked +skyjacker +skyjackers +skyjacking +skyjackings +skyjacks +skylark +skylarked +skylarking +skylarks +skylight +skylights +skyline +skylines +skyrocket +skyrocketed +skyrocketing +skyrockets +skyscraper +skyscrapers +skyward +skywards +skywriter +skywriters +skywriting +slab +slabbed +slabbing +slabs +slack +slacked +slacken +slackened +slackening +slackens +slacker +slackers +slackest +slacking +slackly +slackness +slacks +slag +slain +slake +slaked +slakes +slaking +slalom +slalomed +slaloming +slaloms +slam +slammed +slammer +slammers +slamming +slams +slander +slandered +slanderer +slanderers +slandering +slanderous +slanders +slang +slangier +slangiest +slangy +slant +slanted +slanting +slantingly +slants +slantwise +slap +slapdash +slaphappier +slaphappiest +slaphappy +slapped +slapping +slaps +slapstick +slash +slashed +slasher +slashers +slashes +slashing +slat +slate +slated +slates +slather +slathered +slathering +slathers +slating +slats +slattern +slatternly +slatterns +slaughter +slaughtered +slaughterer +slaughterers +slaughterhouse +slaughterhouses +slaughtering +slaughters +slave +slaved +slaveholder +slaveholders +slaver +slavered +slavering +slavers +slavery +slaves +slaving +slavish +slavishly +slavishness +slaw +slay +slayer +slayers +slaying +slayings +slays +sleaze +sleazes +sleazier +sleaziest +sleazily +sleaziness +sleazy +sled +sledded +sledder +sledders +sledding +sledge +sledged +sledgehammer +sledgehammered +sledgehammering +sledgehammers +sledges +sledging +sleds +sleek +sleeked +sleeker +sleekest +sleeking +sleekly +sleekness +sleeks +sleep +sleeper +sleepers +sleepier +sleepiest +sleepily +sleepiness +sleeping +sleepless +sleeplessly +sleeplessness +sleepover +sleepovers +sleeps +sleepwalk +sleepwalked +sleepwalker +sleepwalkers +sleepwalking +sleepwalks +sleepwear +sleepy +sleepyhead +sleepyheads +sleet +sleeted +sleetier +sleetiest +sleeting +sleets +sleety +sleeve +sleeved +sleeveless +sleeves +sleigh +sleighed +sleighing +sleighs +sleight +sleights +slender +slenderer +slenderest +slenderize +slenderized +slenderizes +slenderizing +slenderness +slept +sleuth +sleuths +slew +slewed +slewing +slews +slice +sliced +slicer +slicers +slices +slicing +slick +slicked +slicker +slickers +slickest +slicking +slickly +slickness +slicks +slid +slide +slider +sliders +slides +sliding +slier +sliest +slight +slighted +slighter +slightest +slighting +slightly +slightness +slights +slily +slim +slime +slimier +slimiest +sliminess +slimmed +slimmer +slimmest +slimming +slimness +slims +slimy +sling +slinging +slings +slingshot +slingshots +slink +slinked +slinkier +slinkiest +slinking +slinks +slinky +slip +slipcase +slipcases +slipcover +slipcovers +slipknot +slipknots +slippage +slippages +slipped +slipper +slipperier +slipperiest +slipperiness +slippers +slippery +slipping +slips +slipshod +slipstream +slipstreams +slipway +slipways +slit +slither +slithered +slithering +slithers +slithery +slits +slitting +sliver +slivered +slivering +slivers +slob +slobber +slobbered +slobbering +slobbers +slobbery +slobs +sloe +sloes +slog +slogan +slogans +slogged +slogging +slogs +sloop +sloops +slop +slope +sloped +slopes +sloping +slopped +sloppier +sloppiest +sloppily +sloppiness +slopping +sloppy +slops +slosh +sloshed +sloshes +sloshing +slot +sloth +slothful +slothfully +slothfulness +sloths +slots +slotted +slotting +slouch +slouched +sloucher +slouchers +slouches +slouchier +slouchiest +slouching +slouchy +slough +sloughed +sloughing +sloughs +sloven +slovenlier +slovenliest +slovenliness +slovenly +slovens +slow +slowdown +slowdowns +slowed +slower +slowest +slowing +slowly +slowness +slowpoke +slowpokes +slows +sludge +sludgier +sludgiest +sludgy +slue +slued +slues +slug +sluggard +sluggards +slugged +slugger +sluggers +slugging +sluggish +sluggishly +sluggishness +slugs +sluice +sluiced +sluices +sluicing +sluing +slum +slumber +slumbered +slumbering +slumberous +slumbers +slumbrous +slumlord +slumlords +slummed +slummier +slummiest +slumming +slummy +slump +slumped +slumping +slumps +slums +slung +slunk +slur +slurp +slurped +slurping +slurps +slurred +slurring +slurry +slurs +slush +slushier +slushiest +slushiness +slushy +slut +sluts +sluttier +sluttiest +sluttish +slutty +slyer +slyest +slyly +slyness +smack +smacked +smacker +smackers +smacking +smacks +small +smaller +smallest +smallish +smallness +smallpox +smalls +smarmier +smarmiest +smarmy +smart +smarted +smarten +smartened +smartening +smartens +smarter +smartest +smarties +smarting +smartly +smartness +smarts +smarty +smartypants +smash +smashed +smasher +smashers +smashes +smashing +smashup +smashups +smattering +smatterings +smear +smeared +smearier +smeariest +smearing +smears +smeary +smell +smelled +smellier +smelliest +smelliness +smelling +smells +smelly +smelt +smelted +smelter +smelters +smelting +smelts +smidgen +smidgens +smidgeon +smidgeons +smidgin +smidgins +smilax +smile +smiled +smiles +smiley +smileys +smiling +smilingly +smirch +smirched +smirches +smirching +smirk +smirked +smirking +smirks +smit +smite +smites +smith +smithereens +smithies +smiths +smithy +smiting +smitten +smock +smocked +smocking +smocks +smog +smoggier +smoggiest +smoggy +smoke +smoked +smokehouse +smokehouses +smokeless +smoker +smokers +smokes +smokescreen +smokescreens +smokestack +smokestacks +smokier +smokiest +smokiness +smoking +smoky +smolder +smoldered +smoldering +smolders +smooch +smooched +smooches +smooching +smooth +smoothed +smoother +smoothes +smoothest +smoothie +smoothies +smoothing +smoothly +smoothness +smooths +smorgasbord +smorgasbords +smote +smother +smothered +smothering +smothers +smoulder +smouldered +smouldering +smoulders +smudge +smudged +smudges +smudgier +smudgiest +smudging +smudgy +smug +smugger +smuggest +smuggle +smuggled +smuggler +smugglers +smuggles +smuggling +smugly +smugness +smut +smuts +smuttier +smuttiest +smuttiness +smutty +snack +snacked +snacking +snacks +snaffle +snaffled +snaffles +snaffling +snafu +snafus +snag +snagged +snagging +snags +snail +snails +snake +snakebite +snakebites +snaked +snakelike +snakes +snakier +snakiest +snaking +snaky +snap +snapdragon +snapdragons +snapped +snapper +snappers +snappier +snappiest +snappily +snappiness +snapping +snappish +snappishly +snappishness +snappy +snaps +snapshot +snapshots +snare +snared +snares +snaring +snarl +snarled +snarlier +snarliest +snarling +snarlingly +snarls +snarly +snatch +snatched +snatcher +snatchers +snatches +snatching +snazzier +snazziest +snazzily +snazzy +sneak +sneaked +sneaker +sneakers +sneakier +sneakiest +sneakily +sneakiness +sneaking +sneakingly +sneaks +sneaky +sneer +sneered +sneering +sneeringly +sneers +sneeze +sneezed +sneezes +sneezing +snicker +snickered +snickering +snickers +snide +snidely +snider +snidest +sniff +sniffed +sniffer +sniffers +sniffing +sniffle +sniffled +sniffles +sniffling +sniffs +snifter +snifters +snigger +sniggered +sniggering +sniggers +snip +snipe +sniped +sniper +snipers +snipes +sniping +snipped +snippet +snippets +snippier +snippiest +snipping +snippy +snips +snit +snitch +snitched +snitches +snitching +snits +snivel +sniveled +sniveler +snivelers +sniveling +snivelled +snivelling +snivels +snob +snobbery +snobbier +snobbiest +snobbish +snobbishly +snobbishness +snobby +snobs +snood +snoods +snooker +snookered +snookering +snookers +snoop +snooped +snooper +snoopers +snoopier +snoopiest +snooping +snoops +snoopy +snoot +snootier +snootiest +snootily +snootiness +snoots +snooty +snooze +snoozed +snoozes +snoozing +snore +snored +snorer +snorers +snores +snoring +snorkel +snorkeled +snorkeler +snorkelers +snorkeling +snorkelled +snorkelling +snorkels +snort +snorted +snorter +snorters +snorting +snorts +snot +snots +snottier +snottiest +snottily +snottiness +snotty +snout +snouts +snow +snowball +snowballed +snowballing +snowballs +snowbank +snowbanks +snowbird +snowbirds +snowboard +snowboarded +snowboarder +snowboarders +snowboarding +snowboards +snowbound +snowdrift +snowdrifts +snowdrop +snowdrops +snowed +snowfall +snowfalls +snowfield +snowfields +snowflake +snowflakes +snowier +snowiest +snowiness +snowing +snowman +snowmen +snowmobile +snowmobiled +snowmobiles +snowmobiling +snowplow +snowplowed +snowplowing +snowplows +snows +snowshoe +snowshoed +snowshoeing +snowshoes +snowstorm +snowstorms +snowsuit +snowsuits +snowy +snub +snubbed +snubbing +snubs +snuck +snuff +snuffbox +snuffboxes +snuffed +snuffer +snuffers +snuffing +snuffle +snuffled +snuffles +snuffling +snuffly +snuffs +snug +snugged +snugger +snuggest +snugging +snuggle +snuggled +snuggles +snuggling +snugly +snugness +snugs +soak +soaked +soaking +soakings +soaks +soap +soapbox +soapboxes +soaped +soapier +soapiest +soapiness +soaping +soaps +soapstone +soapsuds +soapy +soar +soared +soaring +soars +sobbed +sobbing +sobbingly +sober +sobered +soberer +soberest +sobering +soberly +soberness +sobers +sobriety +sobriquet +sobriquets +sobs +soccer +sociability +sociable +sociables +sociably +social +socialism +socialist +socialistic +socialists +socialite +socialites +socialization +socialize +socialized +socializes +socializing +socially +socials +societal +societies +society +socioeconomic +sociological +sociologically +sociologist +sociologists +sociology +sociopath +sociopaths +sock +socked +socket +sockets +sockeye +sockeyes +socking +socks +soda +sodas +sodded +sodden +soddenly +sodding +sodium +sodomite +sodomites +sodomize +sodomized +sodomizes +sodomizing +sodomy +sods +soever +sofa +sofabed +sofabeds +sofas +soft +softball +softballs +softbound +soften +softened +softener +softeners +softening +softens +softer +softest +softhearted +softie +softies +softly +softness +software +softwood +softwoods +softy +soggier +soggiest +soggily +sogginess +soggy +soigne +soignee +soil +soiled +soiling +soils +soiree +soirees +sojourn +sojourned +sojourner +sojourners +sojourning +sojourns +solace +solaced +solaces +solacing +solar +solaria +solarium +solariums +sold +solder +soldered +solderer +solderers +soldering +solders +soldier +soldiered +soldiering +soldierly +soldiers +soldiery +sole +solecism +solecisms +soled +solely +solemn +solemner +solemness +solemnest +solemnified +solemnifies +solemnify +solemnifying +solemnity +solemnization +solemnize +solemnized +solemnizes +solemnizing +solemnly +solemnness +solenoid +solenoids +soles +soli +solicit +solicitation +solicitations +solicited +soliciting +solicitor +solicitors +solicitous +solicitously +solicitousness +solicits +solicitude +solid +solidarity +solider +solidest +solidi +solidification +solidified +solidifies +solidify +solidifying +solidity +solidly +solidness +solids +solidus +soliloquies +soliloquize +soliloquized +soliloquizes +soliloquizing +soliloquy +soling +solipsism +solitaire +solitaires +solitaries +solitariness +solitary +solitude +solo +soloed +soloing +soloist +soloists +solos +sols +solstice +solstices +solubility +soluble +solubles +solute +solutes +solution +solutions +solvable +solve +solved +solvency +solvent +solvents +solver +solvers +solves +solving +somatic +somber +somberer +somberest +somberly +somberness +sombre +sombrer +sombrero +sombreros +sombrest +some +somebodies +somebody +someday +somehow +someone +someplace +somersault +somersaulted +somersaulting +somersaults +somerset +somersets +somersetted +somersetting +something +sometime +sometimes +someway +someways +somewhat +somewhere +somnambulism +somnambulist +somnambulists +somnolence +somnolent +sonar +sonars +sonata +sonatas +sonatina +sonatinas +song +songbird +songbirds +songbook +songbooks +songfest +songfests +songs +songster +songsters +songstress +songstresses +songwriter +songwriters +sonic +sonnet +sonnets +sonnies +sonny +sonogram +sonograms +sonority +sonorous +sonorously +sonorousness +sons +soon +sooner +soonest +soot +sooth +soothe +soothed +soother +soothers +soothes +soothing +soothingly +soothsayer +soothsayers +soothsaying +sootier +sootiest +sooty +sophism +sophist +sophistic +sophistical +sophisticate +sophisticated +sophisticates +sophisticating +sophistication +sophistries +sophistry +sophists +sophomore +sophomores +sophomoric +soporific +soporifically +soporifics +sopped +soppier +soppiest +sopping +soppy +soprano +sopranos +sops +sorbet +sorbets +sorcerer +sorcerers +sorceress +sorceresses +sorcery +sordid +sordidly +sordidness +sore +sorehead +soreheads +sorely +soreness +sorer +sores +sorest +sorghum +sororities +sorority +sorrel +sorrels +sorrier +sorriest +sorrily +sorriness +sorrow +sorrowed +sorrowful +sorrowfully +sorrowfulness +sorrowing +sorrows +sorry +sort +sorta +sorted +sorter +sorters +sortie +sortied +sortieing +sorties +sorting +sorts +sots +sottish +soubriquet +soubriquets +souffle +souffles +sough +soughed +soughing +soughs +sought +soul +soulful +soulfully +soulfulness +soulless +soullessly +souls +sound +soundboard +soundboards +sounded +sounder +sounders +soundest +sounding +soundings +soundless +soundlessly +soundly +soundness +soundproof +soundproofed +soundproofing +soundproofs +sounds +soundtrack +soundtracks +soup +soupcon +soupcons +souped +soupier +soupiest +souping +soups +soupy +sour +source +sourced +sources +sourcing +sourdough +sourdoughs +soured +sourer +sourest +souring +sourish +sourly +sourness +sourpuss +sourpusses +sours +sous +sousaphone +sousaphones +souse +soused +souses +sousing +south +southbound +southeast +southeaster +southeasterly +southeastern +southeasters +southeastward +southeastwards +southerlies +southerly +southern +southerner +southerners +southernmost +southerns +southpaw +southpaws +southward +southwards +southwest +southwester +southwesterly +southwestern +southwesters +southwestward +southwestwards +souvenir +souvenirs +sovereign +sovereigns +sovereignty +soviet +soviets +sowed +sower +sowers +sowing +sown +sows +soya +soybean +soybeans +space +spacecraft +spacecrafts +spaced +spaceflight +spaceflights +spaceman +spacemen +spaceport +spaceports +spacer +spacers +spaces +spaceship +spaceships +spacesuit +spacesuits +spacewalk +spacewalked +spacewalking +spacewalks +spacewoman +spacewomen +spacey +spacier +spaciest +spaciness +spacing +spacious +spaciously +spaciousness +spacy +spade +spaded +spadeful +spadefuls +spades +spadework +spadices +spading +spadix +spadixes +spaghetti +spake +span +spandex +spangle +spangled +spangles +spangling +spaniel +spaniels +spank +spanked +spanking +spankings +spanks +spanned +spanner +spanners +spanning +spans +spar +spare +spared +sparely +spareness +sparer +spareribs +spares +sparest +sparing +sparingly +spark +sparked +sparkier +sparkiest +sparking +sparkle +sparkled +sparkler +sparklers +sparkles +sparkling +sparks +sparky +sparred +sparring +sparrow +sparrows +spars +sparse +sparsely +sparseness +sparser +sparsest +sparsity +spartan +spas +spasm +spasmodic +spasmodically +spasms +spastic +spastics +spat +spate +spates +spathe +spathes +spatial +spatially +spats +spatted +spatter +spattered +spattering +spatters +spatting +spatula +spatulas +spavin +spavined +spawn +spawned +spawning +spawns +spay +spayed +spaying +spays +speak +speakeasies +speakeasy +speaker +speakers +speaking +speaks +spear +speared +spearfish +spearfished +spearfishes +spearfishing +spearhead +spearheaded +spearheading +spearheads +spearing +spearmint +spears +spec +specced +speccing +special +specialist +specialists +specialities +speciality +specialization +specializations +specialize +specialized +specializes +specializing +specially +specials +specialties +specialty +specie +species +specific +specifically +specification +specifications +specificity +specifics +specified +specifies +specify +specifying +specimen +specimens +specious +speciously +speciousness +speck +specked +specking +speckle +speckled +speckles +speckling +specks +specs +spectacle +spectacles +spectacular +spectacularly +spectaculars +spectator +spectators +specter +specters +spectra +spectral +spectre +spectres +spectrometer +spectrometers +spectroscope +spectroscopes +spectroscopic +spectroscopy +spectrum +spectrums +speculate +speculated +speculates +speculating +speculation +speculations +speculative +speculatively +speculator +speculators +sped +speech +speeches +speechless +speechlessly +speechlessness +speed +speedboat +speedboats +speeded +speeder +speeders +speedier +speediest +speedily +speediness +speeding +speedometer +speedometers +speeds +speedster +speedsters +speedup +speedups +speedway +speedways +speedwell +speedy +speleologist +speleologists +speleology +spell +spellbind +spellbinder +spellbinders +spellbinding +spellbinds +spellbound +spelldown +spelldowns +spelled +speller +spellers +spelling +spellings +spells +spelt +spelunker +spelunkers +spelunking +spend +spendable +spender +spenders +spending +spends +spendthrift +spendthrifts +spent +sperm +spermatozoa +spermatozoon +spermicidal +spermicide +spermicides +sperms +spew +spewed +spewer +spewers +spewing +spews +sphagnum +sphagnums +sphere +spheres +spherical +spherically +spheroid +spheroidal +spheroids +sphincter +sphincters +sphinges +sphinx +sphinxes +spice +spiced +spices +spicier +spiciest +spicily +spiciness +spicing +spicule +spicules +spicy +spider +spiders +spiderweb +spiderwebs +spidery +spied +spiel +spieled +spieling +spiels +spies +spiffier +spiffiest +spiffy +spigot +spigots +spike +spiked +spikes +spikier +spikiest +spikiness +spiking +spiky +spill +spillage +spillages +spilled +spilling +spillover +spillovers +spills +spillway +spillways +spilt +spin +spinach +spinal +spinally +spinals +spindle +spindled +spindles +spindlier +spindliest +spindling +spindly +spine +spineless +spinelessly +spines +spinet +spinets +spinier +spiniest +spinnaker +spinnakers +spinner +spinneret +spinnerets +spinners +spinning +spinoff +spinoffs +spins +spinster +spinsterhood +spinsterish +spinsters +spiny +spiracle +spiracles +spiraea +spiraeas +spiral +spiraled +spiraling +spiralled +spiralling +spirally +spirals +spire +spirea +spireas +spires +spirit +spirited +spiriting +spiritless +spirits +spiritual +spiritualism +spiritualist +spiritualistic +spiritualists +spirituality +spiritually +spirituals +spirituous +spirochete +spirochetes +spiry +spit +spitball +spitballs +spite +spited +spiteful +spitefuller +spitefullest +spitefully +spitefulness +spites +spitfire +spitfires +spiting +spits +spitted +spitting +spittle +spittoon +spittoons +splash +splashdown +splashdowns +splashed +splashes +splashier +splashiest +splashily +splashiness +splashing +splashy +splat +splats +splatted +splatter +splattered +splattering +splatters +splatting +splay +splayed +splayfeet +splayfoot +splayfooted +splaying +splays +spleen +spleens +splendid +splendider +splendidest +splendidly +splendor +splendorous +splendour +splenetic +splice +spliced +splicer +splicers +splices +splicing +splint +splinted +splinter +splintered +splintering +splinters +splintery +splinting +splints +split +splits +splitting +splittings +splotch +splotched +splotches +splotchier +splotchiest +splotching +splotchy +splurge +splurged +splurges +splurging +splutter +spluttered +spluttering +splutters +spoil +spoilage +spoiled +spoiler +spoilers +spoiling +spoils +spoilsport +spoilsports +spoilt +spoke +spoken +spokes +spokesman +spokesmen +spokespeople +spokesperson +spokespersons +spokeswoman +spokeswomen +spoliation +sponge +spongecake +spongecakes +sponged +sponger +spongers +sponges +spongier +spongiest +sponginess +sponging +spongy +sponsor +sponsored +sponsoring +sponsors +sponsorship +spontaneity +spontaneous +spontaneously +spoof +spoofed +spoofing +spoofs +spook +spooked +spookier +spookiest +spookiness +spooking +spooks +spooky +spool +spooled +spooling +spools +spoon +spoonbill +spoonbills +spooned +spoonerism +spoonerisms +spoonful +spoonfuls +spooning +spoons +spoonsful +spoor +spoored +spooring +spoors +sporadic +sporadically +spore +spored +spores +sporing +sport +sported +sportier +sportiest +sportiness +sporting +sportingly +sportive +sportively +sports +sportscast +sportscaster +sportscasters +sportscasts +sportsman +sportsmanlike +sportsmanship +sportsmen +sportswear +sportswoman +sportswomen +sportswriter +sportswriters +sporty +spot +spotless +spotlessly +spotlessness +spotlight +spotlighted +spotlighting +spotlights +spotlit +spots +spotted +spotter +spotters +spottier +spottiest +spottily +spottiness +spotting +spotty +spousal +spousals +spouse +spouses +spout +spouted +spouting +spouts +sprain +sprained +spraining +sprains +sprang +sprat +sprats +sprawl +sprawled +sprawling +sprawls +spray +sprayed +sprayer +sprayers +spraying +sprays +spread +spreadable +spreader +spreaders +spreading +spreads +spreadsheet +spreadsheets +spree +sprees +sprier +spriest +sprig +sprightlier +sprightliest +sprightliness +sprightly +sprigs +spring +springboard +springboards +springbok +springboks +springier +springiest +springily +springiness +springing +springlike +springs +springtime +springy +sprinkle +sprinkled +sprinkler +sprinklers +sprinkles +sprinkling +sprinklings +sprint +sprinted +sprinter +sprinters +sprinting +sprints +sprite +sprites +spritz +spritzed +spritzer +spritzers +spritzes +spritzing +sprocket +sprockets +sprout +sprouted +sprouting +sprouts +spruce +spruced +sprucely +spruceness +sprucer +spruces +sprucest +sprucing +sprung +spry +spryer +spryest +spryly +spryness +spud +spuds +spume +spumed +spumes +spumier +spumiest +spuming +spumone +spumoni +spumy +spun +spunk +spunkier +spunkiest +spunky +spur +spurge +spurious +spuriously +spuriousness +spurn +spurned +spurning +spurns +spurred +spurring +spurs +spurt +spurted +spurting +spurts +sputnik +sputniks +sputter +sputtered +sputtering +sputters +sputum +spyglass +spyglasses +spying +squab +squabble +squabbled +squabbler +squabblers +squabbles +squabbling +squabs +squad +squadron +squadrons +squads +squalid +squalider +squalidest +squalidly +squalidness +squall +squalled +squallier +squalliest +squalling +squalls +squally +squalor +squamous +squander +squandered +squandering +squanders +square +squared +squarely +squareness +squarer +squares +squarest +squaring +squarish +squash +squashed +squashes +squashier +squashiest +squashing +squashy +squat +squatness +squats +squatted +squatter +squatters +squattest +squatting +squaw +squawk +squawked +squawker +squawkers +squawking +squawks +squaws +squeak +squeaked +squeaker +squeakers +squeakier +squeakiest +squeakily +squeakiness +squeaking +squeaks +squeaky +squeal +squealed +squealer +squealers +squealing +squeals +squeamish +squeamishly +squeamishness +squeegee +squeegeed +squeegeeing +squeegees +squeezable +squeeze +squeezed +squeezer +squeezers +squeezes +squeezing +squelch +squelched +squelches +squelchier +squelchiest +squelching +squelchy +squib +squibs +squid +squids +squiggle +squiggled +squiggles +squigglier +squiggliest +squiggling +squiggly +squint +squinted +squinter +squintest +squinting +squints +squire +squired +squires +squiring +squirm +squirmed +squirmier +squirmiest +squirming +squirms +squirmy +squirrel +squirreled +squirreling +squirrelled +squirrelling +squirrels +squirt +squirted +squirting +squirts +squish +squished +squishes +squishier +squishiest +squishing +squishy +stab +stabbed +stabber +stabbers +stabbing +stabbings +stability +stabilization +stabilize +stabilized +stabilizer +stabilizers +stabilizes +stabilizing +stable +stabled +stableman +stablemen +stabler +stables +stablest +stabling +stably +stabs +staccati +staccato +staccatos +stack +stacked +stacking +stacks +stadia +stadium +stadiums +staff +staffed +staffer +staffers +staffing +staffs +stag +stage +stagecoach +stagecoaches +stagecraft +staged +stagehand +stagehands +stages +stagestruck +stagflation +stagger +staggered +staggering +staggeringly +staggers +stagier +stagiest +staging +stagings +stagnancy +stagnant +stagnantly +stagnate +stagnated +stagnates +stagnating +stagnation +stags +stagy +staid +staider +staidest +staidly +staidness +stain +stained +staining +stainless +stains +stair +staircase +staircases +stairs +stairway +stairways +stairwell +stairwells +stake +staked +stakeholder +stakeholders +stakeout +stakeouts +stakes +staking +stalactite +stalactites +stalagmite +stalagmites +stale +staled +stalemate +stalemated +stalemates +stalemating +staleness +staler +stales +stalest +staling +stalk +stalked +stalker +stalkers +stalking +stalkings +stalks +stall +stalled +stalling +stallion +stallions +stalls +stalwart +stalwarts +stamen +stamens +stamina +stammer +stammered +stammerer +stammerers +stammering +stammeringly +stammers +stamp +stamped +stampede +stampeded +stampedes +stampeding +stamper +stampers +stamping +stamps +stance +stances +stanch +stanched +stancher +stanches +stanchest +stanching +stanchion +stanchions +stand +standalone +standard +standardization +standardize +standardized +standardizes +standardizing +standards +standby +standbys +standee +standees +stander +standers +standing +standings +standoff +standoffish +standoffs +standout +standouts +standpipe +standpipes +standpoint +standpoints +stands +standstill +standstills +standup +stank +stanza +stanzas +staph +staphylococcal +staphylococci +staphylococcus +staple +stapled +stapler +staplers +staples +stapling +star +starboard +starch +starched +starches +starchier +starchiest +starchily +starchiness +starching +starchy +stardom +stardust +stare +stared +starer +starers +stares +starfish +starfishes +stargaze +stargazed +stargazer +stargazers +stargazes +stargazing +staring +stark +starker +starkest +starkly +starkness +starless +starlet +starlets +starlight +starling +starlings +starlit +starred +starrier +starriest +starring +starry +stars +start +started +starter +starters +starting +startle +startled +startles +startling +starts +starvation +starve +starved +starveling +starvelings +starves +starving +stash +stashed +stashes +stashing +stat +state +statecraft +stated +statehood +statehouse +statehouses +stateless +statelessness +statelier +stateliest +stateliness +stately +statement +statemented +statementing +statements +stateroom +staterooms +states +stateside +statesman +statesmanlike +statesmanship +statesmen +stateswoman +stateswomen +static +statically +stating +station +stationary +stationed +stationer +stationers +stationery +stationing +stations +statistic +statistical +statistically +statistician +statisticians +statistics +stats +statuary +statue +statues +statuesque +statuette +statuettes +stature +statures +status +statuses +statute +statutes +statutorily +statutory +staunch +staunched +stauncher +staunches +staunchest +staunching +staunchly +staunchness +stave +staved +staves +staving +stay +stayed +staying +stays +stead +steadfast +steadfastly +steadfastness +steadied +steadier +steadies +steadiest +steadily +steadiness +steads +steady +steadying +steak +steakhouse +steakhouses +steaks +steal +stealing +steals +stealth +stealthier +stealthiest +stealthily +stealthiness +stealthy +steam +steamboat +steamboats +steamed +steamer +steamers +steamfitter +steamfitters +steamfitting +steamier +steamiest +steaminess +steaming +steamroll +steamrolled +steamroller +steamrollered +steamrollering +steamrollers +steamrolling +steamrolls +steams +steamship +steamships +steamy +steed +steeds +steel +steeled +steelier +steeliest +steeliness +steeling +steels +steelworker +steelworkers +steelworks +steely +steelyard +steelyards +steep +steeped +steepen +steepened +steepening +steepens +steeper +steepest +steeping +steeple +steeplechase +steeplechases +steeplejack +steeplejacks +steeples +steeply +steepness +steeps +steer +steerable +steerage +steered +steering +steers +steersman +steersmen +stegosauri +stegosaurus +stegosauruses +stein +steins +stellar +stem +stemless +stemmed +stemming +stems +stemware +stench +stenches +stencil +stenciled +stenciling +stencilled +stencilling +stencils +steno +stenographer +stenographers +stenographic +stenography +stenos +stentorian +step +stepbrother +stepbrothers +stepchild +stepchildren +stepdaughter +stepdaughters +stepfather +stepfathers +stepladder +stepladders +stepmother +stepmothers +stepparent +stepparents +steppe +stepped +stepper +steppers +steppes +stepping +steppingstone +steppingstones +steps +stepsister +stepsisters +stepson +stepsons +stereo +stereophonic +stereos +stereoscope +stereoscopes +stereoscopic +stereotype +stereotyped +stereotypes +stereotypical +stereotyping +sterile +sterility +sterilization +sterilize +sterilized +sterilizer +sterilizers +sterilizes +sterilizing +sterling +stern +sterna +sterner +sternest +sternly +sternness +sterns +sternum +sternums +steroid +steroidal +steroids +stertorous +stet +stethoscope +stethoscopes +stets +stetson +stetsons +stetted +stetting +stevedore +stevedores +stew +steward +stewarded +stewardess +stewardesses +stewarding +stewards +stewardship +stewed +stewing +stews +stick +sticker +stickers +stickier +stickies +stickiest +stickily +stickiness +sticking +stickleback +sticklebacks +stickler +sticklers +stickpin +stickpins +sticks +stickup +stickups +sticky +sties +stiff +stiffed +stiffen +stiffened +stiffener +stiffeners +stiffening +stiffens +stiffer +stiffest +stiffing +stiffly +stiffness +stiffs +stifle +stifled +stifles +stifling +stiflingly +stigma +stigmas +stigmata +stigmatic +stigmatization +stigmatize +stigmatized +stigmatizes +stigmatizing +stile +stiles +stiletto +stilettoes +stilettos +still +stillbirth +stillbirths +stillborn +stilled +stiller +stillest +stilling +stillness +stills +stilt +stilted +stilts +stimulant +stimulants +stimulate +stimulated +stimulates +stimulating +stimulation +stimulative +stimuli +stimulus +sting +stinger +stingers +stingier +stingiest +stingily +stinginess +stinging +stingray +stingrays +stings +stingy +stink +stinkbug +stinkbugs +stinker +stinkers +stinkier +stinkiest +stinking +stinks +stinky +stint +stinted +stinting +stints +stipend +stipends +stipple +stippled +stipples +stippling +stipulate +stipulated +stipulates +stipulating +stipulation +stipulations +stir +stirred +stirrer +stirrers +stirring +stirringly +stirrings +stirrup +stirrups +stirs +stitch +stitched +stitchery +stitches +stitching +stoat +stoats +stock +stockade +stockaded +stockades +stockading +stockbreeder +stockbreeders +stockbroker +stockbrokers +stockbroking +stocked +stockholder +stockholders +stockier +stockiest +stockily +stockiness +stockinet +stockinette +stocking +stockings +stockpile +stockpiled +stockpiles +stockpiling +stockpot +stockpots +stockroom +stockrooms +stocks +stocktaking +stocky +stockyard +stockyards +stodgier +stodgiest +stodgily +stodginess +stodgy +stogie +stogies +stogy +stoic +stoical +stoically +stoicism +stoics +stoke +stoked +stoker +stokers +stokes +stoking +stole +stolen +stoles +stolid +stolider +stolidest +stolidity +stolidly +stolidness +stolon +stolons +stomach +stomachache +stomachaches +stomached +stomacher +stomachers +stomaching +stomachs +stomp +stomped +stomping +stomps +stone +stoned +stonemason +stonemasons +stones +stonewall +stonewalled +stonewalling +stonewalls +stoneware +stonewashed +stonework +stoney +stonier +stoniest +stonily +stoniness +stoning +stony +stood +stooge +stooges +stool +stools +stoop +stooped +stooping +stoops +stop +stopcock +stopcocks +stopgap +stopgaps +stoplight +stoplights +stopover +stopovers +stoppage +stoppages +stopped +stopper +stoppered +stoppering +stoppers +stopping +stopple +stoppled +stopples +stoppling +stops +stopwatch +stopwatches +storage +store +stored +storefront +storefronts +storehouse +storehouses +storekeeper +storekeepers +storeroom +storerooms +stores +storey +storeys +storied +stories +storing +stork +storks +storm +stormed +stormier +stormiest +stormily +storminess +storming +storms +stormy +story +storyboard +storyboards +storybook +storybooks +storyteller +storytellers +storytelling +stoup +stoups +stout +stouter +stoutest +stouthearted +stoutly +stoutness +stove +stovepipe +stovepipes +stoves +stow +stowage +stowaway +stowaways +stowed +stowing +stows +straddle +straddled +straddler +straddlers +straddles +straddling +strafe +strafed +strafes +strafing +straggle +straggled +straggler +stragglers +straggles +stragglier +straggliest +straggling +straggly +straight +straightaway +straightaways +straightedge +straightedges +straighten +straightened +straightener +straighteners +straightening +straightens +straighter +straightest +straightforward +straightforwardly +straightforwardness +straightforwards +straightjacket +straightjacketed +straightjacketing +straightjackets +straightly +straightness +straights +straightway +strain +strained +strainer +strainers +straining +strains +strait +straiten +straitened +straitening +straitens +straitjacket +straitjacketed +straitjacketing +straitjackets +straitlaced +straits +strand +stranded +stranding +strands +strange +strangely +strangeness +stranger +strangers +strangest +strangle +strangled +stranglehold +strangleholds +strangler +stranglers +strangles +strangling +strangulate +strangulated +strangulates +strangulating +strangulation +strap +strapless +straplesses +strapped +strapping +straps +strata +stratagem +stratagems +strategic +strategical +strategically +strategics +strategies +strategist +strategists +strategy +strati +stratification +stratified +stratifies +stratify +stratifying +stratosphere +stratospheres +stratospheric +stratum +stratums +stratus +straw +strawberries +strawberry +straws +stray +strayed +straying +strays +streak +streaked +streaker +streakers +streakier +streakiest +streaking +streaks +streaky +stream +streamed +streamer +streamers +streaming +streamline +streamlined +streamlines +streamlining +streams +street +streetcar +streetcars +streetlight +streetlights +streets +streetwalker +streetwalkers +streetwise +strength +strengthen +strengthened +strengthener +strengtheners +strengthening +strengthens +strengths +strenuous +strenuously +strenuousness +strep +streptococcal +streptococci +streptococcus +streptomycin +stress +stressed +stresses +stressful +stressing +stretch +stretchable +stretched +stretcher +stretchers +stretches +stretchier +stretchiest +stretching +stretchy +strew +strewed +strewing +strewn +strews +stria +striae +striated +striation +striations +stricken +strict +stricter +strictest +strictly +strictness +stricture +strictures +stridden +stride +stridency +strident +stridently +strides +striding +strife +strike +strikebreaker +strikebreakers +strikeout +strikeouts +striker +strikers +strikes +striking +strikingly +string +stringed +stringency +stringent +stringently +stringer +stringers +stringier +stringiest +stringiness +stringing +strings +stringy +strip +stripe +striped +stripes +stripier +stripiest +striping +stripling +striplings +stripped +stripper +strippers +stripping +strips +stript +striptease +stripteased +stripteaser +stripteasers +stripteases +stripteasing +stripy +strive +strived +striven +strives +striving +strobe +strobes +stroboscope +stroboscopes +stroboscopic +strode +stroke +stroked +strokes +stroking +stroll +strolled +stroller +strollers +strolling +strolls +strong +strongbox +strongboxes +stronger +strongest +stronghold +strongholds +strongly +strongman +strongmen +strontium +strop +strophe +strophes +strophic +stropped +stropping +strops +strove +struck +structural +structurally +structure +structured +structures +structuring +strudel +strudels +struggle +struggled +struggles +struggling +strum +strummed +strumming +strumpet +strumpets +strums +strung +strut +struts +strutted +strutting +strychnine +stub +stubbed +stubbier +stubbiest +stubbing +stubble +stubblier +stubbliest +stubbly +stubborn +stubborner +stubbornest +stubbornly +stubbornness +stubby +stubs +stucco +stuccoed +stuccoes +stuccoing +stuccos +stuck +stud +studbook +studbooks +studded +studding +student +students +studied +studiedly +studies +studio +studios +studious +studiously +studiousness +studs +study +studying +stuff +stuffed +stuffier +stuffiest +stuffily +stuffiness +stuffing +stuffings +stuffs +stuffy +stultification +stultified +stultifies +stultify +stultifying +stumble +stumbled +stumbler +stumblers +stumbles +stumbling +stump +stumped +stumpier +stumpiest +stumping +stumps +stumpy +stun +stung +stunk +stunned +stunning +stunningly +stuns +stunt +stunted +stunting +stunts +stupefaction +stupefied +stupefies +stupefy +stupefying +stupendous +stupendously +stupid +stupider +stupidest +stupidities +stupidity +stupidly +stupids +stupor +stupors +sturdier +sturdiest +sturdily +sturdiness +sturdy +sturgeon +sturgeons +stutter +stuttered +stutterer +stutterers +stuttering +stutters +stye +styes +style +styled +styles +styli +styling +stylish +stylishly +stylishness +stylist +stylistic +stylistically +stylists +stylize +stylized +stylizes +stylizing +stylus +styluses +stymie +stymied +stymieing +stymies +stymy +stymying +styptic +styptics +suasion +suave +suavely +suaveness +suaver +suavest +suavity +subaltern +subalterns +subarctic +subarea +subareas +subatomic +subbasement +subbasements +subbed +subbing +subbranch +subbranches +subcategories +subcategory +subcommittee +subcommittees +subcompact +subcompacts +subconscious +subconsciously +subconsciousness +subcontinent +subcontinental +subcontinents +subcontract +subcontracted +subcontracting +subcontractor +subcontractors +subcontracts +subculture +subcultures +subcutaneous +subcutaneously +subdivide +subdivided +subdivides +subdividing +subdivision +subdivisions +subdue +subdued +subdues +subduing +subfamilies +subfamily +subfreezing +subgroup +subgroups +subhead +subheading +subheadings +subheads +subhuman +subhumans +subject +subjected +subjecting +subjection +subjective +subjectively +subjectivity +subjects +subjoin +subjoined +subjoining +subjoins +subjugate +subjugated +subjugates +subjugating +subjugation +subjunctive +subjunctives +sublease +subleased +subleases +subleasing +sublet +sublets +subletting +sublimate +sublimated +sublimates +sublimating +sublimation +sublime +sublimed +sublimely +sublimer +sublimes +sublimest +subliminal +subliming +sublimity +submarginal +submarine +submariner +submariners +submarines +submerge +submerged +submergence +submerges +submerging +submerse +submersed +submerses +submersible +submersibles +submersing +submersion +submicroscopic +submission +submissions +submissive +submissively +submissiveness +submit +submits +submitted +submitting +subnormal +suborbital +suborder +suborders +subordinate +subordinated +subordinates +subordinating +subordination +suborn +subornation +suborned +suborning +suborns +subpena +subpenaed +subpenaing +subpenas +subplot +subplots +subpoena +subpoenaed +subpoenaing +subpoenas +subprofessional +subprofessionals +subroutine +subroutines +subs +subscribe +subscribed +subscriber +subscribers +subscribes +subscribing +subscript +subscription +subscriptions +subscripts +subsection +subsections +subsequent +subsequently +subservience +subservient +subserviently +subset +subsets +subside +subsided +subsidence +subsides +subsidiaries +subsidiary +subsidies +subsiding +subsidization +subsidize +subsidized +subsidizer +subsidizers +subsidizes +subsidizing +subsidy +subsist +subsisted +subsistence +subsisting +subsists +subsoil +subsonic +subspecies +substance +substances +substandard +substantial +substantially +substantiate +substantiated +substantiates +substantiating +substantiation +substantiations +substantive +substantively +substantives +substation +substations +substitute +substituted +substitutes +substituting +substitution +substitutions +substrata +substrate +substrates +substratum +substratums +substructure +substructures +subsume +subsumed +subsumes +subsuming +subsurface +subsystem +subsystems +subteen +subteens +subtenancy +subtenant +subtenants +subterfuge +subterfuges +subterranean +subtext +subtexts +subtitle +subtitled +subtitles +subtitling +subtle +subtler +subtlest +subtleties +subtlety +subtly +subtopic +subtopics +subtotal +subtotaled +subtotaling +subtotalled +subtotalling +subtotals +subtract +subtracted +subtracting +subtraction +subtractions +subtracts +subtrahend +subtrahends +subtropic +subtropical +subtropics +suburb +suburban +suburbanite +suburbanites +suburbans +suburbia +suburbs +subvention +subventions +subversion +subversive +subversively +subversiveness +subversives +subvert +subverted +subverting +subverts +subway +subways +subzero +succeed +succeeded +succeeding +succeeds +success +successes +successful +successfully +succession +successions +successive +successively +successor +successors +succinct +succincter +succinctest +succinctly +succinctness +succor +succored +succoring +succors +succotash +succour +succoured +succouring +succours +succulence +succulency +succulent +succulents +succumb +succumbed +succumbing +succumbs +such +suchlike +suck +sucked +sucker +suckered +suckering +suckers +sucking +suckle +suckled +suckles +suckling +sucklings +sucks +sucrose +suction +suctioned +suctioning +suctions +sudden +suddenly +suddenness +suds +sudsier +sudsiest +sudsy +sued +suede +sues +suet +suety +suffer +sufferance +suffered +sufferer +sufferers +suffering +sufferings +suffers +suffice +sufficed +suffices +sufficiency +sufficient +sufficiently +sufficing +suffix +suffixation +suffixed +suffixes +suffixing +suffocate +suffocated +suffocates +suffocating +suffocation +suffragan +suffragans +suffrage +suffragette +suffragettes +suffragist +suffragists +suffuse +suffused +suffuses +suffusing +suffusion +sugar +sugarcane +sugarcoat +sugarcoated +sugarcoating +sugarcoats +sugared +sugarier +sugariest +sugaring +sugarless +sugarplum +sugarplums +sugars +sugary +suggest +suggested +suggestibility +suggestible +suggesting +suggestion +suggestions +suggestive +suggestively +suggestiveness +suggests +suicidal +suicide +suicides +suing +suit +suitability +suitable +suitableness +suitably +suitcase +suitcases +suite +suited +suites +suiting +suitor +suitors +suits +sukiyaki +sulfa +sulfate +sulfates +sulfide +sulfides +sulfur +sulfuric +sulfurous +sulk +sulked +sulkier +sulkies +sulkiest +sulkily +sulkiness +sulking +sulks +sulky +sullen +sullener +sullenest +sullenly +sullenness +sullied +sullies +sully +sullying +sulphur +sulphured +sulphuring +sulphurous +sulphurs +sultan +sultana +sultanas +sultanate +sultanates +sultans +sultrier +sultriest +sultrily +sultriness +sultry +sumac +sumach +summaries +summarily +summarize +summarized +summarizes +summarizing +summary +summation +summations +summed +summer +summered +summerhouse +summerhouses +summering +summers +summertime +summery +summing +summit +summitry +summits +summon +summoned +summoner +summoners +summoning +summons +summonsed +summonses +summonsing +sumo +sump +sumps +sumptuous +sumptuously +sumptuousness +sums +sunbath +sunbathe +sunbathed +sunbather +sunbathers +sunbathes +sunbathing +sunbaths +sunbeam +sunbeams +sunbelt +sunbelts +sunblock +sunblocks +sunbonnet +sunbonnets +sunburn +sunburned +sunburning +sunburns +sunburnt +sunburst +sunbursts +sundae +sundaes +sunder +sundered +sundering +sunders +sundial +sundials +sundown +sundowns +sundries +sundry +sunfish +sunfishes +sunflower +sunflowers +sung +sunglasses +sunk +sunken +sunlamp +sunlamps +sunless +sunlight +sunlit +sunned +sunnier +sunniest +sunniness +sunning +sunny +sunrise +sunrises +sunroof +sunroofs +suns +sunscreen +sunscreens +sunset +sunsets +sunshade +sunshades +sunshine +sunshiny +sunspot +sunspots +sunstroke +suntan +suntanned +suntanning +suntans +sunup +super +superabundance +superabundances +superabundant +superannuate +superannuated +superannuates +superannuating +superannuation +superb +superber +superbest +superbly +supercargo +supercargoes +supercargos +supercharge +supercharged +supercharger +superchargers +supercharges +supercharging +supercilious +superciliously +superciliousness +supercities +supercity +supercomputer +supercomputers +superconducting +superconductive +superconductivity +superconductor +superconductors +superego +superegos +supererogation +supererogatory +superficial +superficiality +superficially +superfine +superfluity +superfluous +superfluously +superfluousness +superhero +superheroes +superhighway +superhighways +superhuman +superimpose +superimposed +superimposes +superimposing +superimposition +superintend +superintended +superintendence +superintendency +superintendent +superintendents +superintending +superintends +superior +superiority +superiors +superlative +superlatively +superlatives +superman +supermarket +supermarkets +supermen +supermom +supermoms +supernal +supernatural +supernaturally +supernova +supernovae +supernovas +supernumeraries +supernumerary +superpose +superposed +superposes +superposing +superposition +superpower +superpowers +supers +supersaturate +supersaturated +supersaturates +supersaturating +supersaturation +superscribe +superscribed +superscribes +superscribing +superscript +superscription +superscripts +supersede +superseded +supersedes +superseding +supersonic +superstar +superstars +superstition +superstitions +superstitious +superstitiously +superstore +superstores +superstructure +superstructures +supertanker +supertankers +supervene +supervened +supervenes +supervening +supervention +supervise +supervised +supervises +supervising +supervision +supervisor +supervisors +supervisory +superwoman +superwomen +supine +supped +supper +suppers +supping +supplant +supplanted +supplanting +supplants +supple +supplement +supplemental +supplementary +supplementation +supplemented +supplementing +supplements +suppleness +suppler +supplest +suppliant +suppliants +supplicant +supplicants +supplicate +supplicated +supplicates +supplicating +supplication +supplications +supplied +supplier +suppliers +supplies +supply +supplying +support +supportable +supported +supporter +supporters +supporting +supportive +supports +suppose +supposed +supposedly +supposes +supposing +supposition +suppositions +suppositories +suppository +suppress +suppressant +suppressants +suppressed +suppresses +suppressible +suppressing +suppression +suppressor +suppressors +suppurate +suppurated +suppurates +suppurating +suppuration +supra +supranational +supremacist +supremacists +supremacy +supreme +supremely +sups +surcease +surceased +surceases +surceasing +surcharge +surcharged +surcharges +surcharging +surcingle +surcingles +sure +surefire +surefooted +surely +sureness +surer +surest +sureties +surety +surf +surface +surfaced +surfaces +surfacing +surfboard +surfboarded +surfboarding +surfboards +surfed +surfeit +surfeited +surfeiting +surfeits +surfer +surfers +surfing +surfs +surge +surged +surgeon +surgeons +surgeries +surgery +surges +surgical +surgically +surging +surlier +surliest +surliness +surly +surmise +surmised +surmises +surmising +surmount +surmountable +surmounted +surmounting +surmounts +surname +surnames +surpass +surpassed +surpasses +surpassing +surplice +surplices +surplus +surplused +surpluses +surplusing +surplussed +surplussing +surprise +surprised +surprises +surprising +surprisingly +surreal +surrealism +surrealist +surrealistic +surrealistically +surrealists +surrender +surrendered +surrendering +surrenders +surreptitious +surreptitiously +surreptitiousness +surrey +surreys +surrogacy +surrogate +surrogates +surround +surrounded +surrounding +surroundings +surrounds +surtax +surtaxed +surtaxes +surtaxing +surveillance +survey +surveyed +surveying +surveyor +surveyors +surveys +survival +survivalist +survivalists +survivals +survive +survived +survives +surviving +survivor +survivors +susceptibility +susceptible +sushi +suspect +suspected +suspecting +suspects +suspend +suspended +suspender +suspenders +suspending +suspends +suspense +suspenseful +suspension +suspensions +suspicion +suspicions +suspicious +suspiciously +sustain +sustainable +sustained +sustaining +sustains +sustenance +sutler +sutlers +suture +sutured +sutures +suturing +suzerain +suzerains +suzerainty +svelte +svelter +sveltest +swab +swabbed +swabbing +swabs +swaddle +swaddled +swaddles +swaddling +swag +swagged +swagger +swaggered +swaggering +swaggers +swagging +swags +swain +swains +swallow +swallowed +swallowing +swallows +swallowtail +swallowtails +swam +swami +swamis +swamp +swamped +swampier +swampiest +swamping +swampland +swamps +swampy +swan +swank +swanked +swanker +swankest +swankier +swankiest +swankily +swankiness +swanking +swanks +swanky +swans +swansdown +swap +swapped +swapping +swaps +sward +swards +swarm +swarmed +swarming +swarms +swarthier +swarthiest +swarthy +swash +swashbuckler +swashbucklers +swashbuckling +swashed +swashes +swashing +swastika +swastikas +swat +swatch +swatches +swath +swathe +swathed +swathes +swathing +swaths +swats +swatted +swatter +swatters +swatting +sway +swayback +swaybacked +swayed +swaying +sways +swear +swearer +swearers +swearing +swears +swearword +swearwords +sweat +sweatband +sweatbands +sweated +sweater +sweaters +sweatier +sweatiest +sweating +sweatpants +sweats +sweatshirt +sweatshirts +sweatshop +sweatshops +sweaty +swede +swedes +sweep +sweeper +sweepers +sweeping +sweepingly +sweepings +sweeps +sweepstake +sweepstakes +sweet +sweetbread +sweetbreads +sweetbriar +sweetbriars +sweetbrier +sweetbriers +sweeten +sweetened +sweetener +sweeteners +sweetening +sweetens +sweeter +sweetest +sweetheart +sweethearts +sweetie +sweeties +sweetish +sweetly +sweetmeat +sweetmeats +sweetness +sweets +swell +swelled +sweller +swellest +swellhead +swellheaded +swellheads +swelling +swellings +swells +swelter +sweltered +sweltering +swelters +swept +sweptback +swerve +swerved +swerves +swerving +swift +swifter +swiftest +swiftly +swiftness +swifts +swig +swigged +swigging +swigs +swill +swilled +swilling +swills +swim +swimmer +swimmers +swimming +swimmingly +swims +swimsuit +swimsuits +swindle +swindled +swindler +swindlers +swindles +swindling +swine +swineherd +swineherds +swines +swing +swinger +swingers +swinging +swings +swinish +swipe +swiped +swipes +swiping +swirl +swirled +swirlier +swirliest +swirling +swirls +swirly +swish +swished +swisher +swishes +swishest +swishing +switch +switchback +switchbacks +switchblade +switchblades +switchboard +switchboards +switched +switcher +switchers +switches +switching +swivel +swiveled +swiveling +swivelled +swivelling +swivels +swob +swobbed +swobbing +swobs +swollen +swoon +swooned +swooning +swoons +swoop +swooped +swooping +swoops +swoosh +swooshed +swooshes +swooshing +swop +swopped +swopping +swops +sword +swordfish +swordfishes +swordplay +swords +swordsman +swordsmanship +swordsmen +swore +sworn +swum +swung +sybarite +sybarites +sybaritic +sycamore +sycamores +sycophancy +sycophant +sycophantic +sycophants +syllabi +syllabic +syllabicate +syllabicated +syllabicates +syllabicating +syllabication +syllabification +syllabified +syllabifies +syllabify +syllabifying +syllable +syllables +syllabus +syllabuses +syllogism +syllogisms +syllogistic +sylph +sylphic +sylphlike +sylphs +sylvan +symbioses +symbiosis +symbiotic +symbol +symbolic +symbolical +symbolically +symbolism +symbolization +symbolize +symbolized +symbolizes +symbolizing +symbols +symmetric +symmetrical +symmetrically +symmetry +sympathetic +sympathetically +sympathies +sympathize +sympathized +sympathizer +sympathizers +sympathizes +sympathizing +sympathy +symphonic +symphonies +symphony +symposia +symposium +symposiums +symptom +symptomatic +symptomatically +symptoms +synagog +synagogal +synagogs +synagogue +synagogues +synapse +synapses +synaptic +sync +synced +synch +synched +synching +synchronization +synchronizations +synchronize +synchronized +synchronizes +synchronizing +synchronous +synchs +syncing +syncopate +syncopated +syncopates +syncopating +syncopation +syncope +syncs +syndicate +syndicated +syndicates +syndicating +syndication +syndrome +syndromes +synergism +synergistic +synergy +synfuel +synfuels +synod +synods +synonym +synonymous +synonyms +synonymy +synopses +synopsis +synoptic +syntactic +syntactical +syntactically +syntax +syntheses +synthesis +synthesize +synthesized +synthesizer +synthesizers +synthesizes +synthesizing +synthetic +synthetically +synthetics +syphilis +syphilitic +syphilitics +syphon +syphoned +syphoning +syphons +syringe +syringed +syringes +syringing +syrup +syrups +syrupy +system +systematic +systematical +systematically +systematization +systematize +systematized +systematizes +systematizing +systemic +systemically +systemics +systems +systole +systoles +systolic +tabbed +tabbies +tabbing +tabbouleh +tabby +tabernacle +tabernacles +tabla +tablas +table +tableau +tableaus +tableaux +tablecloth +tablecloths +tabled +tableland +tablelands +tables +tablespoon +tablespoonful +tablespoonfuls +tablespoons +tablespoonsful +tablet +tabletop +tabletops +tablets +tableware +tabling +tabloid +tabloids +taboo +tabooed +tabooing +taboos +tabor +tabors +tabs +tabu +tabued +tabuing +tabular +tabulate +tabulated +tabulates +tabulating +tabulation +tabulator +tabulators +tabus +tachometer +tachometers +tachycardia +tacit +tacitly +tacitness +taciturn +taciturnity +taciturnly +tack +tacked +tacker +tackers +tackier +tackiest +tackiness +tacking +tackle +tackled +tackler +tacklers +tackles +tackling +tacks +tacky +taco +tacos +tact +tactful +tactfully +tactfulness +tactic +tactical +tactically +tactician +tacticians +tactics +tactile +tactility +tactless +tactlessly +tactlessness +tadpole +tadpoles +tads +taffeta +taffies +taffrail +taffrails +taffy +tagged +tagger +taggers +tagging +tags +taiga +taigas +tail +tailback +tailbacks +tailcoat +tailcoats +tailed +tailgate +tailgated +tailgater +tailgaters +tailgates +tailgating +tailing +tailless +taillight +taillights +tailor +tailored +tailoring +tailors +tailpipe +tailpipes +tails +tailspin +tailspins +tailwind +tailwinds +taint +tainted +tainting +taints +take +taken +takeoff +takeoffs +takeout +takeouts +takeover +takeovers +taker +takers +takes +taking +takings +talc +talcum +tale +talebearer +talebearers +talent +talented +talents +tales +tali +talisman +talismans +talk +talkative +talkatively +talkativeness +talked +talker +talkers +talkie +talkier +talkies +talkiest +talking +talks +talky +tall +tallboy +tallboys +taller +tallest +tallied +tallier +talliers +tallies +tallish +tallness +tallow +tallowy +tally +tallyho +tallyhoed +tallyhoing +tallyhos +tallying +talon +talons +talus +taluses +tamable +tamale +tamales +tamarack +tamaracks +tamarind +tamarinds +tambourine +tambourines +tame +tameable +tamed +tamely +tameness +tamer +tamers +tames +tamest +taming +tamp +tamped +tamper +tampered +tamperer +tamperers +tampering +tampers +tamping +tampon +tampons +tamps +tams +tanager +tanagers +tanbark +tandem +tandems +tandoori +tang +tangelo +tangelos +tangent +tangential +tangentially +tangents +tangerine +tangerines +tangibility +tangible +tangibleness +tangibles +tangibly +tangier +tangiest +tangle +tangled +tangles +tangling +tango +tangoed +tangoing +tangos +tangs +tangy +tank +tankard +tankards +tanked +tanker +tankers +tankful +tankfuls +tanking +tanks +tanned +tanner +tanneries +tanners +tannery +tannest +tannin +tanning +tans +tansy +tantalise +tantalised +tantalises +tantalising +tantalization +tantalize +tantalized +tantalizer +tantalizers +tantalizes +tantalizing +tantalizingly +tantalum +tantamount +tantra +tantrum +tantrums +tape +taped +tapeline +tapelines +taper +tapered +tapering +tapers +tapes +tapestries +tapestry +tapeworm +tapeworms +taping +tapioca +tapir +tapirs +tapped +tapper +tappers +tappet +tappets +tapping +taproom +taprooms +taproot +taproots +taps +tarantella +tarantellas +tarantula +tarantulae +tarantulas +tardier +tardiest +tardily +tardiness +tardy +tare +tared +tares +target +targeted +targeting +targets +tariff +tariffs +taring +tarmac +tarmacked +tarmacking +tarmacs +tarn +tarnish +tarnished +tarnishes +tarnishing +tarns +taro +taros +tarot +tarots +tarp +tarpaulin +tarpaulins +tarpon +tarpons +tarps +tarragon +tarragons +tarred +tarried +tarrier +tarries +tarriest +tarring +tarry +tarrying +tars +tarsal +tarsals +tarsi +tarsus +tart +tartan +tartans +tartar +tartaric +tartars +tarter +tartest +tartly +tartness +tarts +task +tasked +tasking +taskmaster +taskmasters +taskmistress +taskmistresses +tasks +tassel +tasseled +tasseling +tasselled +tasselling +tassels +taste +tasted +tasteful +tastefully +tastefulness +tasteless +tastelessly +tastelessness +taster +tasters +tastes +tastier +tastiest +tastily +tastiness +tasting +tastings +tasty +tatami +tatamis +tater +taters +tats +tatted +tatter +tatterdemalion +tatterdemalions +tattered +tattering +tatters +tatting +tattle +tattled +tattler +tattlers +tattles +tattletale +tattletales +tattling +tattoo +tattooed +tattooer +tattooers +tattooing +tattooist +tattooists +tattoos +taught +taunt +taunted +taunter +taunters +taunting +tauntingly +taunts +taupe +taus +taut +tauten +tautened +tautening +tautens +tauter +tautest +tautly +tautness +tautological +tautologically +tautologies +tautologous +tautology +tavern +taverns +tawdrier +tawdriest +tawdrily +tawdriness +tawdry +tawnier +tawniest +tawny +taxable +taxation +taxed +taxer +taxers +taxes +taxi +taxicab +taxicabs +taxidermist +taxidermists +taxidermy +taxied +taxies +taxiing +taximeter +taximeters +taxing +taxis +taxonomic +taxonomical +taxonomies +taxonomist +taxonomists +taxonomy +taxpayer +taxpayers +taxpaying +taxying +teacake +teacakes +teach +teachable +teacher +teachers +teaches +teaching +teachings +teacup +teacupful +teacupfuls +teacups +teak +teakettle +teakettles +teaks +teal +teals +team +teamed +teaming +teammate +teammates +teams +teamster +teamsters +teamwork +teapot +teapots +tear +teardrop +teardrops +teared +tearful +tearfully +teargas +teargases +teargassed +teargasses +teargassing +tearier +teariest +tearing +tearjerker +tearjerkers +tearoom +tearooms +tears +teary +teas +tease +teased +teasel +teasels +teaser +teasers +teases +teasing +teaspoon +teaspoonful +teaspoonfuls +teaspoons +teaspoonsful +teat +teats +tech +technetium +technical +technicalities +technicality +technically +technician +technicians +technique +techniques +technocracy +technocrat +technocratic +technocrats +technological +technologically +technologies +technologist +technologists +technology +techs +tectonic +tectonics +tedious +tediously +tediousness +tedium +teed +teeing +teem +teemed +teeming +teems +teen +teenage +teenaged +teenager +teenagers +teenier +teeniest +teens +teensier +teensiest +teensy +teeny +teenybopper +teenyboppers +teepee +teepees +tees +teeter +teetered +teetering +teeters +teeth +teethe +teethed +teethes +teething +teetotal +teetotaler +teetotalers +teetotalism +teetotaller +teetotallers +tektite +tektites +telecast +telecasted +telecaster +telecasters +telecasting +telecasts +telecommunication +telecommunications +telecommute +telecommuted +telecommuter +telecommuters +telecommutes +telecommuting +teleconference +teleconferenced +teleconferences +teleconferencing +telegenic +telegram +telegrams +telegraph +telegraphed +telegrapher +telegraphers +telegraphic +telegraphically +telegraphing +telegraphist +telegraphists +telegraphs +telegraphy +telekinesis +telekinetic +telemarketer +telemarketers +telemarketing +telemeter +telemeters +telemetries +telemetry +telepathic +telepathically +telepathy +telephone +telephoned +telephoner +telephoners +telephones +telephonic +telephoning +telephony +telephoto +telephotography +telephotos +teleplay +teleplays +teleprinter +teleprinters +teleprocessing +teleprompter +teleprompters +telescope +telescoped +telescopes +telescopic +telescopically +telescoping +teletext +teletexts +telethon +telethons +teletypewriter +teletypewriters +televangelism +televangelist +televangelists +televise +televised +televises +televising +television +televisions +telex +telexed +telexes +telexing +tell +teller +tellers +tellies +telling +tellingly +tells +telltale +telltales +tellurium +telly +temblor +temblors +temerity +temp +temped +temper +tempera +temperament +temperamental +temperamentally +temperaments +temperance +temperas +temperate +temperately +temperateness +temperature +temperatures +tempered +tempering +tempers +tempest +tempests +tempestuous +tempestuously +tempestuousness +tempi +temping +template +templates +temple +temples +tempo +temporal +temporally +temporaries +temporarily +temporariness +temporary +temporize +temporized +temporizer +temporizers +temporizes +temporizing +tempos +temps +tempt +temptation +temptations +tempted +tempter +tempters +tempting +temptingly +temptress +temptresses +tempts +tempura +tenability +tenable +tenably +tenacious +tenaciously +tenaciousness +tenacity +tenancies +tenancy +tenant +tenanted +tenanting +tenantry +tenants +tend +tended +tendencies +tendency +tendentious +tendentiously +tendentiousness +tender +tendered +tenderer +tenderest +tenderfeet +tenderfoot +tenderfoots +tenderhearted +tenderheartedness +tendering +tenderize +tenderized +tenderizer +tenderizers +tenderizes +tenderizing +tenderloin +tenderloins +tenderly +tenderness +tenders +tending +tendinitis +tendon +tendonitis +tendons +tendril +tendrils +tends +tenement +tenements +tenet +tenets +tenfold +tennis +tenon +tenoned +tenoning +tenons +tenor +tenors +tenpin +tenpins +tens +tense +tensed +tensely +tenseness +tenser +tenses +tensest +tensile +tensing +tension +tensions +tensity +tent +tentacle +tentacled +tentacles +tentative +tentatively +tentativeness +tented +tenterhook +tenterhooks +tenth +tenthly +tenths +tenting +tents +tenuity +tenuous +tenuously +tenuousness +tenure +tenured +tenures +tenuring +tepee +tepees +tepid +tepider +tepidest +tepidity +tepidly +tepidness +tequila +tequilas +terabyte +terabytes +terbium +tercentenaries +tercentenary +tercentennial +tercentennials +term +termagant +termagants +termed +terminable +terminal +terminally +terminals +terminate +terminated +terminates +terminating +termination +terminations +terming +termini +terminological +terminologically +terminologies +terminology +terminus +terminuses +termite +termites +termly +terms +tern +ternaries +ternary +terns +terrace +terraced +terraces +terracing +terracotta +terrain +terrains +terrapin +terrapins +terraria +terrarium +terrariums +terrazzo +terrazzos +terrestrial +terrestrially +terrestrials +terrible +terribleness +terribly +terrier +terriers +terrific +terrifically +terrified +terrifies +terrify +terrifying +terrifyingly +territorial +territorials +territories +territory +terror +terrorism +terrorist +terrorists +terrorize +terrorized +terrorizes +terrorizing +terrors +terry +terrycloth +terse +tersely +terseness +terser +tersest +tertiary +tessellate +tessellated +tessellates +tessellating +tessellation +tessellations +test +testament +testamentary +testaments +testate +testator +testators +testatrices +testatrix +tested +tester +testers +testes +testicle +testicles +testier +testiest +testified +testifier +testifiers +testifies +testify +testifying +testily +testimonial +testimonials +testimonies +testimony +testiness +testing +testis +testosterone +tests +testy +tetanus +tetchier +tetchiest +tetchy +tether +tethered +tethering +tethers +tetra +tetracycline +tetrahedra +tetrahedral +tetrahedron +tetrahedrons +tetrameter +tetrameters +tetras +text +textbook +textbooks +textile +textiles +texts +textual +textually +textural +texture +textured +textures +texturing +thalami +thalamus +thalidomide +thallium +than +thane +thanes +thank +thanked +thankful +thankfully +thankfulness +thanking +thankless +thanklessly +thanklessness +thanks +thanksgiving +thanksgivings +that +thatch +thatched +thatcher +thatchers +thatches +thatching +thaw +thawed +thawing +thaws +theater +theatergoer +theatergoers +theaters +theatre +theatres +theatrical +theatricality +theatrically +theatricals +theatrics +thee +theft +thefts +their +theirs +theism +theist +theistic +theists +them +thematic +thematically +theme +themes +themselves +then +thence +thenceforth +thenceforward +theocracies +theocracy +theocratic +theologian +theologians +theological +theologically +theologies +theology +theorem +theorems +theoretic +theoretical +theoretically +theoretician +theoreticians +theories +theorist +theorists +theorize +theorized +theorizes +theorizing +theory +theosophic +theosophical +theosophist +theosophists +theosophy +therapeutic +therapeutically +therapeutics +therapies +therapist +therapists +therapy +there +thereabout +thereabouts +thereafter +thereat +thereby +therefor +therefore +therein +thereof +thereon +thereto +theretofore +thereunto +thereupon +therewith +therm +thermal +thermally +thermals +thermodynamic +thermodynamics +thermometer +thermometers +thermometric +thermonuclear +thermoplastic +thermoplastics +thermos +thermoses +thermostat +thermostatic +thermostatically +thermostats +therms +thesauri +thesaurus +thesauruses +these +theses +thesis +thespian +thespians +theta +thetas +thew +thews +they +thiamin +thiamine +thick +thicken +thickened +thickener +thickeners +thickening +thickenings +thickens +thicker +thickest +thicket +thickets +thickheaded +thickly +thickness +thicknesses +thickset +thief +thieve +thieved +thievery +thieves +thieving +thievish +thigh +thighbone +thighbones +thighs +thimble +thimbleful +thimblefuls +thimbles +thin +thine +thing +thingamabob +thingamabobs +thingamajig +thingamajigs +things +think +thinkable +thinker +thinkers +thinking +thinks +thinly +thinned +thinner +thinners +thinness +thinnest +thinning +thins +third +thirdly +thirds +thirst +thirsted +thirstier +thirstiest +thirstily +thirstiness +thirsting +thirsts +thirsty +thirteen +thirteens +thirteenth +thirteenths +thirties +thirtieth +thirtieths +thirty +this +thistle +thistledown +thistles +thither +thole +tholes +thong +thongs +thoraces +thoracic +thorax +thoraxes +thorium +thorn +thornier +thorniest +thorniness +thorns +thorny +thorough +thoroughbred +thoroughbreds +thorougher +thoroughest +thoroughfare +thoroughfares +thoroughgoing +thoroughly +thoroughness +those +thou +though +thought +thoughtful +thoughtfully +thoughtfulness +thoughtless +thoughtlessly +thoughtlessness +thoughts +thous +thousand +thousandfold +thousands +thousandth +thousandths +thraldom +thrall +thralldom +thralled +thralling +thralls +thrash +thrashed +thrasher +thrashers +thrashes +thrashing +thrashings +thread +threadbare +threaded +threader +threaders +threadier +threadiest +threading +threadlike +threads +thready +threat +threaten +threatened +threatening +threateningly +threatens +threats +three +threefold +threepence +threepences +threes +threescore +threescores +threesome +threesomes +threnodies +threnody +thresh +threshed +thresher +threshers +threshes +threshing +threshold +thresholds +threw +thrice +thrift +thriftier +thriftiest +thriftily +thriftiness +thriftless +thrifts +thrifty +thrill +thrilled +thriller +thrillers +thrilling +thrillingly +thrills +thrive +thrived +thriven +thrives +thriving +throat +throatier +throatiest +throatily +throatiness +throats +throaty +throb +throbbed +throbbing +throbs +throe +throes +thrombi +thromboses +thrombosis +thrombotic +thrombus +throne +thrones +throng +thronged +thronging +throngs +throttle +throttled +throttler +throttlers +throttles +throttling +through +throughout +throughput +throughway +throughways +throve +throw +throwaway +throwaways +throwback +throwbacks +thrower +throwers +throwing +thrown +throws +thru +thrum +thrummed +thrumming +thrums +thrush +thrushes +thrust +thrusting +thrusts +thruway +thruways +thud +thudded +thudding +thuds +thug +thuggery +thuggish +thugs +thulium +thumb +thumbed +thumbing +thumbnail +thumbnails +thumbprint +thumbprints +thumbs +thumbscrew +thumbscrews +thumbtack +thumbtacks +thump +thumped +thumping +thumpings +thumps +thunder +thunderbolt +thunderbolts +thunderclap +thunderclaps +thundercloud +thunderclouds +thundered +thunderer +thunderers +thunderhead +thunderheads +thundering +thunderous +thunderously +thunders +thundershower +thundershowers +thunderstorm +thunderstorms +thunderstricken +thunderstruck +thus +thwack +thwacked +thwacker +thwackers +thwacking +thwacks +thwart +thwarted +thwarting +thwarts +thyme +thymi +thymine +thymus +thymuses +thyroid +thyroidal +thyroids +thyself +tiara +tiaras +tibia +tibiae +tibial +tibias +tick +ticked +ticker +tickers +ticket +ticketed +ticketing +tickets +ticking +tickle +tickled +tickler +ticklers +tickles +tickling +ticklish +ticklishly +ticklishness +ticks +ticktacktoe +ticktock +ticktocks +tics +tidal +tidally +tidbit +tidbits +tiddlywinks +tide +tided +tideland +tidelands +tides +tidewater +tidewaters +tideway +tideways +tidied +tidier +tidies +tidiest +tidily +tidiness +tiding +tidings +tidy +tidying +tieback +tiebacks +tiebreaker +tiebreakers +tied +tieing +tier +tiered +tiers +ties +tiff +tiffed +tiffing +tiffs +tiger +tigerish +tigers +tight +tighten +tightened +tightener +tighteners +tightening +tightens +tighter +tightest +tightfisted +tightly +tightness +tightrope +tightropes +tights +tightwad +tightwads +tigress +tigresses +tike +tikes +tilde +tildes +tile +tiled +tiler +tilers +tiles +tiling +till +tillable +tillage +tilled +tiller +tillers +tilling +tills +tilt +tilted +tilting +tilts +timber +timbered +timbering +timberland +timberline +timberlines +timbers +timbre +timbrel +timbrels +timbres +time +timed +timekeeper +timekeepers +timekeeping +timeless +timelessly +timelessness +timelier +timeliest +timeliness +timely +timeout +timeouts +timepiece +timepieces +timer +timers +times +timeserver +timeservers +timeserving +timetable +timetabled +timetables +timetabling +timeworn +timid +timider +timidest +timidity +timidly +timidness +timing +timorous +timorously +timorousness +timothy +timpani +timpanist +timpanists +tincture +tinctured +tinctures +tincturing +tinder +tinderbox +tinderboxes +tine +tines +tinfoil +ting +tinge +tinged +tingeing +tinges +tinging +tingle +tingled +tingles +tinglier +tingliest +tingling +tinglings +tingly +tings +tinier +tiniest +tininess +tinker +tinkered +tinkerer +tinkerers +tinkering +tinkers +tinkle +tinkled +tinkles +tinkling +tinned +tinnier +tinniest +tinniness +tinning +tinnitus +tinny +tinplate +tins +tinsel +tinseled +tinseling +tinselled +tinselling +tinsels +tinsmith +tinsmiths +tint +tinted +tinting +tintinnabulation +tintinnabulations +tints +tintype +tintypes +tinware +tiny +tipi +tipis +tipped +tipper +tippers +tippet +tippets +tipping +tipple +tippled +tippler +tipplers +tipples +tippling +tips +tipsier +tipsiest +tipsily +tipsiness +tipster +tipsters +tipsy +tiptoe +tiptoed +tiptoeing +tiptoes +tiptop +tiptops +tirade +tirades +tire +tired +tireder +tiredest +tiredly +tiredness +tireless +tirelessly +tirelessness +tires +tiresome +tiresomely +tiresomeness +tiring +tiro +tiros +tissue +tissues +titan +titanic +titanium +titans +titbit +titbits +tithe +tithed +tither +tithers +tithes +tithing +titian +titillate +titillated +titillates +titillating +titillatingly +titillation +titivate +titivated +titivates +titivating +titivation +title +titled +titleholder +titleholders +titles +titling +titlist +titlists +titmice +titmouse +tits +titter +tittered +tittering +titters +tittivate +tittivated +tittivates +tittivating +tittle +tittles +titular +tizzies +tizzy +toad +toadied +toadies +toads +toadstool +toadstools +toady +toadying +toadyism +toast +toasted +toaster +toasters +toastier +toastiest +toasting +toastmaster +toastmasters +toastmistress +toastmistresses +toasts +toasty +tobacco +tobaccoes +tobacconist +tobacconists +tobaccos +toboggan +tobogganed +tobogganer +tobogganers +tobogganing +toboggans +tocsin +tocsins +today +toddies +toddle +toddled +toddler +toddlers +toddles +toddling +toddy +toecap +toecaps +toed +toehold +toeholds +toeing +toenail +toenails +toes +toffee +toffees +toffies +toffy +tofu +toga +togae +togaed +togas +together +togetherness +togged +togging +toggle +toggled +toggles +toggling +togs +toil +toiled +toiler +toilers +toilet +toileted +toileting +toiletries +toiletry +toilets +toilette +toiling +toils +toilsome +toke +toked +token +tokenism +tokens +tokes +toking +told +tole +tolerable +tolerably +tolerance +tolerances +tolerant +tolerantly +tolerate +tolerated +tolerates +tolerating +toleration +toll +tollbooth +tollbooths +tolled +tollgate +tollgates +tolling +tolls +tollway +tollways +toluene +tomahawk +tomahawked +tomahawking +tomahawks +tomato +tomatoes +tomb +tombed +tombing +tomboy +tomboyish +tomboys +tombs +tombstone +tombstones +tomcat +tomcats +tome +tomes +tomfooleries +tomfoolery +tomographic +tomography +tomorrow +tomorrows +toms +tomtit +tomtits +tonal +tonalities +tonality +tonally +tone +tonearm +tonearms +toned +toneless +tonelessly +toner +tones +tong +tonged +tonging +tongs +tongue +tongued +tongueless +tongues +tonguing +tonic +tonics +tonier +toniest +tonight +toning +tonnage +tonnages +tonne +tonnes +tons +tonsil +tonsillectomies +tonsillectomy +tonsillitis +tonsils +tonsorial +tonsure +tonsured +tonsures +tonsuring +tony +took +tool +toolbox +toolboxes +tooled +tooling +toolmaker +toolmakers +tools +toot +tooted +tooter +tooters +tooth +toothache +toothaches +toothbrush +toothbrushes +toothed +toothier +toothiest +toothily +toothless +toothpaste +toothpastes +toothpick +toothpicks +toothsome +toothy +tooting +tootle +tootled +tootles +tootling +toots +topaz +topazes +topcoat +topcoats +topdressing +topdressings +topflight +topiary +topic +topical +topicality +topically +topics +topknot +topknots +topless +topmast +topmasts +topmost +topnotch +topographer +topographers +topographic +topographical +topographically +topographies +topography +topped +topper +toppers +topping +toppings +topple +toppled +topples +toppling +tops +topsail +topsails +topside +topsides +topsoil +topspin +toque +toques +torah +torahs +torch +torchbearer +torchbearers +torched +torches +torching +torchlight +tore +toreador +toreadors +torment +tormented +tormenter +tormenters +tormenting +tormentingly +tormentor +tormentors +torments +torn +tornado +tornadoes +tornados +torpedo +torpedoed +torpedoes +torpedoing +torpid +torpidity +torpidly +torpor +torque +torqued +torques +torquing +torrent +torrential +torrents +torrid +torridity +torridly +torridness +tors +torsi +torsion +torsional +torso +torsos +tort +torte +tortellini +tortes +tortilla +tortillas +tortoise +tortoises +tortoiseshell +tortoiseshells +tortoni +torts +tortuous +tortuously +tortuousness +torture +tortured +torturer +torturers +tortures +torturing +torturous +toss +tossed +tosses +tossing +tossup +tossups +tost +total +totaled +totaling +totalisator +totalisators +totalitarian +totalitarianism +totalitarians +totalities +totality +totalizator +totalizators +totalled +totalling +totally +totals +tote +toted +totem +totemic +totems +totes +toting +tots +totted +totter +tottered +totterer +totterers +tottering +totters +totting +toucan +toucans +touch +touchable +touchdown +touchdowns +touche +touched +touches +touchier +touchiest +touchily +touchiness +touching +touchingly +touchscreen +touchscreens +touchstone +touchstones +touchy +tough +toughen +toughened +toughener +tougheners +toughening +toughens +tougher +toughest +toughie +toughies +toughly +toughness +toughs +toupee +toupees +tour +toured +touring +tourism +tourist +tourists +tourmaline +tournament +tournaments +tourney +tourneys +tourniquet +tourniquets +tours +tousle +tousled +tousles +tousling +tout +touted +touting +touts +toward +towards +towboat +towboats +towed +towel +toweled +towelette +towelettes +toweling +towelled +towelling +towels +tower +towered +towering +towers +towhead +towheaded +towheads +towhee +towhees +towing +towline +towlines +town +townhouse +townhouses +townie +townies +towns +townsfolk +township +townships +townsman +townsmen +townspeople +townswoman +townswomen +towpath +towpaths +towrope +towropes +tows +toxemia +toxic +toxicity +toxicological +toxicologist +toxicologists +toxicology +toxin +toxins +toyed +toying +toys +trace +traceable +traced +tracer +traceries +tracers +tracery +traces +trachea +tracheae +tracheal +tracheas +tracheotomies +tracheotomy +tracing +tracings +track +trackball +trackballs +tracked +tracker +trackers +tracking +trackless +tracks +tract +tractability +tractable +tractably +traction +tractor +tractors +tracts +trade +traded +trademark +trademarked +trademarking +trademarks +tradeoff +tradeoffs +trader +traders +trades +tradesman +tradesmen +tradespeople +tradeswoman +tradeswomen +trading +tradings +tradition +traditional +traditionalism +traditionalist +traditionalists +traditionally +traditions +traduce +traduced +traducer +traducers +traduces +traducing +traffic +trafficked +trafficker +traffickers +trafficking +traffics +tragedian +tragedians +tragedienne +tragediennes +tragedies +tragedy +tragic +tragically +tragicomedies +tragicomedy +tragicomic +trail +trailblazer +trailblazers +trailblazing +trailed +trailer +trailers +trailing +trails +train +trainable +trained +trainee +trainees +trainer +trainers +training +trainload +trainloads +trainman +trainmen +trains +traipse +traipsed +traipses +traipsing +trait +traitor +traitorous +traitorously +traitors +traits +trajectories +trajectory +tram +trammed +trammel +trammeled +trammeling +trammelled +trammelling +trammels +tramming +tramp +tramped +tramper +trampers +tramping +trample +trampled +trampler +tramplers +tramples +trampling +trampoline +trampolined +trampolines +trampolining +tramps +trams +trance +trances +tranquil +tranquiler +tranquilest +tranquility +tranquilize +tranquilized +tranquilizer +tranquilizers +tranquilizes +tranquilizing +tranquiller +tranquillest +tranquillity +tranquillize +tranquillized +tranquillizer +tranquillizers +tranquillizes +tranquillizing +tranquilly +transact +transacted +transacting +transaction +transactions +transactor +transactors +transacts +transatlantic +transceiver +transceivers +transcend +transcended +transcendence +transcendent +transcendental +transcendentalism +transcendentalist +transcendentalists +transcendentally +transcending +transcends +transcontinental +transcribe +transcribed +transcriber +transcribers +transcribes +transcribing +transcript +transcription +transcriptions +transcripts +transducer +transducers +transect +transected +transecting +transects +transept +transepts +transfer +transferable +transferal +transferals +transference +transferred +transferring +transfers +transfiguration +transfigure +transfigured +transfigures +transfiguring +transfix +transfixed +transfixes +transfixing +transfixt +transform +transformable +transformation +transformations +transformed +transformer +transformers +transforming +transforms +transfuse +transfused +transfuses +transfusing +transfusion +transfusions +transgender +transgendered +transgenders +transgress +transgressed +transgresses +transgressing +transgression +transgressions +transgressor +transgressors +transience +transiency +transient +transiently +transients +transistor +transistorize +transistorized +transistorizes +transistorizing +transistors +transit +transited +transiting +transition +transitional +transitionally +transitioned +transitioning +transitions +transitive +transitively +transitiveness +transitives +transitivity +transitory +transits +translatable +translate +translated +translates +translating +translation +translations +translator +translators +transliterate +transliterated +transliterates +transliterating +transliteration +transliterations +translucence +translucency +translucent +translucently +transmigrate +transmigrated +transmigrates +transmigrating +transmigration +transmissible +transmission +transmissions +transmit +transmits +transmittable +transmittal +transmittance +transmitted +transmitter +transmitters +transmitting +transmogrification +transmogrified +transmogrifies +transmogrify +transmogrifying +transmutable +transmutation +transmutations +transmute +transmuted +transmutes +transmuting +transnational +transnationals +transoceanic +transom +transoms +transpacific +transparencies +transparency +transparent +transparently +transpiration +transpire +transpired +transpires +transpiring +transplant +transplantation +transplanted +transplanting +transplants +transpolar +transponder +transponders +transport +transportable +transportation +transported +transporter +transporters +transporting +transports +transpose +transposed +transposes +transposing +transposition +transpositions +transsexual +transsexualism +transsexuals +transship +transshipment +transshipped +transshipping +transships +transubstantiation +transverse +transversely +transverses +transvestism +transvestite +transvestites +trap +trapdoor +trapdoors +trapeze +trapezes +trapezia +trapezium +trapeziums +trapezoid +trapezoidal +trapezoids +trapped +trapper +trappers +trapping +trappings +traps +trapshooting +trash +trashed +trashes +trashier +trashiest +trashiness +trashing +trashy +trauma +traumas +traumata +traumatic +traumatically +traumatize +traumatized +traumatizes +traumatizing +travail +travailed +travailing +travails +travel +traveled +traveler +travelers +traveling +travelled +traveller +travellers +travelling +travelog +travelogs +travelogue +travelogues +travels +traversal +traversals +traverse +traversed +traverses +traversing +travestied +travesties +travesty +travestying +trawl +trawled +trawler +trawlers +trawling +trawls +tray +trays +treacheries +treacherous +treacherously +treacherousness +treachery +treacle +treacly +tread +treading +treadle +treadled +treadles +treadling +treadmill +treadmills +treads +treason +treasonable +treasonous +treasure +treasured +treasurer +treasurers +treasures +treasuries +treasuring +treasury +treat +treatable +treated +treaties +treating +treatise +treatises +treatment +treatments +treats +treaty +treble +trebled +trebles +trebling +tree +treed +treeing +treeless +treelike +trees +treetop +treetops +trefoil +trefoils +trek +trekked +trekker +trekkers +trekking +treks +trellis +trellised +trellises +trellising +trematode +trematodes +tremble +trembled +trembles +trembling +tremendous +tremendously +tremolo +tremolos +tremor +tremors +tremulous +tremulously +tremulousness +trench +trenchancy +trenchant +trenchantly +trenched +trencher +trencherman +trenchermen +trenchers +trenches +trenching +trend +trended +trendier +trendies +trendiest +trendily +trendiness +trending +trends +trendy +trepidation +trespass +trespassed +trespasser +trespassers +trespasses +trespassing +tress +tresses +trestle +trestles +trey +treys +triad +triads +triage +trial +trialled +trialling +trials +triangle +triangles +triangular +triangularly +triangulate +triangulated +triangulates +triangulating +triangulation +triathlon +triathlons +tribal +tribalism +tribe +tribes +tribesman +tribesmen +tribeswoman +tribeswomen +tribulation +tribulations +tribunal +tribunals +tribune +tribunes +tributaries +tributary +tribute +tributes +trice +tricentennial +tricentennials +triceps +tricepses +triceratops +triceratopses +trichina +trichinae +trichinas +trichinosis +trick +tricked +trickery +trickier +trickiest +trickily +trickiness +tricking +trickle +trickled +trickles +trickling +tricks +trickster +tricksters +tricky +tricolor +tricolors +tricolour +tricolours +tricycle +tricycles +trident +tridents +tried +triennial +triennially +triennials +trier +triers +tries +trifle +trifled +trifler +triflers +trifles +trifling +trifocals +trig +trigger +triggered +triggering +triggers +triglyceride +triglycerides +trigonometric +trigonometrical +trigonometry +trike +trikes +trilateral +trilbies +trilby +trill +trilled +trilling +trillion +trillions +trillionth +trillionths +trillium +trills +trilobite +trilobites +trilogies +trilogy +trim +trimaran +trimarans +trimester +trimesters +trimly +trimmed +trimmer +trimmers +trimmest +trimming +trimmings +trimness +trimonthly +trims +trinities +trinitrotoluene +trinity +trinket +trinkets +trio +trios +trip +tripartite +tripe +triple +tripled +triples +triplet +triplets +triplex +triplexes +triplicate +triplicated +triplicates +triplicating +tripling +triply +tripod +tripodal +tripods +tripped +tripper +trippers +tripping +trips +triptych +triptychs +trireme +triremes +trisect +trisected +trisecting +trisection +trisects +trite +tritely +triteness +triter +tritest +tritium +triumph +triumphal +triumphant +triumphantly +triumphed +triumphing +triumphs +triumvir +triumvirate +triumvirates +triumvirs +trivalent +trivet +trivets +trivia +trivial +trivialities +triviality +trivialization +trivialize +trivialized +trivializes +trivializing +trivially +trivium +trochaic +trochee +trochees +trod +trodden +troglodyte +troglodytes +troika +troikas +troll +trolled +trolley +trolleybus +trolleybuses +trolleybusses +trolleys +trollies +trolling +trollop +trollops +trolls +trolly +trombone +trombones +trombonist +trombonists +tromp +tromped +tromping +tromps +troop +trooped +trooper +troopers +trooping +troops +troopship +troopships +trope +tropes +trophies +trophy +tropic +tropical +tropically +tropics +tropism +tropisms +troposphere +tropospheres +trot +troth +trots +trotted +trotter +trotters +trotting +troubadour +troubadours +trouble +troubled +troublemaker +troublemakers +troubles +troubleshoot +troubleshooted +troubleshooter +troubleshooters +troubleshooting +troubleshoots +troubleshot +troublesome +troublesomely +troubling +trough +troughs +trounce +trounced +trouncer +trouncers +trounces +trouncing +troupe +trouped +trouper +troupers +troupes +trouping +trouser +trousers +trousseau +trousseaus +trousseaux +trout +trouts +trove +troves +trow +trowed +trowel +troweled +troweling +trowelled +trowelling +trowels +trowing +trows +troy +truancy +truant +truanted +truanting +truants +truce +truces +truck +trucked +trucker +truckers +trucking +truckle +truckled +truckles +truckling +truckload +truckloads +trucks +truculence +truculent +truculently +trudge +trudged +trudges +trudging +true +trued +trueing +truelove +trueloves +truer +trues +truest +truffle +truffles +truing +truism +truisms +truly +trump +trumped +trumpery +trumpet +trumpeted +trumpeter +trumpeters +trumpeting +trumpets +trumping +trumps +truncate +truncated +truncates +truncating +truncation +truncheon +truncheons +trundle +trundled +trundler +trundlers +trundles +trundling +trunk +trunks +truss +trussed +trusses +trussing +trust +trusted +trustee +trustees +trusteeship +trusteeships +trustful +trustfully +trustfulness +trustier +trusties +trustiest +trusting +trustingly +trusts +trustworthier +trustworthiest +trustworthiness +trustworthy +trusty +truth +truthful +truthfully +truthfulness +truths +trying +tryingly +tryout +tryouts +tryst +trysted +trysting +trysts +tsar +tsarina +tsarinas +tsars +tsetse +tsetses +tsunami +tsunamis +tuba +tubal +tubas +tubbier +tubbiest +tubby +tube +tubed +tubeless +tuber +tubercle +tubercles +tubercular +tuberculin +tuberculosis +tuberculous +tuberose +tuberous +tubers +tubes +tubful +tubfuls +tubing +tubs +tubular +tubule +tubules +tuck +tucked +tucker +tuckered +tuckering +tuckers +tucking +tucks +tuft +tufted +tufter +tufters +tufting +tufts +tugboat +tugboats +tugged +tugging +tugs +tuition +tularemia +tulip +tulips +tulle +tumble +tumbled +tumbledown +tumbler +tumblers +tumbles +tumbleweed +tumbleweeds +tumbling +tumbrel +tumbrels +tumbril +tumbrils +tumescence +tumescent +tumid +tumidity +tummies +tummy +tumor +tumorous +tumors +tumour +tumours +tumult +tumults +tumultuous +tumultuously +tuna +tunas +tundra +tundras +tune +tuned +tuneful +tunefully +tunefulness +tuneless +tunelessly +tuner +tuners +tunes +tuneup +tuneups +tungsten +tunic +tunics +tuning +tunnel +tunneled +tunneler +tunnelers +tunneling +tunnelled +tunneller +tunnellers +tunnelling +tunnels +tunnies +tunny +tuns +tuque +tuques +turban +turbans +turbid +turbidity +turbine +turbines +turbo +turbocharge +turbocharged +turbocharger +turbochargers +turbocharges +turbocharging +turbofan +turbofans +turbojet +turbojets +turboprop +turboprops +turbos +turbot +turbots +turbulence +turbulent +turbulently +turd +turds +tureen +tureens +turf +turfed +turfing +turfs +turfy +turgid +turgidity +turgidly +turkey +turkeys +turmeric +turmerics +turmoil +turmoils +turn +turnabout +turnabouts +turnaround +turnarounds +turnbuckle +turnbuckles +turncoat +turncoats +turned +turner +turners +turning +turnings +turnip +turnips +turnkey +turnkeys +turnoff +turnoffs +turnout +turnouts +turnover +turnovers +turnpike +turnpikes +turns +turnstile +turnstiles +turntable +turntables +turpentine +turpitude +turquoise +turquoises +turret +turreted +turrets +turtle +turtledove +turtledoves +turtleneck +turtlenecked +turtlenecks +turtles +turves +tush +tushes +tusk +tusked +tusks +tussle +tussled +tussles +tussling +tussock +tussocks +tussocky +tutelage +tutelary +tutor +tutored +tutorial +tutorials +tutoring +tutors +tutorship +tuts +tutted +tutti +tutting +tuttis +tutu +tutus +tuxedo +tuxedoes +tuxedos +tuxes +twaddle +twaddled +twaddler +twaddlers +twaddles +twaddling +twain +twang +twanged +twangier +twangiest +twanging +twangs +twangy +twas +tweak +tweaked +tweaking +tweaks +tweed +tweedier +tweediest +tweeds +tweedy +tween +tweet +tweeted +tweeter +tweeters +tweeting +tweets +tweezers +twelfth +twelfths +twelve +twelvemonth +twelvemonths +twelves +twenties +twentieth +twentieths +twenty +twerp +twerps +twice +twiddle +twiddled +twiddles +twiddlier +twiddliest +twiddling +twiddly +twig +twigged +twiggier +twiggiest +twigging +twiggy +twigs +twilight +twilit +twill +twilled +twin +twine +twined +twiner +twiners +twines +twinge +twinged +twingeing +twinges +twinging +twinight +twining +twinkle +twinkled +twinkles +twinkling +twinklings +twinkly +twinned +twinning +twins +twirl +twirled +twirler +twirlers +twirlier +twirliest +twirling +twirls +twirly +twist +twisted +twister +twisters +twistier +twistiest +twisting +twists +twisty +twit +twitch +twitched +twitches +twitchier +twitchiest +twitching +twitchy +twits +twitted +twitter +twittered +twittering +twitters +twittery +twitting +twixt +twofer +twofers +twofold +twopence +twopences +twopenny +twos +twosome +twosomes +tycoon +tycoons +tying +tyke +tykes +tympana +tympani +tympanist +tympanists +tympanum +tympanums +type +typecast +typecasting +typecasts +typed +typeface +typefaces +types +typescript +typescripts +typeset +typesets +typesetter +typesetters +typesetting +typewrite +typewriter +typewriters +typewrites +typewriting +typewritten +typewrote +typhoid +typhoon +typhoons +typhus +typical +typicality +typically +typification +typified +typifies +typify +typifying +typing +typist +typists +typo +typographer +typographers +typographic +typographical +typographically +typography +typology +typos +tyrannic +tyrannical +tyrannically +tyrannies +tyrannize +tyrannized +tyrannizes +tyrannizing +tyrannosaur +tyrannosaurs +tyrannosaurus +tyrannosauruses +tyrannous +tyranny +tyrant +tyrants +tyre +tyres +tyro +tyros +tzar +tzarina +tzarinas +tzars +ubiquitous +ubiquitously +ubiquity +udder +udders +ufologist +ufologists +ufology +uglier +ugliest +ugliness +ugly +ukase +ukases +ukelele +ukeleles +ukulele +ukuleles +ulcer +ulcerate +ulcerated +ulcerates +ulcerating +ulceration +ulcerations +ulcerous +ulcers +ulna +ulnae +ulnar +ulnas +ulster +ulsters +ulterior +ultimata +ultimate +ultimately +ultimatum +ultimatums +ultimo +ultra +ultraconservative +ultraconservatives +ultrahigh +ultralight +ultralights +ultramarine +ultramodern +ultras +ultrasonic +ultrasonically +ultrasound +ultrasounds +ultraviolet +ululate +ululated +ululates +ululating +ululation +ululations +umbel +umbels +umber +umbilical +umbilici +umbilicus +umbilicuses +umbra +umbrae +umbrage +umbras +umbrella +umbrellas +umiak +umiaks +umlaut +umlauts +umped +umping +umpire +umpired +umpires +umpiring +umps +umpteen +umpteenth +unabashed +unabashedly +unabated +unable +unabridged +unabridgeds +unaccented +unacceptable +unacceptably +unaccommodating +unaccompanied +unaccomplished +unaccountable +unaccountably +unaccounted +unaccredited +unaccustomed +unacknowledged +unacquainted +unadorned +unadulterated +unadventurous +unadvertised +unadvised +unadvisedly +unaesthetic +unaffected +unaffectedly +unaffiliated +unafraid +unaided +unalienable +unaligned +unalike +unalloyed +unalterable +unalterably +unaltered +unambiguous +unambiguously +unambitious +unanimity +unanimous +unanimously +unannounced +unanswerable +unanswered +unanticipated +unapologetic +unapparent +unappealing +unappealingly +unappetizing +unappreciated +unappreciative +unapproachable +unappropriated +unapproved +unarguable +unarguably +unarmed +unarmored +unashamed +unashamedly +unasked +unassailable +unassertive +unassisted +unassuming +unassumingly +unattached +unattainable +unattended +unattested +unattractive +unattractively +unauthentic +unauthorized +unavailability +unavailable +unavailing +unavailingly +unavoidable +unavoidably +unaware +unawareness +unawares +unbaked +unbalanced +unbaptized +unbar +unbarred +unbarring +unbars +unbearable +unbearably +unbeatable +unbeaten +unbecoming +unbecomingly +unbeknown +unbeknownst +unbelief +unbelievable +unbelievably +unbeliever +unbelievers +unbelieving +unbend +unbending +unbends +unbent +unbiased +unbiassed +unbid +unbidden +unbind +unbinding +unbinds +unbleached +unblemished +unblinking +unblock +unblocked +unblocking +unblocks +unblushing +unblushingly +unbolt +unbolted +unbolting +unbolts +unborn +unbosom +unbosomed +unbosoming +unbosoms +unbound +unbounded +unbowed +unbreakable +unbridgeable +unbridled +unbroken +unbuckle +unbuckled +unbuckles +unbuckling +unburden +unburdened +unburdening +unburdens +unbutton +unbuttoned +unbuttoning +unbuttons +uncannier +uncanniest +uncannily +uncanny +uncap +uncapped +uncapping +uncaps +uncaring +uncaught +unceasing +unceasingly +uncensored +unceremonious +unceremoniously +uncertain +uncertainly +uncertainties +uncertainty +unchain +unchained +unchaining +unchains +unchallenged +unchangeable +unchanged +unchanging +unchaperoned +uncharacteristic +uncharacteristically +uncharged +uncharitable +uncharitably +uncharted +unchaste +unchaster +unchastest +unchecked +unchristian +uncial +uncircumcised +uncivil +uncivilized +uncivilly +unclad +unclaimed +unclasp +unclasped +unclasping +unclasps +unclassified +uncle +unclean +uncleaned +uncleaner +uncleanest +uncleanlier +uncleanliest +uncleanliness +uncleanly +uncleanness +unclear +uncleared +unclearer +unclearest +uncles +uncloak +uncloaked +uncloaking +uncloaks +unclog +unclogged +unclogging +unclogs +unclothe +unclothed +unclothes +unclothing +unclouded +uncluttered +uncoil +uncoiled +uncoiling +uncoils +uncollected +uncolored +uncombed +uncombined +uncomfortable +uncomfortably +uncommitted +uncommon +uncommoner +uncommonest +uncommonly +uncommonness +uncommunicative +uncompensated +uncomplaining +uncomplainingly +uncompleted +uncomplicated +uncomplimentary +uncompounded +uncomprehending +uncomprehendingly +uncompromising +uncompromisingly +unconcealed +unconcern +unconcerned +unconcernedly +unconditional +unconditionally +unconditioned +unconfined +unconfirmed +unconformable +uncongenial +unconnected +unconquerable +unconquered +unconscionable +unconscionably +unconscious +unconsciously +unconsciousness +unconsecrated +unconsidered +unconsolidated +unconstitutional +unconstitutionality +unconstitutionally +unconstrained +unconsumed +unconsummated +uncontaminated +uncontested +uncontrollable +uncontrollably +uncontrolled +unconventional +unconventionality +unconventionally +unconverted +unconvinced +unconvincing +unconvincingly +uncooked +uncool +uncooperative +uncoordinated +uncork +uncorked +uncorking +uncorks +uncorrected +uncorroborated +uncountable +uncounted +uncouple +uncoupled +uncouples +uncoupling +uncouth +uncouthly +uncover +uncovered +uncovering +uncovers +uncritical +uncritically +uncross +uncrossed +uncrosses +uncrossing +uncrowded +uncrowned +unction +unctions +unctuous +unctuously +unctuousness +uncultivated +uncultured +uncured +uncurl +uncurled +uncurling +uncurls +uncustomary +uncut +undamaged +undated +undaunted +undauntedly +undeceive +undeceived +undeceives +undeceiving +undecided +undecideds +undecipherable +undeclared +undefeated +undefended +undefinable +undefined +undemanding +undemocratic +undemonstrative +undemonstratively +undeniable +undeniably +undependable +under +underachieve +underachieved +underachiever +underachievers +underachieves +underachieving +underact +underacted +underacting +underacts +underage +underarm +underarms +underbellies +underbelly +underbid +underbidding +underbids +underbrush +undercarriage +undercarriages +undercharge +undercharged +undercharges +undercharging +underclass +underclassman +underclassmen +underclothes +underclothing +undercoat +undercoated +undercoating +undercoatings +undercoats +undercover +undercurrent +undercurrents +undercut +undercuts +undercutting +underdeveloped +underdevelopment +underdog +underdogs +underdone +underemployed +underemployment +underestimate +underestimated +underestimates +underestimating +underestimation +underestimations +underexpose +underexposed +underexposes +underexposing +underexposure +underexposures +underfed +underfeed +underfeeding +underfeeds +underfoot +underfur +undergarment +undergarments +undergo +undergoes +undergoing +undergone +undergraduate +undergraduates +underground +undergrounds +undergrowth +underhand +underhanded +underhandedly +underhandedness +underlain +underlay +underlays +underlie +underlies +underline +underlined +underlines +underling +underlings +underlining +underlip +underlips +underlying +undermanned +undermentioned +undermine +undermined +undermines +undermining +undermost +underneath +underneaths +undernourished +undernourishment +underpaid +underpants +underpart +underparts +underpass +underpasses +underpay +underpaying +underpayment +underpayments +underpays +underpin +underpinned +underpinning +underpinnings +underpins +underplay +underplayed +underplaying +underplays +underpopulated +underprivileged +underproduction +underrate +underrated +underrates +underrating +underrepresented +underscore +underscored +underscores +underscoring +undersea +underseas +undersecretaries +undersecretary +undersell +underselling +undersells +undersexed +undershirt +undershirts +undershoot +undershooting +undershoots +undershorts +undershot +underside +undersides +undersign +undersigned +undersigning +undersigns +undersize +undersized +underskirt +underskirts +undersold +understaffed +understand +understandable +understandably +understanding +understandingly +understandings +understands +understate +understated +understatement +understatements +understates +understating +understood +understudied +understudies +understudy +understudying +undertake +undertaken +undertaker +undertakers +undertakes +undertaking +undertakings +underthings +undertone +undertones +undertook +undertow +undertows +undervaluation +undervalue +undervalued +undervalues +undervaluing +underwater +underway +underwear +underweight +underwent +underwhelm +underwhelmed +underwhelming +underwhelms +underworld +underworlds +underwrite +underwriter +underwriters +underwrites +underwriting +underwritten +underwrote +undeserved +undeservedly +undeserving +undesirability +undesirable +undesirables +undesirably +undesired +undetectable +undetected +undetermined +undeterred +undeveloped +undeviating +undid +undies +undifferentiated +undigested +undignified +undiluted +undiminished +undimmed +undiplomatic +undischarged +undisciplined +undisclosed +undiscovered +undiscriminating +undisguised +undismayed +undisputed +undissolved +undistinguished +undistributed +undisturbed +undivided +undo +undocumented +undoes +undoing +undoings +undomesticated +undone +undoubted +undoubtedly +undramatic +undreamed +undreamt +undress +undressed +undresses +undressing +undrinkable +undue +undulant +undulate +undulated +undulates +undulating +undulation +undulations +unduly +undying +unearned +unearth +unearthed +unearthing +unearthlier +unearthliest +unearthliness +unearthly +unearths +unease +uneasier +uneasiest +uneasily +uneasiness +uneasy +uneatable +uneaten +uneconomic +uneconomical +uneconomically +unedifying +unedited +uneducated +unembarrassed +unemotional +unemphatic +unemployable +unemployed +unemployment +unenclosed +unencumbered +unending +unendurable +unenforced +unenlightened +unenterprising +unenthusiastic +unenviable +unequal +unequaled +unequalled +unequally +unequipped +unequivocal +unequivocally +unerring +unerringly +unessential +unethical +unethically +uneven +unevenly +unevenness +uneventful +uneventfully +unexampled +unexceptionable +unexceptionably +unexceptional +unexceptionally +unexcited +unexciting +unexcused +unexpected +unexpectedly +unexpectedness +unexpired +unexplained +unexploited +unexplored +unexposed +unexpressed +unexpurgated +unfading +unfailing +unfailingly +unfair +unfairer +unfairest +unfairly +unfairness +unfaithful +unfaithfully +unfaithfulness +unfaltering +unfamiliar +unfamiliarity +unfashionable +unfashionably +unfasten +unfastened +unfastening +unfastens +unfathomable +unfathomably +unfavorable +unfavorably +unfeasible +unfed +unfeeling +unfeelingly +unfeigned +unfeminine +unfertilized +unfetter +unfettered +unfettering +unfetters +unfilled +unfiltered +unfinished +unfit +unfitness +unfits +unfitted +unfitter +unfittest +unfitting +unfix +unfixed +unfixes +unfixing +unflagging +unflaggingly +unflappability +unflappable +unflappably +unflattering +unflavored +unfledged +unflinching +unflinchingly +unfocused +unfold +unfolded +unfolding +unfolds +unforced +unforeseeable +unforeseen +unforgettable +unforgettably +unforgivable +unforgivably +unforgiving +unforgotten +unformed +unformulated +unfortified +unfortunate +unfortunately +unfortunates +unfounded +unframed +unfreeze +unfreezes +unfreezing +unfrequented +unfriendlier +unfriendliest +unfriendliness +unfriendly +unfrock +unfrocked +unfrocking +unfrocks +unfroze +unfrozen +unfruitful +unfulfilled +unfunded +unfunny +unfurl +unfurled +unfurling +unfurls +unfurnished +ungainlier +ungainliest +ungainliness +ungainly +ungenerous +ungentle +ungentlemanly +unglued +ungodlier +ungodliest +ungodliness +ungodly +ungovernable +ungoverned +ungraceful +ungracefully +ungracious +ungraciously +ungraded +ungrammatical +ungrammatically +ungrateful +ungratefully +ungratefulness +ungrudging +unguarded +unguent +unguents +unguided +ungulate +ungulates +unhallowed +unhampered +unhand +unhanded +unhandier +unhandiest +unhanding +unhands +unhandy +unhappier +unhappiest +unhappily +unhappiness +unhappy +unhardened +unharmed +unharness +unharnessed +unharnesses +unharnessing +unharvested +unhatched +unhealed +unhealthful +unhealthier +unhealthiest +unhealthily +unhealthiness +unhealthy +unheard +unheated +unheeded +unhelpful +unhelpfully +unheralded +unhesitating +unhesitatingly +unhindered +unhinge +unhinged +unhinges +unhinging +unhistorical +unhitch +unhitched +unhitches +unhitching +unholier +unholiest +unholiness +unholy +unhook +unhooked +unhooking +unhooks +unhorse +unhorsed +unhorses +unhorsing +unhurried +unhurriedly +unhurt +unicameral +unicellular +unicorn +unicorns +unicycle +unicycles +unidentifiable +unidentified +unidiomatic +unification +unified +unifies +uniform +uniformed +uniforming +uniformity +uniformly +uniforms +unify +unifying +unilateral +unilaterally +unimaginable +unimaginative +unimaginatively +unimpaired +unimpeachable +unimpeded +unimportant +unimposing +unimpressed +unimpressive +unimproved +unincorporated +uninfected +uninfluenced +uninformative +uninformed +uninhabitable +uninhabited +uninhibited +uninhibitedly +uninitiated +uninjured +uninspired +uninspiring +uninstructed +uninsured +unintelligent +unintelligible +unintelligibly +unintended +unintentional +unintentionally +uninterested +uninteresting +uninterrupted +uninvited +uninviting +union +unionism +unionist +unionists +unionization +unionize +unionized +unionizes +unionizing +unions +unique +uniquely +uniqueness +uniquer +uniquest +unisex +unison +unit +unitary +unite +united +unitedly +unites +unities +uniting +unitize +unitized +unitizes +unitizing +units +unity +univalent +univalve +univalves +universal +universality +universalize +universalized +universalizes +universalizing +universally +universals +universe +universes +universities +university +unjust +unjustifiable +unjustifiably +unjustified +unjustly +unkempt +unkind +unkinder +unkindest +unkindlier +unkindliest +unkindly +unkindness +unknowable +unknowing +unknowingly +unknown +unknowns +unlabeled +unlace +unlaced +unlaces +unlacing +unladen +unladylike +unlatch +unlatched +unlatches +unlatching +unlawful +unlawfully +unlawfulness +unleaded +unlearn +unlearned +unlearning +unlearns +unleash +unleashed +unleashes +unleashing +unleavened +unless +unlettered +unlicensed +unlighted +unlikable +unlike +unlikelier +unlikeliest +unlikelihood +unlikeliness +unlikely +unlikeness +unlimber +unlimbered +unlimbering +unlimbers +unlimited +unlined +unlisted +unlit +unlivable +unload +unloaded +unloading +unloads +unlock +unlocked +unlocking +unlocks +unloose +unloosed +unloosen +unloosened +unloosening +unloosens +unlooses +unloosing +unlovable +unloved +unlovelier +unloveliest +unlovely +unloving +unluckier +unluckiest +unluckily +unluckiness +unlucky +unmade +unmake +unmakes +unmaking +unman +unmanageable +unmanlier +unmanliest +unmanly +unmanned +unmannerly +unmanning +unmans +unmarked +unmarketable +unmarred +unmarried +unmask +unmasked +unmasking +unmasks +unmatched +unmeaning +unmeant +unmeasured +unmediated +unmentionable +unmentionables +unmentioned +unmerciful +unmercifully +unmerited +unmindful +unmistakable +unmistakably +unmitigated +unmixed +unmodified +unmolested +unmoral +unmorality +unmotivated +unmounted +unmovable +unmoved +unmusical +unnameable +unnamed +unnatural +unnaturally +unnaturalness +unnecessarily +unnecessary +unneeded +unnerve +unnerved +unnerves +unnerving +unnoticeable +unnoticed +unnumbered +unobjectionable +unobservant +unobserved +unobstructed +unobtainable +unobtrusive +unobtrusively +unobtrusiveness +unoccupied +unoffensive +unofficial +unofficially +unopened +unopposed +unorganized +unoriginal +unorthodox +unpack +unpacked +unpacking +unpacks +unpaid +unpainted +unpaired +unpalatable +unparalleled +unparallelled +unpardonable +unpardonably +unpasteurized +unpatriotic +unpaved +unpeeled +unperceived +unperceptive +unperformed +unperson +unpersons +unpersuaded +unpersuasive +unperturbed +unpin +unpinned +unpinning +unpins +unplanned +unpleasant +unpleasantly +unpleasantness +unpleasing +unplug +unplugged +unplugging +unplugs +unplumbed +unpolished +unpolitical +unpolluted +unpopular +unpopularity +unpractical +unpracticed +unprecedented +unprecedentedly +unpredictability +unpredictable +unpredictably +unprejudiced +unpremeditated +unprepared +unpreparedness +unprepossessing +unpressed +unpretentious +unpretentiously +unpreventable +unprincipled +unprintable +unprocessed +unproductive +unproductively +unprofessional +unprofessionally +unprofitable +unprofitably +unpromising +unprompted +unpronounceable +unpropitious +unprotected +unproved +unproven +unprovided +unprovoked +unpublished +unpunished +unqualified +unquenchable +unquestionable +unquestionably +unquestioned +unquestioning +unquestioningly +unquiet +unquieter +unquietest +unquote +unquoted +unquotes +unquoting +unrated +unravel +unraveled +unraveling +unravelled +unravelling +unravels +unread +unreadable +unready +unreal +unrealistic +unrealistically +unreality +unrealized +unreasonable +unreasonableness +unreasonably +unreasoning +unrecognizable +unrecognized +unreconstructed +unrecorded +unrecoverable +unreel +unreeled +unreeling +unreels +unrefined +unreformed +unregenerate +unregistered +unregulated +unrehearsed +unrelated +unrelenting +unrelentingly +unreliability +unreliable +unreliably +unrelieved +unremarkable +unremembered +unremitting +unremittingly +unrepentant +unreported +unrepresentative +unrepresented +unrequited +unreserved +unreservedly +unresistant +unresolved +unresponsive +unresponsively +unresponsiveness +unrest +unrestrained +unrestricted +unrewarded +unrewarding +unrighteous +unrighteousness +unripe +unripened +unriper +unripest +unrivaled +unrivalled +unroll +unrolled +unrolling +unrolls +unromantic +unruffled +unrulier +unruliest +unruliness +unruly +unsaddle +unsaddled +unsaddles +unsaddling +unsafe +unsafely +unsafer +unsafest +unsaid +unsalable +unsalted +unsanctioned +unsanitary +unsatisfactorily +unsatisfactory +unsatisfied +unsatisfying +unsaturated +unsaved +unsavory +unsavoury +unsay +unsaying +unsays +unscathed +unscented +unscheduled +unschooled +unscientific +unscientifically +unscramble +unscrambled +unscrambles +unscrambling +unscratched +unscrew +unscrewed +unscrewing +unscrews +unscripted +unscrupulous +unscrupulously +unscrupulousness +unseal +unsealed +unsealing +unseals +unsearchable +unseasonable +unseasonably +unseasoned +unseat +unseated +unseating +unseats +unseeing +unseeingly +unseemlier +unseemliest +unseemliness +unseemly +unseen +unsegmented +unsegregated +unselfish +unselfishly +unselfishness +unsentimental +unsettle +unsettled +unsettles +unsettling +unshackle +unshackled +unshackles +unshackling +unshakable +unshakably +unshakeable +unshaken +unshaped +unshapely +unshaven +unsheathe +unsheathed +unsheathes +unsheathing +unshod +unshorn +unsifted +unsightlier +unsightliest +unsightliness +unsightly +unsigned +unsinkable +unskilled +unskillful +unskillfully +unsmiling +unsnap +unsnapped +unsnapping +unsnaps +unsnarl +unsnarled +unsnarling +unsnarls +unsociable +unsocial +unsoiled +unsold +unsolicited +unsolvable +unsolved +unsophisticated +unsorted +unsought +unsound +unsounder +unsoundest +unsoundly +unsoundness +unsparing +unsparingly +unspeakable +unspeakably +unspecific +unspecified +unspectacular +unspent +unspoiled +unspoken +unsportsmanlike +unstable +unstabler +unstablest +unstably +unstained +unstated +unsteadier +unsteadiest +unsteadily +unsteadiness +unsteady +unstinting +unstintingly +unstop +unstoppable +unstopped +unstopping +unstops +unstrap +unstrapped +unstrapping +unstraps +unstressed +unstructured +unstrung +unstuck +unstudied +unsubstantial +unsubstantiated +unsuccessful +unsuccessfully +unsuitability +unsuitable +unsuitably +unsuited +unsullied +unsung +unsupervised +unsupported +unsure +unsurpassed +unsurprising +unsurprisingly +unsuspected +unsuspecting +unsuspectingly +unsustainable +unswayed +unsweetened +unswerving +unsymmetrical +unsympathetic +unsympathetically +unsystematic +untactful +untainted +untalented +untamed +untangle +untangled +untangles +untangling +untanned +untapped +untarnished +untasted +untaught +unteachable +untenable +untenanted +untended +untested +unthinkable +unthinkably +unthinking +unthinkingly +untidier +untidiest +untidily +untidiness +untidy +untie +untied +unties +until +untimelier +untimeliest +untimeliness +untimely +untiring +untiringly +untitled +unto +untold +untouchable +untouchables +untouched +untoward +untraceable +untrained +untrammeled +untrammelled +untranslatable +untranslated +untraveled +untreated +untried +untrimmed +untrod +untroubled +untrue +untruer +untruest +untruly +untrustworthy +untruth +untruthful +untruthfully +untruthfulness +untruths +untutored +untwist +untwisted +untwisting +untwists +untying +untypical +unusable +unused +unusual +unusually +unutterable +unutterably +unvaried +unvarnished +unvarying +unveil +unveiled +unveiling +unveils +unverifiable +unverified +unversed +unvoiced +unwanted +unwarier +unwariest +unwarily +unwariness +unwarrantable +unwarranted +unwary +unwashed +unwavering +unwearable +unwearied +unwed +unwelcome +unwell +unwholesome +unwholesomeness +unwieldier +unwieldiest +unwieldiness +unwieldy +unwilling +unwillingly +unwillingness +unwind +unwinding +unwinds +unwise +unwisely +unwiser +unwisest +unwitting +unwittingly +unwonted +unworkable +unworldlier +unworldliest +unworldliness +unworldly +unworn +unworried +unworthier +unworthiest +unworthily +unworthiness +unworthy +unwound +unwoven +unwrap +unwrapped +unwrapping +unwraps +unwrinkled +unwritten +unyielding +unyoke +unyoked +unyokes +unyoking +unzip +unzipped +unzipping +unzips +upbeat +upbeats +upbraid +upbraided +upbraiding +upbraids +upbringing +upbringings +upchuck +upchucked +upchucking +upchucks +upcoming +upcountry +update +updated +updates +updating +updraft +updrafts +upend +upended +upending +upends +upfront +upgrade +upgraded +upgrades +upgrading +upheaval +upheavals +upheld +uphill +uphills +uphold +upholder +upholders +upholding +upholds +upholster +upholstered +upholsterer +upholsterers +upholstering +upholsters +upholstery +upkeep +upland +uplands +uplift +uplifted +uplifting +uplifts +upload +uploaded +uploading +uploads +upmarket +upmost +upon +upped +upper +uppercase +upperclassman +upperclassmen +uppercut +uppercuts +uppercutting +uppermost +uppers +upping +uppish +uppity +upraise +upraised +upraises +upraising +uprear +upreared +uprearing +uprears +upright +uprightly +uprightness +uprights +uprising +uprisings +upriver +uproar +uproarious +uproariously +uproars +uproot +uprooted +uprooting +uproots +upscale +upset +upsets +upsetting +upshot +upshots +upside +upsides +upsilon +upsilons +upstage +upstaged +upstages +upstaging +upstairs +upstanding +upstart +upstarted +upstarting +upstarts +upstate +upstream +upstroke +upstrokes +upsurge +upsurged +upsurges +upsurging +upswing +upswings +uptake +uptakes +upthrust +upthrusting +upthrusts +uptick +upticks +uptight +uptown +upturn +upturned +upturning +upturns +upward +upwardly +upwards +upwind +uracil +uranium +urban +urbane +urbanely +urbaner +urbanest +urbanity +urbanization +urbanize +urbanized +urbanizes +urbanizing +urbanologist +urbanologists +urbanology +urchin +urchins +urea +uremia +uremic +ureter +ureters +urethane +urethra +urethrae +urethral +urethras +urge +urged +urgency +urgent +urgently +urges +urging +uric +urinal +urinals +urinalyses +urinalysis +urinary +urinate +urinated +urinates +urinating +urination +urine +urns +urogenital +urological +urologist +urologists +urology +ursine +urticaria +usability +usable +usage +usages +useability +useable +used +useful +usefully +usefulness +useless +uselessly +uselessness +user +users +uses +usher +ushered +usherette +usherettes +ushering +ushers +using +usual +usually +usurer +usurers +usurious +usurp +usurpation +usurped +usurper +usurpers +usurping +usurps +usury +utensil +utensils +uteri +uterine +uterus +uteruses +utilitarian +utilitarianism +utilitarians +utilities +utility +utilization +utilize +utilized +utilizes +utilizing +utmost +utopia +utopian +utopians +utopias +utter +utterance +utterances +uttered +uttering +utterly +uttermost +utters +uvula +uvulae +uvular +uvulars +uvulas +uxorious +vacancies +vacancy +vacant +vacantly +vacate +vacated +vacates +vacating +vacation +vacationed +vacationer +vacationers +vacationing +vacationist +vacationists +vacations +vaccinate +vaccinated +vaccinates +vaccinating +vaccination +vaccinations +vaccine +vaccines +vacillate +vacillated +vacillates +vacillating +vacillation +vacillations +vacua +vacuity +vacuole +vacuoles +vacuous +vacuously +vacuousness +vacuum +vacuumed +vacuuming +vacuums +vagabond +vagabondage +vagabonds +vagaries +vagarious +vagary +vagina +vaginae +vaginal +vaginally +vaginas +vagrancy +vagrant +vagrants +vague +vaguely +vagueness +vaguer +vaguest +vain +vainer +vainest +vainglorious +vaingloriously +vainglory +vainly +valance +valances +vale +valediction +valedictions +valedictorian +valedictorians +valedictories +valedictory +valence +valences +valencies +valency +valentine +valentines +vales +valet +valeted +valeting +valets +valetudinarian +valetudinarianism +valetudinarians +valiance +valiant +valiantly +valid +validate +validated +validates +validating +validation +validations +validity +validly +validness +valise +valises +valley +valleys +valor +valorous +valorously +valour +valuable +valuables +valuate +valuated +valuates +valuating +valuation +valuations +value +valued +valueless +valuer +valuers +values +valuing +valve +valved +valveless +valves +valving +valvular +vamoose +vamoosed +vamooses +vamoosing +vamp +vamped +vamping +vampire +vampires +vamps +vanadium +vandal +vandalise +vandalised +vandalises +vandalising +vandalism +vandalize +vandalized +vandalizes +vandalizing +vandals +vane +vanes +vanguard +vanguards +vanilla +vanillas +vanish +vanished +vanishes +vanishing +vanities +vanity +vanned +vanning +vanquish +vanquished +vanquisher +vanquishers +vanquishes +vanquishing +vans +vantage +vantages +vapid +vapidity +vapidly +vapidness +vapor +vaporise +vaporised +vaporises +vaporising +vaporization +vaporize +vaporized +vaporizer +vaporizers +vaporizes +vaporizing +vaporous +vapors +vapory +vapour +vapours +vaquero +vaqueros +variability +variable +variables +variably +variance +variances +variant +variants +variation +variations +varicolored +varicose +varied +variegate +variegated +variegates +variegating +variegation +varies +varietal +varietals +varieties +variety +various +variously +varlet +varlets +varmint +varmints +varnish +varnished +varnishes +varnishing +varsities +varsity +vary +varying +vascular +vase +vasectomies +vasectomy +vases +vasomotor +vassal +vassalage +vassals +vast +vaster +vastest +vastly +vastness +vasts +vats +vatted +vatting +vaudeville +vaudevillian +vaudevillians +vault +vaulted +vaulter +vaulters +vaulting +vaults +vaunt +vaunted +vaunting +vaunts +veal +vector +vectored +vectoring +vectors +veejay +veejays +veep +veeps +veer +veered +veering +veers +vegan +vegans +veges +vegetable +vegetables +vegetarian +vegetarianism +vegetarians +vegetate +vegetated +vegetates +vegetating +vegetation +vegetative +vegged +vegges +veggie +veggies +vegging +vegs +vehemence +vehemency +vehement +vehemently +vehicle +vehicles +vehicular +veil +veiled +veiling +veils +vein +veined +veining +veins +vela +velar +velars +veld +velds +veldt +veldts +vellum +velocipede +velocipedes +velocities +velocity +velour +velours +velum +velvet +velveteen +velvety +venal +venality +venally +venation +vend +vended +vender +venders +vendetta +vendettas +vendible +vending +vendor +vendors +vends +veneer +veneered +veneering +veneers +venerability +venerable +venerate +venerated +venerates +venerating +veneration +venereal +vengeance +vengeful +vengefully +venial +venireman +veniremen +venison +venom +venomous +venomously +venous +vent +vented +ventilate +ventilated +ventilates +ventilating +ventilation +ventilator +ventilators +venting +ventral +ventricle +ventricles +ventricular +ventriloquism +ventriloquist +ventriloquists +ventriloquy +vents +venture +ventured +ventures +venturesome +venturesomely +venturesomeness +venturing +venturous +venturously +venturousness +venue +venues +veracious +veraciously +veracity +veranda +verandah +verandahs +verandas +verb +verbal +verbalization +verbalize +verbalized +verbalizes +verbalizing +verbally +verbals +verbatim +verbena +verbenas +verbiage +verbose +verbosely +verbosity +verboten +verbs +verdant +verdantly +verdict +verdicts +verdigris +verdure +verge +verged +verger +vergers +verges +verging +verier +veriest +verifiable +verification +verified +verifies +verify +verifying +verily +verisimilitude +veritable +veritably +verities +verity +vermicelli +vermiculite +vermiform +vermilion +vermillion +vermin +verminous +vermouth +vernacular +vernaculars +vernal +vernier +verniers +veronica +verruca +verrucae +verrucas +versatile +versatility +verse +versed +verses +versification +versified +versifier +versifiers +versifies +versify +versifying +versing +version +versions +verso +versos +versus +vertebra +vertebrae +vertebral +vertebras +vertebrate +vertebrates +vertex +vertexes +vertical +vertically +verticals +vertices +vertiginous +vertigo +verve +very +vesicle +vesicles +vesicular +vesiculate +vesper +vespers +vessel +vessels +vest +vestal +vestals +vested +vestibule +vestibules +vestige +vestiges +vestigial +vestigially +vesting +vestment +vestments +vestries +vestry +vestryman +vestrymen +vests +vetch +vetches +veteran +veterans +veterinarian +veterinarians +veterinaries +veterinary +veto +vetoed +vetoes +vetoing +vets +vetted +vetting +vexation +vexations +vexatious +vexatiously +vexed +vexes +vexing +viability +viable +viably +viaduct +viaducts +vial +vials +viand +viands +vibe +vibes +vibraharp +vibraharps +vibrancy +vibrant +vibrantly +vibraphone +vibraphones +vibraphonist +vibraphonists +vibrate +vibrated +vibrates +vibrating +vibration +vibrations +vibrato +vibrator +vibrators +vibratory +vibratos +viburnum +viburnums +vicar +vicarage +vicarages +vicarious +vicariously +vicariousness +vicars +vice +viced +vicegerent +vicegerents +vicennial +viceregal +viceroy +viceroys +vices +vichyssoise +vicing +vicinity +vicious +viciously +viciousness +vicissitude +vicissitudes +victim +victimization +victimize +victimized +victimizes +victimizing +victims +victor +victories +victorious +victoriously +victors +victory +victual +victualed +victualing +victualled +victualling +victuals +vicuna +vicunas +videlicet +video +videocassette +videocassettes +videodisc +videodiscs +videodisk +videodisks +videoed +videoing +videophone +videophones +videos +videotape +videotaped +videotapes +videotaping +vied +vies +view +viewed +viewer +viewers +viewership +viewfinder +viewfinders +viewing +viewings +viewpoint +viewpoints +views +vigesimal +vigil +vigilance +vigilant +vigilante +vigilantes +vigilantism +vigilantist +vigilantists +vigilantly +vigils +vignette +vignetted +vignettes +vignetting +vignettist +vignettists +vigor +vigorous +vigorously +vigour +viking +vikings +vile +vilely +vileness +viler +vilest +vilification +vilified +vilifies +vilify +vilifying +villa +village +villager +villagers +villages +villain +villainies +villainous +villains +villainy +villas +villein +villeinage +villeins +villi +villus +vinaigrette +vincible +vindicate +vindicated +vindicates +vindicating +vindication +vindications +vindicator +vindicators +vindictive +vindictively +vindictiveness +vine +vinegar +vinegary +vines +vineyard +vineyards +vino +vinous +vintage +vintages +vintner +vintners +vinyl +vinyls +viol +viola +violable +violas +violate +violated +violates +violating +violation +violations +violator +violators +violence +violent +violently +violet +violets +violin +violinist +violinists +violins +violist +violists +violoncellist +violoncellists +violoncello +violoncellos +viols +viper +viperous +vipers +virago +viragoes +viragos +viral +vireo +vireos +virgin +virginal +virginals +virginity +virgins +virgule +virgules +virile +virility +virologist +virologists +virology +virtual +virtually +virtue +virtues +virtuosi +virtuosity +virtuoso +virtuosos +virtuous +virtuously +virtuousness +virulence +virulent +virulently +virus +viruses +visa +visaed +visage +visages +visaing +visas +viscera +visceral +viscerally +viscid +viscose +viscosity +viscount +viscountcies +viscountcy +viscountess +viscountesses +viscounts +viscous +viscus +vise +vised +vises +visibility +visible +visibly +vising +vision +visionaries +visionary +visioned +visioning +visions +visit +visitant +visitants +visitation +visitations +visited +visiting +visitor +visitors +visits +visor +visors +vista +vistas +visual +visualization +visualize +visualized +visualizer +visualizers +visualizes +visualizing +visually +visuals +vita +vitae +vital +vitality +vitalization +vitalize +vitalized +vitalizes +vitalizing +vitally +vitals +vitamin +vitamins +vitas +vitiate +vitiated +vitiates +vitiating +vitiation +viticulture +viticulturist +viticulturists +vitreous +vitrifaction +vitrification +vitrified +vitrifies +vitrify +vitrifying +vitrine +vitrines +vitriol +vitriolic +vittles +vituperate +vituperated +vituperates +vituperating +vituperation +vituperative +viva +vivace +vivacious +vivaciously +vivaciousness +vivacity +vivaria +vivarium +vivariums +vivas +vivid +vivider +vividest +vividly +vividness +vivified +vivifies +vivify +vivifying +viviparous +vivisect +vivisected +vivisecting +vivisection +vivisectional +vivisectionist +vivisectionists +vivisects +vixen +vixenish +vixenishly +vixens +vizier +viziers +vizir +vizirs +vizor +vizors +vocable +vocables +vocabularies +vocabulary +vocal +vocalic +vocalist +vocalists +vocalization +vocalizations +vocalize +vocalized +vocalizes +vocalizing +vocally +vocals +vocation +vocational +vocations +vocative +vocatives +vociferate +vociferated +vociferates +vociferating +vociferation +vociferous +vociferously +vociferousness +vodka +vogue +vogues +voguish +voice +voiced +voiceless +voicelessly +voicelessness +voices +voicing +void +voidable +voided +voiding +voids +voila +voile +volatile +volatility +volatilize +volatilized +volatilizes +volatilizing +volcanic +volcano +volcanoes +volcanos +vole +voles +volition +volitional +volley +volleyball +volleyballs +volleyed +volleying +volleys +volt +voltage +voltages +voltaic +voltmeter +voltmeters +volts +volubility +voluble +volubly +volume +volumes +voluminous +voluminously +voluminousness +voluntaries +voluntarily +voluntarism +voluntary +volunteer +volunteered +volunteering +volunteerism +volunteers +voluptuaries +voluptuary +voluptuous +voluptuously +voluptuousness +volute +volutes +vomit +vomited +vomiting +vomits +voodoo +voodooed +voodooing +voodooism +voodoos +voracious +voraciously +voraciousness +voracity +vortex +vortexes +vortices +votaries +votary +vote +voted +voter +voters +votes +voting +votive +vouch +vouched +voucher +vouchers +vouches +vouching +vouchsafe +vouchsafed +vouchsafes +vouchsafing +vowed +vowel +vowels +vowing +vows +voyage +voyaged +voyager +voyagers +voyages +voyageur +voyageurs +voyaging +voyeur +voyeurism +voyeuristic +voyeurs +vulcanization +vulcanize +vulcanized +vulcanizes +vulcanizing +vulgar +vulgarer +vulgarest +vulgarian +vulgarians +vulgarism +vulgarisms +vulgarities +vulgarity +vulgarization +vulgarize +vulgarized +vulgarizer +vulgarizers +vulgarizes +vulgarizing +vulgarly +vulnerabilities +vulnerability +vulnerable +vulnerably +vulpine +vulture +vultures +vulturous +vulva +vulvae +vulvas +vying +wackier +wackiest +wackiness +wacko +wackos +wacky +wadded +wadding +waddle +waddled +waddles +waddling +wade +waded +wader +waders +wades +wadi +wading +wadis +wads +wafer +wafers +waffle +waffled +waffler +wafflers +waffles +waffling +waft +wafted +wafting +wafts +wage +waged +wager +wagered +wagerer +wagerers +wagering +wagers +wages +wagged +waggeries +waggery +wagging +waggish +waggishly +waggishness +waggle +waggled +waggles +waggling +waggon +waggons +waging +wagon +wagoner +wagoners +wagons +wags +wagtail +wagtails +waif +waifs +wail +wailed +wailer +wailers +wailing +wails +wain +wains +wainscot +wainscoted +wainscoting +wainscotings +wainscots +wainscotted +wainscotting +wainscottings +wainwright +wainwrights +waist +waistband +waistbands +waistcoat +waistcoats +waistline +waistlines +waists +wait +waited +waiter +waiters +waiting +waitperson +waitpersons +waitress +waitresses +waits +waitstaff +waive +waived +waiver +waivers +waives +waiving +wake +waked +wakeful +wakefully +wakefulness +waken +wakened +wakening +wakens +wakes +waking +wale +waled +wales +waling +walk +walkaway +walkaways +walked +walker +walkers +walking +walkout +walkouts +walkover +walkovers +walks +walkway +walkways +wall +wallabies +wallaby +wallboard +walled +wallet +wallets +walleye +walleyed +walleyes +wallflower +wallflowers +walling +wallop +walloped +walloping +wallopings +wallops +wallow +wallowed +wallowing +wallows +wallpaper +wallpapered +wallpapering +wallpapers +walls +walnut +walnuts +walrus +walruses +waltz +waltzed +waltzer +waltzers +waltzes +waltzing +wampum +wand +wander +wandered +wanderer +wanderers +wandering +wanderings +wanderlust +wanderlusts +wanders +wands +wane +waned +wanes +wangle +wangled +wangler +wanglers +wangles +wangling +waning +wanly +wanna +wannabe +wannabes +wanner +wanness +wannest +want +wanted +wanting +wanton +wantoned +wantoning +wantonly +wantonness +wantons +wants +wapiti +wapitis +warble +warbled +warbler +warblers +warbles +warbling +warbonnet +warbonnets +ward +warded +warden +wardens +warder +warders +warding +wardrobe +wardrobes +wardroom +wardrooms +wards +ware +warehouse +warehoused +warehouses +warehousing +wares +warfare +warhead +warheads +warhorse +warhorses +warier +wariest +warily +wariness +warlike +warlock +warlocks +warlord +warlords +warm +warmblooded +warmed +warmer +warmers +warmest +warmhearted +warmheartedness +warming +warmish +warmly +warmness +warmonger +warmongering +warmongers +warms +warmth +warmup +warmups +warn +warned +warning +warnings +warns +warp +warpath +warpaths +warped +warping +warplane +warplanes +warps +warrant +warranted +warrantied +warranties +warranting +warrants +warranty +warrantying +warred +warren +warrens +warring +warrior +warriors +wars +warship +warships +wart +warthog +warthogs +wartier +wartiest +wartime +warts +warty +wary +wash +washable +washables +washbasin +washbasins +washboard +washboards +washbowl +washbowls +washcloth +washcloths +washed +washer +washers +washerwoman +washerwomen +washes +washier +washiest +washing +washings +washout +washouts +washrag +washrags +washroom +washrooms +washstand +washstands +washtub +washtubs +washy +wasp +waspish +waspishly +waspishness +wasps +wassail +wassailed +wassailing +wassails +wast +wastage +waste +wastebasket +wastebaskets +wasted +wasteful +wastefully +wastefulness +wasteland +wastelands +wastepaper +waster +wasters +wastes +wasting +wastrel +wastrels +watch +watchband +watchbands +watchdog +watchdogs +watched +watcher +watchers +watches +watchful +watchfully +watchfulness +watching +watchmaker +watchmakers +watchmaking +watchman +watchmen +watchtower +watchtowers +watchword +watchwords +water +waterbed +waterbeds +waterbird +waterbirds +waterborne +watercolor +watercolors +watercourse +watercourses +watercraft +watercress +watered +waterfall +waterfalls +waterfowl +waterfowls +waterfront +waterfronts +waterhole +waterholes +waterier +wateriest +wateriness +watering +waterlilies +waterlily +waterline +waterlines +waterlogged +watermark +watermarked +watermarking +watermarks +watermelon +watermelons +watermill +watermills +waterpower +waterproof +waterproofed +waterproofing +waterproofs +waters +watershed +watersheds +waterside +watersides +waterspout +waterspouts +watertight +waterway +waterways +waterwheel +waterwheels +waterworks +watery +watt +wattage +wattle +wattled +wattles +wattling +watts +wave +waved +wavelength +wavelengths +wavelet +wavelets +wavelike +waver +wavered +waverer +waverers +wavering +waveringly +wavers +waves +wavier +waviest +waviness +waving +wavy +waxed +waxen +waxes +waxier +waxiest +waxiness +waxing +waxwing +waxwings +waxwork +waxworks +waxy +waybill +waybills +wayfarer +wayfarers +wayfaring +wayfarings +waylaid +waylay +waylayer +waylayers +waylaying +waylays +ways +wayside +waysides +wayward +waywardly +waywardness +weak +weaken +weakened +weakener +weakeners +weakening +weakens +weaker +weakest +weakfish +weakfishes +weakling +weaklings +weakly +weakness +weaknesses +weal +weals +wealth +wealthier +wealthiest +wealthiness +wealthy +wean +weaned +weaning +weans +weapon +weaponless +weaponry +weapons +wear +wearable +wearer +wearers +wearied +wearier +wearies +weariest +wearily +weariness +wearing +wearisome +wearisomely +wears +weary +wearying +weasel +weaseled +weaseling +weaselled +weaselling +weaselly +weasels +weather +weathercock +weathercocks +weathered +weathering +weatherization +weatherize +weatherized +weatherizes +weatherizing +weatherman +weathermen +weatherperson +weatherpersons +weatherproof +weatherproofed +weatherproofing +weatherproofs +weathers +weatherstrip +weatherstripped +weatherstripping +weatherstrips +weave +weaved +weaver +weavers +weaves +weaving +webbed +webbing +webfeet +webfoot +webs +website +websites +wedded +wedding +weddings +wedge +wedged +wedges +wedgie +wedgies +wedging +wedlock +weds +weed +weeded +weeder +weeders +weedier +weediest +weeding +weedless +weeds +weedy +weeing +week +weekday +weekdays +weekend +weekended +weekending +weekends +weeklies +weekly +weeknight +weeknights +weeks +ween +weened +weenie +weenier +weenies +weeniest +weening +weens +weensier +weensiest +weensy +weeny +weep +weeper +weepers +weepier +weepies +weepiest +weeping +weeps +weepy +weer +wees +weest +weevil +weevils +weft +wefts +weigh +weighed +weighing +weighs +weight +weighted +weightier +weightiest +weightily +weightiness +weighting +weightless +weightlessly +weightlessness +weightlifter +weightlifters +weightlifting +weights +weighty +weir +weird +weirder +weirdest +weirdie +weirdies +weirdly +weirdness +weirdo +weirdos +weirs +welch +welched +welches +welching +welcome +welcomed +welcomes +welcoming +weld +weldable +welded +welder +welders +welding +welds +welfare +welkin +well +welled +wellhead +wellheads +welling +wellington +wellingtons +wellness +wells +wellspring +wellsprings +welsh +welshed +welsher +welshers +welshes +welshing +welt +welted +welter +weltered +weltering +welters +welterweight +welterweights +welting +welts +wench +wenches +wend +wended +wending +wends +wens +went +wept +were +werewolf +werewolves +werwolf +werwolves +west +westbound +westerlies +westerly +western +westerner +westerners +westernization +westernize +westernized +westernizes +westernizing +westernmost +westerns +westward +westwards +wetback +wetbacks +wetland +wetlands +wetly +wetness +wets +wetted +wetter +wetters +wettest +wetting +whack +whacked +whacker +whackers +whackier +whackiest +whacking +whacks +whacky +whale +whaleboat +whaleboats +whalebone +whaled +whaler +whalers +whales +whaling +wham +whammed +whammies +whamming +whammy +whams +wharf +wharfs +wharves +what +whatchamacallit +whatchamacallits +whatever +whatnot +whats +whatsoever +wheal +wheals +wheat +wheaten +whee +wheedle +wheedled +wheedler +wheedlers +wheedles +wheedling +wheel +wheelbarrow +wheelbarrows +wheelbase +wheelbases +wheelchair +wheelchairs +wheeled +wheelhouse +wheelhouses +wheelie +wheelies +wheeling +wheels +wheelwright +wheelwrights +wheeze +wheezed +wheezes +wheezier +wheeziest +wheezily +wheeziness +wheezing +wheezy +whelk +whelks +whelm +whelmed +whelming +whelms +whelp +whelped +whelping +whelps +when +whence +whenever +whens +whensoever +where +whereabouts +whereas +whereat +whereby +wherefore +wherefores +wherein +whereof +whereon +wheres +wheresoever +whereto +whereupon +wherever +wherewith +wherewithal +wherries +wherry +whet +whether +whets +whetstone +whetstones +whetted +whetting +whew +whey +which +whichever +whiff +whiffed +whiffing +whiffletree +whiffletrees +whiffs +while +whiled +whiles +whiling +whilom +whilst +whim +whimper +whimpered +whimpering +whimpers +whims +whimsey +whimseys +whimsical +whimsicality +whimsically +whimsies +whimsy +whine +whined +whiner +whiners +whines +whinier +whiniest +whining +whinnied +whinnies +whinny +whinnying +whiny +whip +whipcord +whiplash +whiplashes +whipped +whipper +whippers +whippersnapper +whippersnappers +whippet +whippets +whipping +whippings +whippletree +whippletrees +whippoorwill +whippoorwills +whips +whipsaw +whipsawed +whipsawing +whipsawn +whipsaws +whir +whirl +whirled +whirligig +whirligigs +whirling +whirlpool +whirlpools +whirls +whirlwind +whirlwinds +whirlybird +whirlybirds +whirr +whirred +whirring +whirrs +whirs +whisk +whisked +whisker +whiskered +whiskers +whiskery +whiskey +whiskeys +whiskies +whisking +whisks +whisky +whisper +whispered +whisperer +whisperers +whispering +whispers +whist +whistle +whistled +whistler +whistlers +whistles +whistling +whit +white +whitecap +whitecaps +whitefish +whitefishes +whitehead +whiteheads +whiten +whitened +whitener +whiteners +whiteness +whitening +whitenings +whitens +whiteout +whiteouts +whiter +whites +whitest +whitetail +whitetails +whitewall +whitewalls +whitewash +whitewashed +whitewashes +whitewashing +whitewater +whitey +whiteys +whither +whiting +whitings +whitish +whits +whittle +whittled +whittler +whittlers +whittles +whittling +whiz +whizkid +whizkids +whizz +whizzbang +whizzbangs +whizzed +whizzes +whizzing +whoa +whodunit +whodunits +whodunnit +whodunnits +whoever +whole +wholehearted +wholeheartedly +wholeheartedness +wholeness +wholes +wholesale +wholesaled +wholesaler +wholesalers +wholesales +wholesaling +wholesome +wholesomely +wholesomeness +wholesomer +wholesomest +wholly +whom +whomever +whomsoever +whoop +whooped +whoopee +whooper +whoopers +whooping +whoops +whoosh +whooshed +whooshes +whooshing +whopper +whoppers +whopping +whore +whorehouse +whorehouses +whoreish +whores +whorish +whorl +whorled +whorls +whose +whoso +whosoever +whys +wick +wicked +wickeder +wickedest +wickedly +wickedness +wicker +wickers +wickerwork +wicket +wickets +wicking +wicks +wide +widely +widemouthed +widen +widened +widener +wideners +wideness +widening +widens +wider +widespread +widest +widgeon +widgeons +widow +widowed +widower +widowers +widowhood +widowing +widows +width +widths +wield +wielded +wielder +wielders +wielding +wields +wiener +wieners +wienie +wienies +wife +wifeless +wifely +wigeon +wigeons +wigged +wigging +wiggle +wiggled +wiggler +wigglers +wiggles +wigglier +wiggliest +wiggling +wiggly +wight +wights +wiglet +wiglets +wigs +wigwag +wigwagged +wigwagging +wigwags +wigwam +wigwams +wild +wildcat +wildcats +wildcatted +wildcatter +wildcatters +wildcatting +wildebeest +wildebeests +wilder +wilderness +wildernesses +wildest +wildfire +wildfires +wildflower +wildflowers +wildfowl +wildfowls +wildlife +wildly +wildness +wilds +wile +wiled +wiles +wilful +wilfully +wilfulness +wilier +wiliest +wiliness +wiling +will +willed +willful +willfully +willfulness +willies +willing +willingly +willingness +williwaw +williwaws +willow +willowier +willowiest +willows +willowy +willpower +wills +wilt +wilted +wilting +wilts +wily +wimp +wimpier +wimpiest +wimpish +wimple +wimpled +wimples +wimpling +wimps +wimpy +wince +winced +winces +winch +winched +winches +winching +wincing +wind +windbag +windbags +windblown +windbreak +windbreaker +windbreakers +windbreaks +windburn +windburned +windchill +winded +winder +winders +windfall +windfalls +windflower +windflowers +windier +windiest +windily +windiness +winding +windjammer +windjammers +windlass +windlasses +windless +windmill +windmilled +windmilling +windmills +window +windowless +windowpane +windowpanes +windows +windowsill +windowsills +windpipe +windpipes +windproof +windrow +windrows +winds +windshield +windshields +windsock +windsocks +windstorm +windstorms +windsurf +windsurfed +windsurfer +windsurfers +windsurfing +windsurfs +windswept +windup +windups +windward +windy +wine +wined +wineglass +wineglasses +winegrower +winegrowers +winemaker +winemakers +wineries +winery +wines +wing +wingding +wingdings +winged +winging +wingless +winglike +wings +wingspan +wingspans +wingspread +wingspreads +wingtip +wingtips +winier +winiest +wining +wink +winked +winker +winkers +winking +winkle +winkled +winkles +winkling +winks +winnable +winner +winners +winning +winningly +winnings +winnow +winnowed +winnower +winnowers +winnowing +winnows +wino +winos +wins +winsome +winsomely +winsomeness +winsomer +winsomest +winter +wintered +wintergreen +winterier +winteriest +wintering +winterize +winterized +winterizes +winterizing +winters +wintertime +wintery +wintrier +wintriest +wintry +winy +wipe +wiped +wiper +wipers +wipes +wiping +wire +wired +wirehair +wirehairs +wireless +wirelesses +wires +wiretap +wiretapped +wiretapper +wiretappers +wiretapping +wiretaps +wirier +wiriest +wiriness +wiring +wiry +wisdom +wise +wiseacre +wiseacres +wisecrack +wisecracked +wisecracking +wisecracks +wisely +wiser +wises +wisest +wish +wishbone +wishbones +wished +wisher +wishers +wishes +wishful +wishfully +wishing +wisp +wispier +wispiest +wisps +wispy +wist +wistaria +wistarias +wisteria +wisterias +wistful +wistfully +wistfulness +witch +witchcraft +witched +witchery +witches +witching +with +withal +withdraw +withdrawal +withdrawals +withdrawing +withdrawn +withdraws +withdrew +withe +withed +wither +withered +withering +witheringly +withers +withes +withheld +withhold +withholding +withholds +within +withing +without +withstand +withstanding +withstands +withstood +witless +witlessly +witlessness +witness +witnessed +witnesses +witnessing +wits +witted +witticism +witticisms +wittier +wittiest +wittily +wittiness +witting +wittingly +witty +wive +wived +wives +wiving +wizard +wizardry +wizards +wizened +wizes +wizzes +woad +wobble +wobbled +wobbles +wobblier +wobbliest +wobbliness +wobbling +wobbly +woebegone +woeful +woefuller +woefullest +woefully +woefulness +woes +woke +woken +woks +wold +wolds +wolf +wolfed +wolfhound +wolfhounds +wolfing +wolfish +wolfram +wolfs +wolverine +wolverines +wolves +woman +womanhood +womanish +womanize +womanized +womanizer +womanizers +womanizes +womanizing +womankind +womanlier +womanliest +womanlike +womanliness +womanly +womb +wombat +wombats +wombs +women +womenfolk +womenfolks +wonder +wondered +wonderful +wonderfully +wonderfulness +wondering +wonderingly +wonderland +wonderlands +wonderment +wonders +wondrous +wondrously +wonk +wonkier +wonkiest +wonks +wonky +wont +wonted +wonton +wontons +wood +woodbine +woodblock +woodblocks +woodcarver +woodcarvers +woodcarving +woodcarvings +woodchuck +woodchucks +woodcock +woodcocks +woodcraft +woodcut +woodcuts +woodcutter +woodcutters +woodcutting +wooded +wooden +woodener +woodenest +woodenly +woodenness +woodier +woodies +woodiest +woodiness +wooding +woodland +woodlands +woodlot +woodlots +woodman +woodmen +woodpecker +woodpeckers +woodpile +woodpiles +woods +woodshed +woodsheds +woodsier +woodsiest +woodsiness +woodsman +woodsmen +woodsy +woodwind +woodwinds +woodwork +woodworker +woodworkers +woodworking +woody +wooed +wooer +wooers +woof +woofed +woofer +woofers +woofing +woofs +wooing +wool +woolen +woolens +woolgathering +woolie +woolier +woolies +wooliest +woollen +woollens +woollier +woollies +woolliest +woolliness +woolly +wooly +woos +woozier +wooziest +woozily +wooziness +woozy +word +wordage +wordbook +wordbooks +worded +wordier +wordiest +wordily +wordiness +wording +wordings +wordless +wordlessly +wordplay +words +wordy +wore +work +workable +workaday +workaholic +workaholics +workbench +workbenches +workbook +workbooks +workday +workdays +worked +worker +workers +workfare +workforce +workhorse +workhorses +workhouse +workhouses +working +workingman +workingmen +workings +workingwoman +workingwomen +workload +workloads +workman +workmanlike +workmanship +workmen +workout +workouts +workplace +workplaces +workroom +workrooms +works +worksheet +worksheets +workshop +workshops +workstation +workstations +worktable +worktables +workup +workups +workweek +workweeks +world +worldlier +worldliest +worldliness +worldly +worlds +worldview +worldviews +worldwide +worm +wormed +wormhole +wormholes +wormier +wormiest +worming +worms +wormwood +wormy +worn +worried +worrier +worriers +worries +worriment +worrisome +worry +worrying +worryingly +worrywart +worrywarts +worse +worsen +worsened +worsening +worsens +worship +worshiped +worshiper +worshipers +worshipful +worshiping +worshipped +worshipper +worshippers +worshipping +worships +worst +worsted +worsting +worsts +wort +worth +worthier +worthies +worthiest +worthily +worthiness +worthless +worthlessly +worthlessness +worthwhile +worthy +would +wouldst +wound +wounded +wounding +wounds +wove +woven +wowed +wowing +wows +wrack +wraith +wraiths +wrangle +wrangled +wrangler +wranglers +wrangles +wrangling +wrap +wraparound +wraparounds +wrapped +wrapper +wrappers +wrapping +wrappings +wraps +wrapt +wrasse +wrasses +wrath +wrathful +wrathfully +wreak +wreaked +wreaking +wreaks +wreath +wreathe +wreathed +wreathes +wreathing +wreaths +wreck +wreckage +wrecked +wrecker +wreckers +wrecking +wrecks +wren +wrench +wrenched +wrenches +wrenching +wrens +wrest +wrested +wresting +wrestle +wrestled +wrestler +wrestlers +wrestles +wrestling +wrests +wretch +wretched +wretcheder +wretchedest +wretchedly +wretchedness +wretches +wrier +wriest +wriggle +wriggled +wriggler +wrigglers +wriggles +wrigglier +wriggliest +wriggling +wriggly +wright +wrights +wring +wringer +wringers +wringing +wrings +wrinkle +wrinkled +wrinkles +wrinklier +wrinklies +wrinkliest +wrinkling +wrinkly +wrist +wristband +wristbands +wrists +wristwatch +wristwatches +writ +write +writer +writers +writes +writhe +writhed +writhes +writhing +writing +writings +writs +written +wrong +wrongdoer +wrongdoers +wrongdoing +wrongdoings +wronged +wronger +wrongest +wrongful +wrongfully +wrongfulness +wrongheaded +wrongheadedly +wrongheadedness +wronging +wrongly +wrongness +wrongs +wrote +wroth +wrought +wrung +wryer +wryest +wryly +wryness +wurst +wursts +wuss +wusses +wussier +wussies +wussiest +wussy +xenon +xenophobe +xenophobes +xenophobia +xenophobic +xerographic +xerography +xerox +xeroxed +xeroxes +xeroxing +xylem +xylophone +xylophones +xylophonist +xylophonists +yacht +yachted +yachting +yachts +yachtsman +yachtsmen +yachtswoman +yachtswomen +yack +yacked +yacking +yacks +yahoo +yahoos +yakked +yakking +yaks +yammer +yammered +yammerer +yammerers +yammering +yammers +yams +yang +yank +yanked +yanking +yanks +yapped +yapping +yaps +yard +yardage +yardages +yardarm +yardarms +yardman +yardmaster +yardmasters +yardmen +yards +yardstick +yardsticks +yarmelke +yarmelkes +yarmulke +yarmulkes +yarn +yarns +yarrow +yawed +yawing +yawl +yawls +yawn +yawned +yawner +yawners +yawning +yawns +yaws +yeah +yeahs +year +yearbook +yearbooks +yearlies +yearling +yearlings +yearlong +yearly +yearn +yearned +yearning +yearnings +yearns +years +yeas +yeast +yeastier +yeastiest +yeasts +yeasty +yegg +yeggs +yell +yelled +yelling +yellow +yellowed +yellower +yellowest +yellowing +yellowish +yellowness +yellows +yellowy +yells +yelp +yelped +yelping +yelps +yens +yeoman +yeomanry +yeomen +yeps +yeses +yeshiva +yeshivah +yeshivahs +yeshivas +yeshivoth +yessed +yessing +yesterday +yesterdays +yesteryear +yeti +yetis +yews +yield +yielded +yielding +yields +yikes +yipe +yipped +yippee +yipping +yips +yodel +yodeled +yodeler +yodelers +yodeling +yodelled +yodeller +yodellers +yodelling +yodels +yoga +yoghourt +yoghourts +yoghurt +yoghurts +yogi +yogin +yogins +yogis +yogurt +yogurts +yoke +yoked +yokel +yokels +yokes +yoking +yolk +yolked +yolks +yonder +yore +young +younger +youngest +youngish +youngster +youngsters +your +yours +yourself +yourselves +yous +youth +youthful +youthfully +youthfulness +youths +yowl +yowled +yowling +yowls +ytterbium +yttrium +yuan +yucca +yuccas +yuck +yucked +yuckier +yuckiest +yucking +yucks +yucky +yukked +yukking +yuks +yule +yuletide +yummier +yummiest +yummy +yuppie +yuppies +yuppy +yups +yurt +yurts +zanier +zanies +zaniest +zaniness +zany +zapped +zapper +zappers +zapping +zaps +zeal +zealot +zealotry +zealots +zealous +zealously +zealousness +zebra +zebras +zebu +zebus +zeds +zeitgeist +zeitgeists +zenith +zeniths +zephyr +zephyrs +zeppelin +zeppelins +zero +zeroed +zeroes +zeroing +zeros +zest +zestful +zestfully +zestfulness +zestier +zestiest +zests +zesty +zeta +zetas +zigzag +zigzagged +zigzagging +zigzags +zilch +zillion +zillions +zinc +zinced +zincing +zincked +zincking +zincs +zinfandel +zing +zinged +zinger +zingers +zingier +zingiest +zinging +zings +zingy +zinnia +zinnias +zipped +zipper +zippered +zippering +zippers +zippier +zippiest +zipping +zippy +zips +zircon +zirconium +zircons +zither +zithers +zits +zloty +zlotys +zodiac +zodiacal +zodiacs +zombi +zombie +zombies +zombis +zonal +zonally +zone +zoned +zones +zoning +zonked +zookeeper +zookeepers +zoological +zoologically +zoologist +zoologists +zoology +zoom +zoomed +zooming +zooms +zoophyte +zoophytes +zoophytic +zoos +zounds +zucchini +zucchinis +zwieback +zydeco +zygote +zygotes +zygotic +zymurgy diff --git a/apps/bee/metadata.json b/apps/bee/metadata.json new file mode 100644 index 000000000..cfc91155a --- /dev/null +++ b/apps/bee/metadata.json @@ -0,0 +1,15 @@ +{ "id": "bee", + "name": "Bee", + "shortName":"Bee", + "icon": "app.png", + "version":"0.03", + "description": "Spelling bee", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "tags": "game,text", + "storage": [ + {"name":"bee.app.js","url":"bee.app.js"}, + {"name":"bee.words","url":"bee_words_2of12"}, + {"name":"bee.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/berlinc/metadata.json b/apps/berlinc/metadata.json index 49601cbd3..85c42fc47 100644 --- a/apps/berlinc/metadata.json +++ b/apps/berlinc/metadata.json @@ -7,6 +7,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "screenshots": [{"url":"berlin-clock-screenshot.png"}], "storage": [ diff --git a/apps/bikespeedo/ChangeLog b/apps/bikespeedo/ChangeLog new file mode 100644 index 000000000..2a3023750 --- /dev/null +++ b/apps/bikespeedo/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Barometer altitude adjustment setting diff --git a/apps/bikespeedo/Hochrad120px.gif b/apps/bikespeedo/Hochrad120px.gif new file mode 100644 index 000000000..1952cf44f Binary files /dev/null and b/apps/bikespeedo/Hochrad120px.gif differ diff --git a/apps/bikespeedo/Hochrad120px.png b/apps/bikespeedo/Hochrad120px.png new file mode 100644 index 000000000..2c2d4e1ef Binary files /dev/null and b/apps/bikespeedo/Hochrad120px.png differ diff --git a/apps/bikespeedo/README.md b/apps/bikespeedo/README.md new file mode 100644 index 000000000..e4ce5ea5c --- /dev/null +++ b/apps/bikespeedo/README.md @@ -0,0 +1,16 @@ +## GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude... + +![](Hochrad120px.png)...all taken from internal sources. + +#### To speed-up GPS reception it is strongly recommended to upload AGPS data with ["Assisted GPS Update"](https://banglejs.com/apps/?id=assistedgps) + +#### If "CALIB!" is shown on the display or the compass heading differs too much from GPS heading, compass calibration should be done with the ["Navigation Compass" App](https://banglejs.com/apps/?id=magnav) + +Permanently diverging Barometer Altitude values can be compensated in the settings menu. + +Please report bugs to https://github.com/espruino/BangleApps/issues/new?assignees=&labels=bug&template=bangle-bug-report-custom-form.yaml&title=%5BBike+Speedometer%5D+Short+description+of+bug + +**Credits:**
+Bike Speedometer App by github.com/HilmarSt
+Big parts of the software are based on github.com/espruino/BangleApps/tree/master/apps/speedalt
+Compass and Compass Calibration based on github.com/espruino/BangleApps/tree/master/apps/magnav diff --git a/apps/bikespeedo/Screenshot.png b/apps/bikespeedo/Screenshot.png new file mode 100644 index 000000000..fd27728e4 Binary files /dev/null and b/apps/bikespeedo/Screenshot.png differ diff --git a/apps/bikespeedo/app-icon.js b/apps/bikespeedo/app-icon.js new file mode 100644 index 000000000..411d644fd --- /dev/null +++ b/apps/bikespeedo/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/ABO/AokfAgf+r4FD3lPBQcZw4FC/nD+4FC/Pn+YFCBIP7GQ4aDEIMDAol/ApQRFuAFEv0/BoQXBx0HAoPgh/nn40C4fwEoP+n/4/BWC/weBBYP5BAM/C4Pz7/7z+f//n7/z5/f//vA4Pv5//AIPv8/n//d//Ou5yBDIOfu58Bz42B+Z8Bz/8AoPgv+/AoP7w0f3IFBnc/5+bL4Oyv/nEYP/+X/mYFC+n8mff8ln+v4vfd7tfsvzvfN7tPtv2vPn6H35vg/f36vX7vj/fz9vvznH+Z3B/0+5/3/l//iDBMwMf+KEBOAPBUoOCj///CNBUQQAEA=")) diff --git a/apps/bikespeedo/app.js b/apps/bikespeedo/app.js new file mode 100644 index 000000000..a62a429e5 --- /dev/null +++ b/apps/bikespeedo/app.js @@ -0,0 +1,554 @@ +// Bike Speedometer by https://github.com/HilmarSt +// Big parts of this software are based on https://github.com/espruino/BangleApps/tree/master/apps/speedalt +// Compass and Compass Calibration based on https://github.com/espruino/BangleApps/tree/master/apps/magnav + +const BANGLEJS2 = 1; +const screenH = g.getHeight(); +const screenYstart = 24; // 0..23 for widgets +const screenY_Half = screenH / 2 + screenYstart; +const screenW = g.getWidth(); +const screenW_Half = screenW / 2; +const fontFactorB2 = 2/3; +const colfg=g.theme.fg, colbg=g.theme.bg; +const col1=colfg, colUncertain="#88f"; // if (lf.fix) g.setColor(col1); else g.setColor(colUncertain); + +var altiGPS=0, altiBaro=0; +var hdngGPS=0, hdngCompass=0, calibrateCompass=false; + +/*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ +var KalmanFilter = (function () { + 'use strict'; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + /** + * KalmanFilter + * @class + * @author Wouter Bulten + * @see {@link http://github.com/wouterbulten/kalmanjs} + * @version Version: 1.0.0-beta + * @copyright Copyright 2015-2018 Wouter Bulten + * @license MIT License + * @preserve + */ + var KalmanFilter = + /*#__PURE__*/ + function () { + /** + * Create 1-dimensional kalman filter + * @param {Number} options.R Process noise + * @param {Number} options.Q Measurement noise + * @param {Number} options.A State vector + * @param {Number} options.B Control vector + * @param {Number} options.C Measurement vector + * @return {KalmanFilter} + */ + function KalmanFilter() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$R = _ref.R, + R = _ref$R === void 0 ? 1 : _ref$R, + _ref$Q = _ref.Q, + Q = _ref$Q === void 0 ? 1 : _ref$Q, + _ref$A = _ref.A, + A = _ref$A === void 0 ? 1 : _ref$A, + _ref$B = _ref.B, + B = _ref$B === void 0 ? 0 : _ref$B, + _ref$C = _ref.C, + C = _ref$C === void 0 ? 1 : _ref$C; + + _classCallCheck(this, KalmanFilter); + + this.R = R; // noise power desirable + + this.Q = Q; // noise power estimated + + this.A = A; + this.C = C; + this.B = B; + this.cov = NaN; + this.x = NaN; // estimated signal without noise + } + /** + * Filter a new value + * @param {Number} z Measurement + * @param {Number} u Control + * @return {Number} + */ + + + _createClass(KalmanFilter, [{ + key: "filter", + value: function filter(z) { + var u = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + if (isNaN(this.x)) { + this.x = 1 / this.C * z; + this.cov = 1 / this.C * this.Q * (1 / this.C); + } else { + // Compute prediction + var predX = this.predict(u); + var predCov = this.uncertainty(); // Kalman gain + + var K = predCov * this.C * (1 / (this.C * predCov * this.C + this.Q)); // Correction + + this.x = predX + K * (z - this.C * predX); + this.cov = predCov - K * this.C * predCov; + } + + return this.x; + } + /** + * Predict next value + * @param {Number} [u] Control + * @return {Number} + */ + + }, { + key: "predict", + value: function predict() { + var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + return this.A * this.x + this.B * u; + } + /** + * Return uncertainty of filter + * @return {Number} + */ + + }, { + key: "uncertainty", + value: function uncertainty() { + return this.A * this.cov * this.A + this.R; + } + /** + * Return the last filtered measurement + * @return {Number} + */ + + }, { + key: "lastMeasurement", + value: function lastMeasurement() { + return this.x; + } + /** + * Set measurement noise Q + * @param {Number} noise + */ + + }, { + key: "setMeasurementNoise", + value: function setMeasurementNoise(noise) { + this.Q = noise; + } + /** + * Set the process noise R + * @param {Number} noise + */ + + }, { + key: "setProcessNoise", + value: function setProcessNoise(noise) { + this.R = noise; + } + }]); + + return KalmanFilter; + }(); + + return KalmanFilter; + +}()); + + +//==================================== MAIN ==================================== + +var lf = {fix:0,satellites:0}; +var showMax = 0; // 1 = display the max values. 0 = display the cur fix +var canDraw = 1; +var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time. +var sec; // actual seconds for testing purposes + +var max = {}; +max.spd = 0; +max.alt = 0; +max.n = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. + +var emulator = (process.env.BOARD=="EMSCRIPTEN" || process.env.BOARD=="EMSCRIPTEN2")?1:0; // 1 = running in emulator. Supplies test values; + +var wp = {}; // Waypoint to use for distance from cur position. +var SATinView = 0; + +function radians(a) { + return a*Math.PI/180; +} + +function distance(a,b){ + var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2)); + var y = radians(b.lat-a.lat); + + // Distance in selected units + var d = Math.sqrt(x*x + y*y) * 6371000; + d = (d/parseFloat(cfg.dist)).toFixed(2); + if ( d >= 100 ) d = parseFloat(d).toFixed(1); + if ( d >= 1000 ) d = parseFloat(d).toFixed(0); + + return d; +} + +function drawFix(dat) { + + if (!canDraw) return; + + g.clearRect(0,screenYstart,screenW,screenH); + + var v = ''; + var u=''; + + // Primary Display + v = (cfg.primSpd)?dat.speed.toString():dat.alt.toString(); + + // Primary Units + u = (cfg.primSpd)?cfg.spd_unit:dat.alt_units; + + drawPrimary(v,u); + + // Secondary Display + v = (cfg.primSpd)?dat.alt.toString():dat.speed.toString(); + + // Secondary Units + u = (cfg.primSpd)?dat.alt_units:cfg.spd_unit; + + drawSecondary(v,u); + + // Time + drawTime(); + + //Sats + if ( dat.age > 10 ) { + if ( dat.age > 90 ) dat.age = '>90'; + drawSats('Age:'+dat.age); + } + else if (!BANGLEJS2) { + drawSats('Sats:'+dat.sats); + } else { + if (lf.fix) { + drawSats('Sats:'+dat.sats); + } else { + drawSats('View:' + SATinView); + } + } + g.reset(); +} + + +function drawClock() { + if (!canDraw) return; + g.clearRect(0,screenYstart,screenW,screenH); + drawTime(); + g.reset(); +} + + +function drawPrimary(n,u) { + //if(emulator)console.log("\n1: " + n +" "+ u); + var s=40; // Font size + var l=n.length; + + if ( l <= 7 ) s=48; + if ( l <= 6 ) s=55; + if ( l <= 5 ) s=66; + if ( l <= 4 ) s=85; + if ( l <= 3 ) s=110; + + // X -1=left (default), 0=center, 1=right + // Y -1=top (default), 0=center, 1=bottom + g.setFontAlign(0,-1); // center, top + if (lf.fix) g.setColor(col1); else g.setColor(colUncertain); + if (BANGLEJS2) s *= fontFactorB2; + g.setFontVector(s); + g.drawString(n, screenW_Half - 10, screenYstart); + + // Primary Units + s = 35; // Font size + g.setFontAlign(1,-1,3); // right, top, rotate + g.setColor(col1); + if (BANGLEJS2) s = 20; + g.setFontVector(s); + g.drawString(u, screenW - 20, screenYstart + 2); +} + + +function drawSecondary(n,u) { + //if(emulator)console.log("2: " + n +" "+ u); + + if (calibrateCompass) hdngCompass = "CALIB!"; + else hdngCompass +="°"; + + g.setFontAlign(0,1); + g.setColor(col1); + + g.setFontVector(12).drawString("Altitude GPS / Barometer", screenW_Half - 5, screenY_Half - 10); + g.setFontVector(20); + g.drawString(n+" "+u+" / "+altiBaro+" "+u, screenW_Half, screenY_Half + 11); + + g.setFontVector(12).drawString("Heading GPS / Compass", screenW_Half - 10, screenY_Half + 26); + g.setFontVector(20); + g.drawString(hdngGPS+"° / "+hdngCompass, screenW_Half, screenY_Half + 47); +} + + +function drawTime() { + var x = 0, y = screenH; + g.setFontAlign(-1,1); // left, bottom + g.setFont("6x8", 2); + + g.setColor(colbg); + g.drawString(time,x+1,y); // clear old time + + time = require("locale").time(new Date(),1); + + g.setColor(colfg); // draw new time + g.drawString(time,x+2,y); +} + + +function drawSats(sats) { + + g.setColor(col1); + g.setFont("6x8", 2); + g.setFontAlign(1,1); //right, bottom + g.drawString(sats,screenW,screenH); + + g.setFontVector(18); + g.setColor(col1); + + if ( cfg.modeA == 1 ) { + if ( showMax ) { + g.setFontAlign(0,1); //centre, bottom + g.drawString('MAX',120,164); + } + } +} + +function onGPS(fix) { + + if ( emulator ) { + fix.fix = 1; + fix.speed = Math.random()*30; // calmed by Kalman filter if cfg.spdFilt + fix.alt = Math.random()*200 -20; // calmed by Kalman filter if cfg.altFilt + fix.lat = 50.59; // google.de/maps/@50.59,8.53,17z + fix.lon = 8.53; + fix.course = 365; + fix.satellites = sec; + fix.time = new Date(); + fix.smoothed = 0; + } + + var m; + + var sp = '---'; + var al = '---'; + var di = '---'; + var age = '---'; + + if (fix.fix) lf = fix; + + hdngGPS = lf.course; + if (isNaN(hdngGPS)) hdngGPS = "---"; + else if (0 == hdngGPS) hdngGPS = "0?"; + else hdngGPS = hdngGPS.toFixed(0); + + if (emulator) hdngCompass = hdngGPS; + if (emulator) altiBaro = lf.alt.toFixed(0); + + if (lf.fix) { + + if (BANGLEJS2 && !emulator) Bangle.removeListener('GPS-raw', onGPSraw); + + // Smooth data + if ( lf.smoothed !== 1 ) { + if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed); + if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt); + lf.smoothed = 1; + if ( max.n <= 15 ) max.n++; + } + + + // Speed + if ( cfg.spd == 0 ) { + m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units + sp = parseFloat(m[1]); + cfg.spd_unit = m[2]; + } + else sp = parseFloat(lf.speed)/parseFloat(cfg.spd); // Calculate for selected units + + if ( sp < 10 ) sp = sp.toFixed(1); + else sp = Math.round(sp); + if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = parseFloat(sp); + + // Altitude + al = lf.alt; + al = Math.round(parseFloat(al)/parseFloat(cfg.alt)); + if (parseFloat(al) > parseFloat(max.alt) && max.n > 15 ) max.alt = parseFloat(al); + + // Distance to waypoint + di = distance(lf,wp); + if (isNaN(di)) di = 0; + + // Age of last fix (secs) + age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); + } + + if ( cfg.modeA == 1 ) { + if ( showMax ) + drawFix({ + speed:max.spd, + sats:lf.satellites, + alt:max.alt, + alt_units:cfg.alt_unit, + age:age, + fix:lf.fix + }); // Speed and alt maximums + else + drawFix({ + speed:sp, + sats:lf.satellites, + alt:al, + alt_units:cfg.alt_unit, + age:age, + fix:lf.fix + }); // Show speed/altitude + } +} + +function setButtons(){ + setWatch(_=>load(), BTN1); + +onGPS(lf); +} + + +function updateClock() { + if (!canDraw) return; + drawTime(); + g.reset(); + + if ( emulator ) { + max.spd++; max.alt++; + d=new Date(); sec=d.getSeconds(); + onGPS(lf); + } +} + + +// =Main Prog + +// Read settings. +let cfg = require('Storage').readJSON('bikespeedo.json',1)||{}; + +cfg.spd = 1; // Multiplier for speed unit conversions. 0 = use the locale values for speed +cfg.spd_unit = 'km/h'; // Displayed speed unit +cfg.alt = 1; // Multiplier for altitude unit conversions. (feet:'0.3048') +cfg.alt_unit = 'm'; // Displayed altitude units ('feet') +cfg.dist = 1000; // Multiplier for distnce unit conversions. +cfg.dist_unit = 'km'; // Displayed distnce units +cfg.modeA = 1; +cfg.primSpd = 1; // 1 = Spd in primary, 0 = Spd in secondary + +cfg.altDiff = cfg.altDiff==undefined?100:cfg.altDiff; +cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt; +cfg.altFilt = cfg.altFilt==undefined?false:cfg.altFilt; +// console.log("cfg.altDiff: " + cfg.altDiff); +// console.log("cfg.spdFilt: " + cfg.spdFilt); +// console.log("cfg.altFilt: " + cfg.altFilt); + +if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 }); +if ( cfg.altFilt ) var altFilter = new KalmanFilter({R: 0.01, Q: 2 }); + +function onGPSraw(nmea) { + var nofGP = 0, nofBD = 0, nofGL = 0; + if (nmea.slice(3,6) == "GSV") { + // console.log(nmea.slice(1,3) + " " + nmea.slice(11,13)); + if (nmea.slice(0,7) == "$GPGSV,") nofGP = Number(nmea.slice(11,13)); + if (nmea.slice(0,7) == "$BDGSV,") nofBD = Number(nmea.slice(11,13)); + if (nmea.slice(0,7) == "$GLGSV,") nofGL = Number(nmea.slice(11,13)); + SATinView = nofGP + nofBD + nofGL; + } } +if(BANGLEJS2) Bangle.on('GPS-raw', onGPSraw); + +function onPressure(dat) { + altiBaro = Number(dat.altitude.toFixed(0)) + Number(cfg.altDiff); +} + +Bangle.setBarometerPower(1); // needs some time... +g.clearRect(0,screenYstart,screenW,screenH); +onGPS(lf); +Bangle.setGPSPower(1); +Bangle.on('GPS', onGPS); +Bangle.on('pressure', onPressure); + +Bangle.setCompassPower(1); +var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; +if (!CALIBDATA) calibrateCompass = true; +function Compass_tiltfixread(O,S){ + "ram"; + //console.log(O.x+" "+O.y+" "+O.z); + var m = Bangle.getCompass(); + var g = Bangle.getAccel(); + m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z; + var d = Math.atan2(-m.dx,m.dy)*180/Math.PI; + if (d<0) d+=360; + var phi = Math.atan(-g.x/-g.z); + var cosphi = Math.cos(phi), sinphi = Math.sin(phi); + var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi)); + var costheta = Math.cos(theta), sintheta = Math.sin(theta); + var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta; + var yh = m.dz*sinphi - m.dx*cosphi; + var psi = Math.atan2(yh,xh)*180/Math.PI; + if (psi<0) psi+=360; + return psi; +} +var Compass_heading = 0; +function Compass_newHeading(m,h){ + var s = Math.abs(m - h); + var delta = (m>h)?1:-1; + if (s>=180){s=360-s; delta = -delta;} + if (s<2) return h; + var hd = h + delta*(1 + Math.round(s/5)); + if (hd<0) hd+=360; + if (hd>360)hd-= 360; + return hd; +} +function Compass_reading() { + "ram"; + var d = Compass_tiltfixread(CALIBDATA.offset,CALIBDATA.scale); + Compass_heading = Compass_newHeading(d,Compass_heading); + hdngCompass = Compass_heading.toFixed(0); +} +if (!calibrateCompass) setInterval(Compass_reading,200); + +setButtons(); +if (emulator) setInterval(updateClock, 2000); +else setInterval(updateClock, 10000); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/bikespeedo/app.png b/apps/bikespeedo/app.png new file mode 100644 index 000000000..50f242b47 Binary files /dev/null and b/apps/bikespeedo/app.png differ diff --git a/apps/bikespeedo/metadata.json b/apps/bikespeedo/metadata.json new file mode 100644 index 000000000..c3de0487c --- /dev/null +++ b/apps/bikespeedo/metadata.json @@ -0,0 +1,20 @@ +{ + "id": "bikespeedo", + "name": "Bike Speedometer (beta)", + "shortName": "Bike Speedometer", + "version": "0.02", + "description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources", + "icon": "app.png", + "screenshots": [{"url":"Screenshot.png"}], + "type": "app", + "tags": "tool,cycling,bicycle,outdoors,sport", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"bikespeedo.app.js","url":"app.js"}, + {"name":"bikespeedo.img","url":"app-icon.js","evaluate":true}, + {"name":"bikespeedo.settings.js","url":"settings.js"} + ], + "data": [{"name":"bikespeedo.json"}] +} diff --git a/apps/bikespeedo/settings.js b/apps/bikespeedo/settings.js new file mode 100644 index 000000000..a3921f4a3 --- /dev/null +++ b/apps/bikespeedo/settings.js @@ -0,0 +1,48 @@ +(function(back) { + + let settings = require('Storage').readJSON('bikespeedo.json',1)||{}; + + function writeSettings() { + require('Storage').write('bikespeedo.json',settings); + } + + const appMenu = { + '': {'title': 'Bike Speedometer'}, + '< Back': back, + '< Load Bike Speedometer': ()=>{load('bikespeedo.app.js');}, + 'Barometer Altitude adjustment' : function() { E.showMenu(altdiffMenu); }, + 'Kalman Filters' : function() { E.showMenu(kalMenu); } + }; + + const altdiffMenu = { + '': { 'title': 'Altitude adjustment' }, + '< Back': function() { E.showMenu(appMenu); }, + 'Altitude delta': { + value: settings.altDiff || 100, + min: -200, + max: 200, + step: 10, + onchange: v => { + settings.altDiff = v; + writeSettings(); } + } + }; + + const kalMenu = { + '': {'title': 'Kalman Filters'}, + '< Back': function() { E.showMenu(appMenu); }, + 'Speed' : { + value : settings.spdFilt, + format : v => v?"On":"Off", + onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); } + }, + 'Altitude' : { + value : settings.altFilt, + format : v => v?"On":"Off", + onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); } + } + }; + + E.showMenu(appMenu); + +}); diff --git a/apps/bledetect/ChangeLog b/apps/bledetect/ChangeLog index e52015f04..e9b98e08c 100644 --- a/apps/bledetect/ChangeLog +++ b/apps/bledetect/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Fixed issue with wrong device informations 0.03: Ensure manufacturer:undefined doesn't overflow screen +0.04: Set Bangle.js 2 compatible, show widgets diff --git a/apps/bledetect/bledetect.js b/apps/bledetect/bledetect.js index ca8699f9a..f3fc70e92 100644 --- a/apps/bledetect/bledetect.js +++ b/apps/bledetect/bledetect.js @@ -5,6 +5,7 @@ let menu = { function showMainMenu() { menu["< Back"] = () => load(); + Bangle.drawWidgets(); return E.showMenu(menu); } @@ -55,5 +56,6 @@ function waitMessage() { E.showMessage("scanning"); } +Bangle.loadWidgets(); scan(); waitMessage(); diff --git a/apps/bledetect/metadata.json b/apps/bledetect/metadata.json index f5e0ffb19..0c30fe8f6 100644 --- a/apps/bledetect/metadata.json +++ b/apps/bledetect/metadata.json @@ -2,11 +2,11 @@ "id": "bledetect", "name": "BLE Detector", "shortName": "BLE Detector", - "version": "0.03", + "version": "0.04", "description": "Detect BLE devices and show some informations.", "icon": "bledetect.png", "tags": "app,bluetooth,tool", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"bledetect.app.js","url":"bledetect.js"}, diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index 4c3d3b930..e3f492d3b 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -46,3 +46,8 @@ 0.40: Bootloader now rebuilds for new firmware versions 0.41: Add Keyboard and Mouse Bluetooth HID option 0.42: Sort *.boot.js files lexically and by optional numeric priority, e.g. appname..boot.js +0.43: Fix Gadgetbridge handling with Programmable:off +0.44: Write .boot0 without ever having it all in RAM (fix Bangle.js 1 issues with BTHRM) +0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk) +0.46: Fix no clock found error on Bangle.js 2 +0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed) diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index 3cf885ac9..45e271f30 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -14,6 +14,6 @@ if (!clockApp) { if (clockApp) clockApp = require("Storage").read(clockApp.src); } -if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, BTN2, {repeat:false,edge:"falling"});`; +if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`; eval(clockApp); delete clockApp; diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index 63424bfbf..119cd2c2c 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -1,10 +1,10 @@ /* This rewrites boot0.js based on current settings. If settings changed then it recalculates, but this avoids us doing a whole bunch of reconfiguration most of the time. */ -E.showMessage("Updating boot0..."); +E.showMessage(/*LANG*/"Updating boot0..."); var s = require('Storage').readJSON('setting.json',1)||{}; var BANGLEJS2 = process.env.HWVERSION==2; // Is Bangle.js 2 -var boot = ""; +var boot = "", bootPost = ""; if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed var CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT); boot += `if (E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; @@ -15,6 +15,7 @@ if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't chang boot += ` { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`; boot += `E.setFlags({pretokenise:1});\n`; boot += `var bleServices = {}, bleServiceOptions = { uart : true};\n`; +bootPost += `NRF.setServices(bleServices, bleServiceOptions);delete bleServices,bleServiceOptions;\n`; // executed after other boot code if (s.ble!==false) { if (s.HID) { // Human interface device if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`; @@ -38,7 +39,7 @@ LoopbackA.setConsole(true);\n`; boot += ` Bluetooth.line=""; Bluetooth.on('data',function(d) { - var l = (Bluetooth.line + d).split("\n"); + var l = (Bluetooth.line + d).split(/[\\n\\r]/); Bluetooth.line = l.pop(); l.forEach(n=>Bluetooth.emit("line",n)); }); @@ -96,52 +97,20 @@ delete g.theme; // deleting stops us getting confused by our own decl. builtins if (!g.theme) { boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7,dark:true};\n`; } -delete Bangle.setUI; // deleting stops us getting confused by our own decl. builtins can't be deleted -if (!Bangle.setUI) { // assume this is just for F18 - Q3 should already have it - boot += `Bangle.setUI=function(mode, cb) { -if (Bangle.btnWatches) { - Bangle.btnWatches.forEach(clearWatch); - delete Bangle.btnWatches; -} -if (Bangle.swipeHandler) { - Bangle.removeListener("swipe", Bangle.swipeHandler); - delete Bangle.swipeHandler; -} -if (Bangle.touchHandler) { - Bangle.removeListener("touch", Bangle.touchHandler); - delete Bangle.touchHandler; -} -if (!mode) return; -else if (mode=="updown") { - Bangle.btnWatches = [ - setWatch(function() { cb(-1); }, BTN1, {repeat:1}), - setWatch(function() { cb(1); }, BTN3, {repeat:1}), - setWatch(function() { cb(); }, BTN2, {repeat:1}) - ]; -} else if (mode=="leftright") { - Bangle.btnWatches = [ - setWatch(function() { cb(-1); }, BTN1, {repeat:1}), - setWatch(function() { cb(1); }, BTN3, {repeat:1}), - setWatch(function() { cb(); }, BTN2, {repeat:1}) - ]; - Bangle.swipeHandler = d => {cb(d);}; - Bangle.on("swipe", Bangle.swipeHandler); - Bangle.touchHandler = d => {cb();}; - Bangle.on("touch", Bangle.touchHandler); -} else if (mode=="clock") { - Bangle.CLOCK=1; - Bangle.btnWatches = [ - setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"}) - ]; -} else if (mode=="clockupdown") { - Bangle.CLOCK=1; - Bangle.btnWatches = [ - setWatch(function() { cb(-1); }, BTN1, {repeat:1}), - setWatch(function() { cb(1); }, BTN3, {repeat:1}), - setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"}) - ]; -} else - throw new Error("Unknown UI mode"); +try { + Bangle.setUI({}); // In 2v12.xx we added the option for mode to be an object - for 2v12 and earlier, add a fix if it fails with an object supplied +} catch(e) { + boot += `Bangle._setUI = Bangle.setUI; +Bangle.setUI=function(mode, cb) { + if (Bangle.uiRemove) { + Bangle.uiRemove(); + delete Bangle.uiRemove; + } + if ("object"==typeof mode) { + // TODO: handle mode.back? + mode = mode.mode; + } + Bangle._setUI(mode, cb); };\n`; } delete E.showScroller; // deleting stops us getting confused by our own decl. builtins can't be deleted @@ -195,8 +164,8 @@ if (!Bangle.appRect) { // added in 2v11 - polyfill for older firmwares // Append *.boot.js files // These could change bleServices/bleServiceOptions if needed -var getPriority = /.*\.(\d+)\.boot\.js$/; -require('Storage').list(/\.boot\.js/).sort((a,b)=>{ +var bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{ + var getPriority = /.*\.(\d+)\.boot\.js$/; var aPriority = a.match(getPriority); var bPriority = b.match(getPriority); if (aPriority && bPriority){ @@ -206,19 +175,41 @@ require('Storage').list(/\.boot\.js/).sort((a,b)=>{ } else if (!aPriority && bPriority){ return 1; } - return a > b; -}).forEach(bootFile=>{ + return a==b ? 0 : (a>b ? 1 : -1); +}); +// precalculate file size +var fileSize = boot.length + bootPost.length; +bootFiles.forEach(bootFile=>{ + // match the size of data we're adding below in bootFiles.forEach + fileSize += 2+bootFile.length+1+require('Storage').read(bootFile).length+2; +}); +// write file in chunks (so as not to use up all RAM) +require('Storage').write('.boot0',boot,0,fileSize); +var fileOffset = boot.length; +bootFiles.forEach(bootFile=>{ // we add a semicolon so if the file is wrapped in (function(){ ... }() // with no semicolon we don't end up with (function(){ ... }()(function(){ ... }() // which would cause an error! - boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n"; + // we write: + // "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n"; + // but we need to do this without ever loading everything into RAM as some + // boot files seem to be getting pretty big now. + require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset); + fileOffset+=2+bootFile.length+1; + var bf = require('Storage').read(bootFile); + require('Storage').write('.boot0',bf,fileOffset); + fileOffset+=bf.length; + require('Storage').write('.boot0',";\n",fileOffset); + fileOffset+=2; }); -// update ble -boot += `NRF.setServices(bleServices, bleServiceOptions);delete bleServices,bleServiceOptions;\n`; -// write file -require('Storage').write('.boot0',boot); +require('Storage').write('.boot0',bootPost,fileOffset); + delete boot; -E.showMessage("Reloading..."); +delete bootPost; +delete bootFiles; +delete fileSize; +delete fileOffset; +E.showMessage(/*LANG*/"Reloading..."); eval(require('Storage').read('.boot0')); // .bootcde should be run automatically after if required, since // we normally get called automatically from '.boot0' diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index 4cbfd9c59..d1bf2edde 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.42", + "version": "0.47", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", diff --git a/apps/bordle/ChangeLog b/apps/bordle/ChangeLog new file mode 100644 index 000000000..ddbd6239c --- /dev/null +++ b/apps/bordle/ChangeLog @@ -0,0 +1,3 @@ +0.01: New App +0.02: app keeps track of statistics now +0.03: Fix bug in valid word detection diff --git a/apps/bordle/README.md b/apps/bordle/README.md new file mode 100644 index 000000000..f15f1e6fa --- /dev/null +++ b/apps/bordle/README.md @@ -0,0 +1,17 @@ +# Bordle + +The Bangle version of a popular word guessing game. The goal is to guess a 5 letter word in 6 tries or less. After each guess, the letters in the guess are +marked in colors: yellow for a letter that appears in the to-be-guessed word, but in a different location and green for a letter in the correct position. + +Only words contained in the internal dictionary are allowed as valid guesses. At app launch, a target word is picked from the dictionary at random. + +On startup, a grid of 6 lines with 5 (empty) letter boxes is displayed. Swiping left or right at any time switches between grid view and keyboard view. +The keyboad was inspired by the 'Scribble' app (it is a simplified version using the layout library). The letter group "Z ..." contains the delete key and +the enter key. Hitting enter after the 5th letter will add the guess to the grid view and color mark it. + +The (English language) dictionary was derived from the the Unix ispell word list by filtering out plurals and past particples (and some hand editing) from all 5 letter words. +It is contained in the file 'wordlencr.txt' which contains one long string (no newline characters) of all the words concatenated. It would not be too difficult to swap it +out for a different language version. The keyboard currently only supports the 26 characters of the latin alphabet (no accents or umlauts). + + + diff --git a/apps/bordle/app-icon.js b/apps/bordle/app-icon.js new file mode 100644 index 000000000..64ccbc8a5 --- /dev/null +++ b/apps/bordle/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AA/TADwoIFkYyOF0owIF04wGUSqvVBZQtZGJYJIFzomKF0onIF07EKF0owLF9wNEnwACE6oZILxovbMBov/F/4v/C54uWF/4vKBQQLLF/4YPFwYMLF7AZGF5Y5KF5xJIFwoMJD44vaBhwvcLQpgHF8gGRF6xYNBpQvTXBoNOF65QJBIgvjBywvUV5YOOF64OIB54v/cQwAKB5ov/F84wKADYuIF+AwkFIwwnE45hmExCSlEpTEiERr3KADw+PF0ownUSoseA==")) diff --git a/apps/bordle/app.png b/apps/bordle/app.png new file mode 100644 index 000000000..633a83e4e Binary files /dev/null and b/apps/bordle/app.png differ diff --git a/apps/bordle/bordle.app.js b/apps/bordle/bordle.app.js new file mode 100644 index 000000000..07e954a6d --- /dev/null +++ b/apps/bordle/bordle.app.js @@ -0,0 +1,196 @@ +var Layout = require("Layout"); + +var gameState = 0; +var keyState = 0; +var keyStateIdx = 0; + +function buttonPushed(b) { + if (keyState==0) { + keyState++; + keyStateIdx = b; + if (b<6) { + for (i=1; i<=5; ++i) { + var c = String.fromCharCode(i+64+(b-1)*5); + layout["bt"+i.toString()].label = c; + layout["bt"+i.toString()].bgCol = wordle.keyColors[c]||g.theme.bg; + } + layout.bt6.label = "<"; + } + else { + layout.bt1.label = "Z"; + layout.bt1.bgCol = wordle.keyColors.Z||g.theme.bg; + layout.bt2.label = ""; + layout.bt4.label = ""; + layout.bt3.label = " "; + layout.bt5.label = ""; + layout.bt6.label = "<"; + } + } + else { // actual button pushed + inp = layout.input.label; + if (b!=6) { + if ((keyStateIdx<=5 || b<=1) && inp.length<5) inp += String.fromCharCode(b+(keyStateIdx-1)*5+64); + else if (layout.input.label.length>0 && b==2) inp = inp.slice(0,-1); + if (keyStateIdx==6 && b==5) { + wordle.drawStats(); + return; + } + layout.input.label = inp; + } + layout = getKeyLayout(inp); + keyState = 0; + if (inp.length==5 && keyStateIdx==6 && b==4) { + rc = wordle.addGuess(inp); + layout.input.label = ""; + layout.update(); + gameState = 0; + if (rc>0) return; + g.clear(); + wordle.render(); + return; + } + } + layout.update(); + g.clear(); + layout.render(); +} + +function getKeyLayout(text) { + return new Layout( { + type: "v", c: [ + {type:"txt", font:"6x8:2", id:"input", label:text, pad: 3}, + {type: "h", c: [ + {type:"btn", font:"6x8:2", id:"bt1", label:"ABCDE", cb: l=>buttonPushed(1), pad:4, filly:1, fillx:1 }, + {type:"btn", font:"6x8:2", id:"bt2", label:"FGHIJ", cb: l=>buttonPushed(2), pad:4, filly:1, fillx:1 }, + ]}, + {type: "h", c: [ + {type:"btn", font:"6x8:2", id:"bt3", label:"KLMNO", cb: l=>buttonPushed(3), pad:4, filly:1, fillx:1 }, + {type:"btn", font:"6x8:2", id:"bt4", label:"PQRST", cb: l=>buttonPushed(4), pad:4, filly:1, fillx:1 }, + ]}, + {type: "h", c: [ + {type:"btn", font:"6x8:2", id:"bt5", label:"UVWXY", cb: l=>buttonPushed(5), pad:4, filly:1, fillx:1 }, + {type:"btn", font:"6x8:2", id:"bt6", label:"Z ...", cb: l=>buttonPushed(6), pad:4, filly:1, fillx:1 }, + ]} + ]}); +} + +class Wordle { + constructor(word) { + this.word = word; + this.guesses = []; + this.guessColors = []; + this.keyColors = []; + this.nGuesses = -1; + if (word == "rnd") { + this.words = require("Storage").read("wordlencr.txt"); + i = Math.floor(Math.floor(this.words.length/5)*Math.random())*5; + this.word = this.words.slice(i, i+5).toUpperCase(); + } + console.log(this.word); + this.stats = require("Storage").readJSON("bordlestats.json") || {'1':0, '2':0, '3':0, '4':0, '5':0, '6':0, 'p':0, 'w':0, 's':0, 'ms':0}; + } + render(clear) { + h = g.getHeight(); + bh = Math.floor(h/6); + bbh = Math.floor(0.85*bh); + w = g.getWidth(); + bw = Math.floor(w/5); + bbw = Math.floor(0.85*bw); + if (clear) g.clear(); + g.setFont("Vector", Math.floor(bbh*0.95)).setFontAlign(0,0); + g.setColor(g.theme.fg); + for (i=0; i<6; ++i) { + for (j=0; j<5; ++j) { + if (i<=this.nGuesses) { + g.setColor(this.guessColors[i][j]).fillRect(j*bw+(bw-bbw)/2, i*bh+(bh-bbh)/2, (j+1)*bw-(bw-bbw)/2, (i+1)*bh-(bh-bbh)/2); + g.setColor(g.theme.fg).drawString(this.guesses[i][j], 2+j*bw+bw/2, 2+i*bh+bh/2); + } + g.setColor(g.theme.fg).drawRect(j*bw+(bw-bbw)/2, i*bh+(bh-bbh)/2, (j+1)*bw-(bw-bbw)/2, (i+1)*bh-(bh-bbh)/2); + } + } + } + addGuess(w) { + let idx = -1; + do{ + idx = this.words.indexOf(w.toLowerCase(), idx+1); + } + while(idx !== -1 && idx%5 !== 0); + if(idx%5 !== 0) { + E.showAlert(w+"\nis not a word", "Invalid word").then(function() { + layout = getKeyLayout(""); + wordle.render(true); + }); + return 1; + } + this.guesses.push(w); + this.nGuesses++; + this.guessColors.push([]); + correct = 0; + var sol = this.word; + for (i=0; iwordle.stats['ms']) wordle.stats['ms'] = wordle.stats['s']; + require("Storage").writeJSON("bordlestats.json", wordle.stats); + wordle.drawStats(); + }); + return 2; + } + if (this.nGuesses==5) { + E.showAlert("The word was\n"+this.word, "You lost!").then(function(){ + wordle.stats['p']++; wordle.stats['s'] = 0; + require("Storage").writeJSON("bordlestats.json", wordle.stats); + wordle.drawStats(); + }); + return 3; + } + } + drawStats() { + E.showMessage(" ", "Statistics"); + var max = 1; + for (i=1; i<=6; ++i) if (max20 ? 25 : 25+tw, 52+(i-0.5)*(h-52)/6); + } + g.setFontVector((h-40)/9).setColor("#fff").drawString("P:"+this.stats["p"]+" W:"+this.stats["w"]+" S:"+this.stats["s"]+" M:"+this.stats["ms"], 4, 34); + Bangle.setUI(); + Bangle.on("touch", (e) => { load(); }); + } +} + +wordle = new Wordle("rnd"); +layout = getKeyLayout(""); +wordle.render(true); + +Bangle.on('swipe', function (dir) { + if (dir==1 || dir==-1) { + g.clear(); + if (gameState==0) { + layout.render(); + gameState = 1; + } + else if (gameState==1) { + wordle.render(); + gameState = 0; + } + } +}); diff --git a/apps/bordle/metadata.json b/apps/bordle/metadata.json new file mode 100644 index 000000000..f6011f798 --- /dev/null +++ b/apps/bordle/metadata.json @@ -0,0 +1,15 @@ +{ "id": "bordle", + "name": "Bordle", + "shortName":"Bordle", + "icon": "app.png", + "version":"0.03", + "description": "Bangle version of a popular word search game", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "tags": "game, text", + "storage": [ + {"name":"bordle.app.js","url":"bordle.app.js"}, + {"name":"wordlencr.txt","url":"wordlencr.txt"}, + {"name":"bordle.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/bordle/wordlencr.txt b/apps/bordle/wordlencr.txt new file mode 100644 index 000000000..e9ca9c304 --- /dev/null +++ b/apps/bordle/wordlencr.txt @@ -0,0 +1 @@ +abackabaftabaseabashabateabbeyabbotabeamabhorabideablerabodeabortaboutaboveabuseabuzzabyssachooacingacornacridactoracuteadageadaptadderaddleadeptadieuadmanadmenadmitadobeadoptadoreadornadultaerieaffixafireafootafoulafteragainagapeagateagaveagentagileagingagismaglowagonyagreeaheadaislealarmalbumalderalertalgaealiasalibialienalignalikealiveallayalleyallotallowalloyaloftalohaalonealongaloofaloudalphaaltaralteramassamazeamberambleamebaamendamigoaminoamissamityamongamourampleamplyampulamuckamuseangelangerangleangryangstanimeanionankleannexannoyannulanodeanticanvilaortaapaceapartappalappleapplyapronapteraptlyarborardorarenaargonargotarguearisearmoraromaarosearrayarrowarsonartsyascotashenasideaskewaspenaspicassayassetasterastiratlasatollatoneatriaattaratticaudioauditaugeraughtaugurauraeauralavailavastavertavianavoidawaitawakeawardawareawashawfulawingawokeaxialaxingaxiomazurebabelbaconbadgebadlybagelbaggybaizebakerbalkybalmybalsabanalbandybanjobarerbargebaronbasalbaserbasicbasilbasinbasisbastebatchbathebatonbattybawdybayoubeachbeadybeardbeastbeauxbebopbeechbeefybefitbegetbeginbegunbeigebeingbelaybelchbeliebellebellybelowbenchberetberryberthberylbesetbesombesotbevelbiblebicepbidetbightbigotbikerbilgebillybimbobingebingobipedbirchbirthbisonbitchblackbladeblameblandblankblareblastblazebleakbleatbleedbleepblendblentblestblimpblindblingblinkblitzbloatblockblondbloodbloomblownbluerbluesbluffbluntblurbblurtblushboardboastbobbybogeyboggybogiebonerboneybongobonnybonusboobyboostboothbootyboozeboozyboraxborerborneboronbosombossybosunbotchboughboundbowelbowerboxerbracebraidbrainbrakebrandbrashbrassbravebravobrawlbrawnbreadbreakbreedbriarbribebrickbridebriefbrierbrinebringbrinkbrinybriskbroadbroilbrokebroodbrookbroombrothbrownbruntbrushbruskbrutebuddybudgebuggybuglebuildbuiltbulgebulgybulkybullybumpybunchbunnyburkaburlyburntburroburstbusbybushybutchbuttebuxombuyerbylawbywaycabalcabbycabincablecacaocachecaddycadetcadgecadrecageycairncalifcalvecalyxcamelcameocampycanalcandycannycanoecanoncantocapercaponcaratcaretcargocarolcarrycarvecastecatchcatercattycaulkcauseceasecedarcellicellochafechaffchainchairchalkchampchantchaptcharmchartcharychasechasmcheapcheatcheckcheekcheepcheerchestchewychickchidechiefchildchilechilichillchimechimpchinachirpchivechockchoirchokechompchordchorechosechuckchumpchunkchurlchurnchutecidercigarciliacinchcircacivetciviccivilclackclaimclampclangclankclashclaspclasscleanclearcleatcleftclerkclickcliffclimbclimeclingclinkcliptcloakclockclompclonecloseclothcloudcloutcloveclowncluckclumpclungclunkcoachcoastcobracoccicockycocoacodexcoliccoloncolorcombocomercometcomfycomiccommacondocongaconiccookycoralcornycorpscouchcoughcouldcountcoupecourtcovencovercovetcoveycowercoyercoylycozencrackcraftcrampcranecrankcrashcratecravecrawlcrazecrazycreakcreamcredocreekcreelcreepcrepecreptcrestcrickcriercrimecrimpcrispcroakcrocicrockcronecronycrookcrooncroupcrowdcrowncrudecruelcruetcrumbcrushcrustcryptcubiccubitcurercuriecuriocurlycurrycursecurstcurvecurvycushycutercutupcyclecynicdaddydaffydailydairydaisydallydancedandydatumdauntdavitdealtdeathdebitdebugdebutdecafdecaldecaydecordecoydecrydeferdeicedeifydeigndeismdeitydelaydeltadelvedemondemurdenimdensedepotdepthderbydeterdetoxdeucedevildhotidiarydiceydigitdillydimerdimlydinerdingodingydinkydiodedirerdirgedirtydiscoditchdittodittydivandiverdivotdivvydizzydjinndodgedoggydogiedogmadoilydoingdollydonordonutdopeydorkydottydoubtdoughdousedowdydoweldownydowrydowsedoyendozendraftdraindrakedramadrankdrapedrawldrawndreaddreamdrierdriftdrilldrilydrinkdrivedrolldronedrooldroopdrovedrowndruiddrunkdryaddryerdrylyduchydullydummydumpyduncedunnoduskydustyduvetdwarfdweebdwelldweltdyingeagereagleearlyeartheaseleateneaterebonyedemaedictedifyeerieegreteidereightejectekingelateelbowelderelectelegyelideeliteelopeeludeemailembedemberemceeemendemeryemojiemoteemptyenactendowendueenemaenemyenjoyennuienrolensueenterentryenureenvoyepochepoxyequalequiperaseerecterodeerroreruptessayesteretherethicevadeeventeveryevictevokeexactexaltexcelexertexileexistexpelextolextraexudeexulteyingeyriefablefacetfagotfaintfairyfaithfakerfakirfalsefancyfannyfarcefatalfattyfaultfaunafavorfeastfecalfeignfeintfelonfemurfenceferalferryfetalfetchfetidfeverfewerfiberfichefieldfiendfieryfifthfiftyfightfilchfiletfillyfilmyfilthfinalfinchfinerfinnyfiordfirstfirthfishyfitlyfiverfixerfizzyfjordflackflailflairflakeflakyflameflankflareflashflaskfleetfleshflickflierflingflintflirtfloatflockfloodfloorfloraflourflownflufffluidflukeflukyflumeflungflunkflushfluteflybyflyerfoamyfocalfocusfoggyfoistfoliofollyfonduforayforceforgeforgoforteforthfortyforumfoundfountfoyerfrackfrailframefrancfrankfraudfreakfreerfreshfriarfrierfrillfriskfrizzfrockfrondfrontfrostfrothfrownfrozefruitfrumpfryerfudgefuguefullyfungifunkyfunnyfurorfurryfurzefussyfustyfutonfuzzygabbygablegaffegailygamergameygamingammagamutgassygaudygaugegauntgauzegauzygavelgawkygayergaylygazergeckogeekygeesegelidgeniegeniigenregenusgeodegetupghostghoulgiantgiddygimmegimpygipsygirthgismogivengizmogladeglandglareglazegleamgleanglideglintgloatglobegloomgloryglossgloveglueyglyphgnarlgnashgnawngnomegoinggollygonadgonergonnagoodygooeygoofygoosegorgegorsegottagougegourdgoutygracegradegraftgrailgraingrandgrantgrapegraphgraspgrategravegravygrazegreatgrebegreedgreengreetgriefgrillgrimegrimygrindgripegristgroangroingroomgropegrossgroupgroutgrovegrowlgrowngruelgruffgruntguanoguardguavaguessguestguideguildguileguiltguisegulaggulchgullygumbogummygunnyguppygushygustogustygutsygypsyhabithaikuhairyhalerhalonhalvehandyhankyhappyhardyharpyharryharshhastehastyhatchhaterhaunthavenhavochazelheadyheardheartheathheaveheavyhedgeheftyheisthelixhellohelothencehennaheronhertzhikerhillyhingehippohippyhitchhoagyhoardhoaryhobbyhoganhoisthokeyhokumhollyhomerhomeyhomiehoneyhonorhoochhooeyhookyhordehornyhorsehorsyhotelhotlyhoundhousehovelhoverhowdyhubbyhuffyhugerhumanhumidhumorhunchhurryhuskyhussyhutchhydrahyenahyinghymenhypericiericilyicingidealidiomidiotidleridylliglooimageimbueimpelimplyinaneinaptinboxincurindexindueineptinertinferinfixingotinlayinletinnerinputinsetinterinureirateironyisletissueitchyivoryjabotjapanjauntjazzyjehadjellojellyjerkyjettyjeweljiffyjihadjimmyjinnijointjoistjokerjollyjoulejoustjudgejuicejuicyjulepjumbojumpyjuncojunkyjuntajurorkabobkapokkaputkaratkarmakayakkazookebabkebobketchkhakikickykiddokiddykindakinkykioskkittyklutzknackknavekneadkneelknellkneltknifeknockknollknownkoalakookykopekkronekudzulabellabialaborladenladlelagerlaitylamerlancelankylapellapselarchlargelargolarvalaserlassolatchlaterlatexlathelattelaughlaxerlaxlylayerleachleafyleakyleaptlearnleaseleashleastleaveledgeleechleeryleftylegalleggylegitlemmalemmelemonlemurleperletupleveelevelleverlibellicitliegeliferlightlikenlikerlilaclimbolimitlinenlinerlingolipidlisleliterlithelivenliverlividllamallanoloamyloathlobbylocallodgeloftylogicloginlogonlonerloonyloopylooselorryloserlottolotuslouselousyloverlowerlowlyloyallucidluckylucrelumpylunarlunchlungelupinlurchluridlustylyinglymphlynchlyricmacawmachomacromadammadlymagicmagmamaizemajormakermambomammamangamangemangomangymaniamanicmanlymannamanormansemaplemarchmariamarrymarshmasonmatchmattematzomauvemavenmavinmaximmaybemayormealymeantmeatymeccamedalmediamedicmelonmercymergemeritmerrymessymetalmetermetromiddymidgemidstmightmilchmilermilkymimicminceminerminimminormintymirthmisdomisermistymitermixermochamodalmodelmodemmogulmoiremoistmolarmoldymommamommymoneymonthmoochmoodymoosemoralmoraymoronmoseymossymotelmotifmotormottomoundmountmournmousemousymouthmovermoviemowermuckymuddymuftimuggymulchmultimummymunchmuralmurkymushymusicmuskymussymustymutermynahmyrrhnabobnachonacrenadirnaiadnaivenakednannynappynasalnastynatalnattynavalnavelneathneedyneighnerdynervenervynevernewelnewernewlynexusnicernichenieceniftynigganightnimbininjaninnyninthnippyniternoblenoisenoisynomadnoncenoosenorthnoseynotchnovelnowaynudernudgenursenuttynylonnymphoakenoasisoatenobeseoccuroceanocherochreoctaloctetoddlyodiumoffalofferoftenoldenolderoldieoliveomegaoniononsetoperaopineopiumopticorateorbitorderorganotherotteroughtounceoutdoouteroutgoovaryovertovoidovuleowingowletowneroxbowoxideozonepaddypadrepaeanpaganpagerpaintpalerpalmypalsypandapanelpanicpansypantypapalpapawpaperparchparkaparryparsepartypashapastapastepastypatchpatiopatsypattypausepayeepayerpeacepeachpearlpeasepecanpedalpeevepenalpencepenispennypeonypeppyperchperilperkypeskypetalpeterpettyphasephialphishphloxphonephonyphotophylapianopickypiecepietypiggypikerpilafpilotpinchpinkypintopinuppiouspiperpipitpiquepitchpithypitonpivotpixelpixiepizzaplaceplaidplainplaitplaneplankplantplateplazapleadpleatpluckplumbplumeplumpplunkplushpoachpointpoisepokerpokeypolarpoliopolkapolyppoochpoppapoppyporchposerpositpossepottypouchpoundpowerprankprateprawnpreenpriceprickpricyprideprimeprimpprintpriorprismprivyprizeprobepromoproneprongproofproseprosyproudproveprowlproxyprudeprunepsalmpshawpsychpubicpudgypuffypulpypulsepunchpupaepupalpupilpuppypureepurerpurgepursepushypussyputtypylonquackquaffquailquakequalmquarkquartquashquasiqueenqueerquellqueryquestqueuequickquietquillquiltquirequirkquitequoitquotaquotequothrabidracerradarradiiradioradonrainyraiserallyranchrandyrangerangyrapidrarerraspyratiorattyravelravenrawerrayonrazorreachreactreadyrealmrearmrebelrebutrecaprectarecurredidreedyreevereferrefitregalrehabreignrelaxrelayrelicremitrenalrenewreorgrepayrepelreplyreranrerunresetresinretchretryreuserevelrevuerheumrhinorhymeriderridgeriferriflerightrigidrigorrinseripenriperrisenriserriskyritzyrivalrivenriverrivetroachroastrobinrobotrockyrodeorogerrogueromanroomyroostrosinrotorrougeroughroundrouserouteroverrowdyrowerroyalrubleruddyruderrugbyruingrulerrumbarummyrumorrunnyrupeeruralrustysablesabresadlysafersaintsaladsallysalonsalsasaltysalvesalvosambasandysanersappysareesassysataysatinsatyrsaucesaucysaunasaversavorsavvyscaldscalescalpscalyscampscantscarescarfscaryscenescentscoldsconescoopscootscopescorescornscourscoutscowlscrapscrewscrubscubascuffscullsedansedgeseedysegueseizesemensennasensesepiaserveservosetupsevenseversewershackshadeshadyshaftshakeshakyshaleshallshaltshameshankshapeshardsharesharksharpshaveshawlsheafshearsheensheepsheersheetsheikshelfshellsherdshiftshillshineshinyshireshirkshirrshirtshoalshockshookshootshoreshortshoutshoveshownshowyshredshrewshrubshrugshuckshuntsibylsidlesiegesievesightsigmasilkysillysincesinewsingesirensirupsisalsissysitarsixthsixtysizerskateskeetskeinskierskiffskillskimpskirtskulkskullskunkslackslainslangslantslashslateslavesleeksleepsleetsleptsliceslickslideslilyslimeslimyslingslinksloopslopesloshslothslumpslungslunkslurpslushsmacksmallsmartsmashsmearsmellsmeltsmilesmirksmitesmithsmocksmokesmokysnacksnailsnakesnakysnaresnarlsneaksneersnidesniffsnipesnoopsnootsnoresnortsnoutsnowysnucksnuffsoapysobersoftysoggysolarsolidsolvesonarsonicsonnysoothsootysoppysorersorrysortasoughsoundsoupysousesouthsowerspacespacyspadespakespanksparesparkspasmspatespawnspeakspearspeckspeedspellspeltspendspentspicespicyspielspikespikyspillspiltspinespinyspirespitesplatsplaysplitspoilspokespoofspookspoolspoonspoorsporesportspoutsprayspreesprigspumesquatsquawsquidstackstaffstagestaidstainstairstakestalestalkstallstampstandstankstaphstarestarkstartstashstatestavesteadsteakstealsteamsteedsteelsteepsteerstentsternstickstiffstilestillstiltstingstinkstintstoatstockstoicstokestolestompstonestonystoodstoolstoopstorestorkstormstorystoutstovestrapstrawstraystrepstrewstripstropstrumstrutstuckstudystuffstumpstungstuntstylesuavesudsysuedesugarsuingsuitesulkysullysunnysupersurersurgesurlysushiswainswamiswampswankswardswarmswashswathswearsweatsweepsweetswellsweptswiftswillswineswingswipeswirlswishswoonswoopswordsworeswornswungsylphsynchsynodsyruptabbytabletabootacittackytaffytainttakentakertallytalontamertangotangytapertapirtardytarottarrytasertastetastytattytaunttaupeteachtearyteaseteenyteethtelextempotempttenettenontenortensetenthtepidtersetestythankthefttheirthemetherethesethetathickthiefthighthinethingthinkthirdthongthornthosethreethrewthrobthroethrowthrumthumbthumpthymetiaratibiatidaltigertighttildetimertimestimidtingetinnytipsytitantithetitletizzytoadytoasttodaytoddytokentonaltonertonictonnetoothtopaztopictoquetorchtorsitorsotortetotaltotemtouchtoughtoweltowertoxictoxintracetracktracttradetrailtraintraittramptrashtrawltreadtreattrendtriadtrialtribetricetricktriketrilltripetritetrolltromptrooptropetrouttrucetrucktrulytrunktrusttruthtrysttubbytubertuliptulletumidtummytumortunertunictunnytutortweaktweettwerktwerptwicetwilltwinetwirltwisttyingudderulcerulnaeultraumbelumberumiakuncleuncutunderundidundueunfitunifyunionuniteunityunmanunpinunsayunsetuntieuntilunzipupendupperupseturbanurineusageusherusingusualusurputteruvulavacuavaguevaletvalidvalorvaluevalvevapidvaporvaultvauntveganveldtvenomvenuevergeversevervevicarvideovigilvigorvillavinylviolaviperviralvirusvisitvisorvistavitalvividvixenvizorvocalvodkavoguevoicevomitvotervouchvowelvulvavyingwackowackywaderwaferwagerwageswagonwaistwaivewakenwaltzwanlywannawartywastewatchwaterwaverwaxenwearyweavewedgeweedyweepyweighweirdwelchwenchwhackwhalewharfwhealwheatwheelwherewhichwhiffwhilewhinewhinywhirlwhiskwhistwhitewhizzwholewhoopwhorewhosewidenwiderwidowwidthwieldwightwimpywincewinchwindywiperwiserwispywitchwittywokenwomanwomenwoodywooerwoolywoozywordyworldwormyworryworseworstworthwouldwoundwovenwrackwrathwreakwreckwrestwrierwringwristwritewrongwrotewrothxenonxylemyachtyahooyearnyeastyieldyodelyokelyoungyouthyuccayuckyyummyyuppyzebrazilchzippyzonal \ No newline at end of file diff --git a/apps/bradbury/app-icon.js b/apps/bradbury/app-icon.js new file mode 100644 index 000000000..07c4f5582 --- /dev/null +++ b/apps/bradbury/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcCkGSpEgwQCChICFkgCBgkQoMEyFJAoICByVBkgLBkkSpIaDEwWShEkFgcIBAIdCEYQCBAoQdBAoYsBC4Q7BpICBEYQCDF4Q7CEYYCCEYUSKYYUDyRlCJQQIBNYYvBMoQCBkgjBFgxxCL4REDFgaPEHYgmCIgosCNYZEEDoZ0CNwY7CIIYgDEYtB9+e/dg/4AB2EJkYEB/mC/fn33Ivvz598v4MB/0BgoRCyVHvmW7Mg2EA8uD/EAh/IkGP/8AgVLtkA5El+FJvoRBgmf4Mkh0HkEQo9kyEfkeQofsgf4kmPCIP+h/gwULkkCncEu/ZsmRI4cEv0H8ESpdgEwMjwXI9kTCIOANYkSEYOCncF+UAjuR/ED+FBg/3/f8RgNgiVPkYdBtkT/Egv0Il+AoMfI4PgyX7vkW799F4Nl//4//woH/+0Ztvx7Fs335sk//5EB/IRBhACB77CBpEkgEIgGQoDRBgEggVBgDdBgGAgPv317ku+5cj334t+OSoI+B8gCBtlx7dkuFfgvx4N8yPbvgOB8ACBR4MA9mf4Egz3IgeChEDwDOBx/AjuCoN8y/JgkX4ME2FBjuQn65BgMtwELkGOEYOO4Mh2EJh+Sh/jOIMd+3fskRcwMTEwOWo98gCSBwFJkm2pfgx3II4PBk++/aABhEfwEInpZBvkX7MkJQMl2FHfANBjgCBlmQhHsgwjB33IkeyBAOChMcEwM9+/ZsBHBboMJtv2hd9+FHZANBVoM7kGC/fv2FJ9+GEYOAh//+UIaIMBkkQpEAHwIIBoMgiFJBANJEAMIkGShEkwQIChIIBhIIBhIaCkmQpIFCgmSEwYpDEYwCCpAICBwUEiQdFEwIICyAIDHwQ7CEYYpCEYWSpA7FDocSEwojBCgIaDIgYCBNwR0BNYYjFEwZTDLgQjGOgYvBEYQ7ENYlJFgQCCDohuGTYpBFkhoCSoQICEYIA=")) diff --git a/apps/bradbury/app.js b/apps/bradbury/app.js new file mode 100644 index 000000000..147242689 --- /dev/null +++ b/apps/bradbury/app.js @@ -0,0 +1,115 @@ +require("Font7x11Numeric7Seg").add(Graphics); +require("Font5x9Numeric7Seg").add(Graphics); +require("Font8x12").add(Graphics); +require("FontDylex7x13").add(Graphics); +const X = 98, Y = 46; +var wizible = 0; + +function getImg() { + return require("heatshrink").decompress(atob("2GwwcCAoNBgmQpMkiACCoMkyALBAoMEyQCDkkSAoICCCIIXCCgQaCAQNJDQYUBDQIIBDQkgIwsShEkwUJkGSpACBBAQFCAQOCBAgFCyQXBDQQIDCIUIEYgOCpICBFgYXCII2W7ft237AQPbt++7fvBAIFBAQgRCAoNtCgIaDC4QaD7dtBAgUBDQYXC9+z5cEIIv279t+/fvoFBvoFC+/bBAe3CIQIBBwW2CIgCCCIYgFAQgjEAoN5sGAIAcF33JaIT4DAoTyDcYL1CAQgXBpIUDfYQCCC4T+CDoYgDDQQFBocMyBBDkeyagb4EBArpCpD1DfAoIDfAQLCDQwIGDQklwRBDLIJfEiffjv//H/AAPkgUB//+oEk+Pf/+B5/8+VHn/wh9/+ff/vx4f/+0f/+yFIIsCPoSMCWYSVDoBBCL48A8kQvEn+VA/i2Bn/yoMgh0BkvwhMFx0Bg6nBhkQh8Ej14h4XB9kSU4iSFQwMkGoWWhCDDagRQCiUPgED5Ej+EI/kAhF8+0BkH+LIOyCIOT4EAF4M8gVfgGG5EhRgNggFJGoIpBJQKJDAQQLBRgSDEKYRZCoNnOIXypaJBgPkjzFBn1AkeQv8/FgMcwFBnkArNsv/Bkl+v4vBVQQCBPQKzBRgoCCjEgIILOCKwQFBo8gwf4kf//50Bp/kz/IgEyhEj+UPskPPQOAoE8yE7GQInBx/4R4R6DfwQCGBYQCBIITOCagJNBo/gyEZg/wAoMCv5GB/MnR4KDBvEEi0IhkCQYMQp8Aj0BFgPBgECOgT+BQwJ9EAoQCEwEAJobOBQwM/kFx5E/+UH8mCv8gyf5kHygHlwVLklz5EjSQM8wP/BwPJkGX7IsBVQL7CO4SJFAoK5BBAUAI4SDDwfP9+eoH//0P/+f//gj//8OOvcsgV5/6JBgm+nNkgPH8kev8ETQMANALOBO4QFBPoa2CR4qDByBKCagTPBwAOBgEIVQJTBTAIIDDQIRBgMggQIBiQaGfAwCLRgWQoDUEpCeDgCSDAQLyCoEAAQIdCHIIjEAoS/BDQbsCyQCNX4dIJQaGCDRwCnRIbUDyTRBIOy8BQYTLDBYL7BAGUEIgI+CQYeCpMgIGYABiSDByUIkkSI4JKBgEB/4AW4/j+PHILECXgKDDpCDCgF+QehBBgDCBkkQI4VIgeAY2q/ByCDBySDCIPKDCgkQoKDCjg4tgcMmHDgACCQYuChMkQYJBujFhwwCBwQICpEEySDDAoKD1oaDDiSDDAQNIDI1z588+YCiQYoCBkCDCwSDCI4KDHgeevPnAUeAQYkQgaDDyFBkjIBkiDHjiAjAQXwQYwxBHAI+CiFBZYKDGg+f/4Ak+CDEAQSDCHwMgZAKDKIMyDEwCDDgg+BIgUkQYJBFQd0DQY9IAQSD1sCDDAQKDCySDvgKDEAQKDCySDChKDygKDCAQMgQYcIgkSI4MSQd8DQYkDQYcSYQJEBAQKDwkCDHgA+CiDLCQeCADmFDQYsgQAICCQd8hw0AsOCgFgQZESQeEMQZbCBQeR6BjFhw0YQYlIgmQoKDFgOwQdnBQYPDQYY+BkmChICBIIcP+yDqQAQCBgEgQYMEYQKDDAoKDxQAKDFiFBkCDBAQJBDAASDpgOCQwdgQYMAwUIkhEBkkSIIccuHAQd1DQY5EBQYnjx04QdMAgUAsOCgCDDyUIIgUEQYtxQdSABAQiDGhICBQeEhw0YsOAhCDCgmSAQOQoMkQeMMmHDQYICBQYQ+BQZUYQdAuCAAo4BHYMkZAKDGmKDriCDHiQ+ByUIQYvggU4QdEYsOGAQdgQYhEBI4SDEgKDrQASDFYQWCQY8AQejCBkmQoMEySD8kBEBQY0GjCD4kkSIIcEuKD0ySDJ8OOQdkIQYw7BZAUEQYkcgKDsoaDGYQKABQY3jwSDqgEGQwOAQYcEQYTIBAQKDyoKDGYQKABpKDF8EOhCDpsOGgACCQYkIkkQpMkiSDwgiACQYuQoMkyUIQY0AjCDnwCDCAQOAhCDCgCDEgiDFuPAgaDl/kAgcMmFDQY0QoKABhKDFsOOAgQAmQYsAQYUEyQCBIgKDFAFcDQAKDC4CDDyCDCpKDFaAIHBAT+Dx048YCDhCDBQAICCQYQ7BQYaJBIAIHDAQM/AokEj3x/mf/IIDz3JgEQv8/+VxtmX+MEj/BnkAuPAglx4cMQYYxBmAFBQYQkBkmSLoSDIn+DxBrDHwP48R0E+0OAoP6k+D/N/33YlAUCQYMIgEOgHgh0YsEGjCGBAoMArA2BQYMSI4KDJnnz4IIDjlx4mwqIID//P4EQp8Hifx4/y56DB6N8QYQaBAQNwQYUBAQPDQY8JkiDKwSDEyV5tGX5AIEh9gwX+QYPJ8+f/CYB5MgQYM48ICB8eOgEBw0AQYMIQYR9BhBBBQZYCRgAOLQYPHQYICBQYMMmFDQY0SoJpBpACCQYQAsQAMYsACCQYMAQYWQI4VJIN4AGgQ7ByCDFIPHIgA+BgmSoMkiFJkBB1iVAgkQQYTIByVJkmAIGcEy1IHYKDBIgKGDIgQCxkuSQYMSQYOShCGBJQQ1m5cs2QCKwUBkjCCiFJQwYC1gBBBAoJWBQYQCBf9gAIgVIYQRECJoKeBJQQFCyALBAQMkiSVBboICDNAgUDDQQOCBAQXFyQRDBAICBDQdBQAMJZYICCQwOSpCPEpICBLIIFBCIIFCDRwCFDQp6BCI5HEOgiJDBAJ0ETAaPFCIgaGSo4IDGpKDCOIZTDBAJWCOgRxCboQCBCgQCCBwICDOgqPCBAqeDCgoODdhDdBBYqPIBwSPDDQgCDfAQCCSQgyDEASJDQYLODOgZfBOIRWFL4TjERIYdDTAYOEBAICEC4o4FBwLaGAXNAgBuFAXMAAH4A/AAcB23btoC84ENIP/Yj/+7Ml/4AG9u27//+3/CgIJB/3bt4EBEAe3DAn2BAO/DoQJCGof/HYgXD/3JlpBDpdsz5CHAF/yrdk//4hvy/9kwf7v5lCTA9vQAJxB/YLFPoRxETYTCSt+WTAOeQYOX7cki1/QWv833bsmRQYOW7Mg31f9rvB/pWCCoR6HNBF9NYQXGRJAOCCQXbtm+5MkgX4jgFBgvy5//wEAAB8PQcPl2VIgG2vEM21Il+W5/8ICAABNYKYEcIf+SoKABBYI4Gtu/CgaMCsmSgNv+VYjmyoN83xBTgKDh8mArf8y14huShfl21bthBS/ZlCt59Bt7vBtowFBYIRCtoFBv4FBBYSGC9kW/8n2XYj+Qr8t+1/Qev83/Jtuz/EN+X5v+z/d8IKqGCAQO///9PQW274FE//tAoYCEDoNvyV/vueQYP+pdsz5NBQen/+Vbsn/QYO27MlcAWAIKEGdgP2dgSGCBAIABPQoOBAoO3SoftAQKbE5Mt2yDBNUQAc/BBBMoXfBALXCAQLyF27sIEIYRCAgJ3CEwQLDv6wBRIIADEAYFDQf6DChpuGAQ++BZP/C5VvOggFCCgV/Rgi5GQf6DDIIP7gAAUgz+C//t2APIn/tO4WABxEbPoKPDtu/IIX4IKsBMImDNQ/+o4EC/ixI/0HQZENZAJBVgBfBcwNsgVbfwQCD2VAAoVgiQLEBwcAAoX9BYfYQbv8CBQOC8AONQYxBXQYRiBthHB8FwBQNwg4CBgF/IJtvBwPtQccB4EcIIcA8eAQbEf+3YILXsIIkDx0AjgQBOIVgD5R9B//9AQKDE/5BVh6DFYoZBFQa4oCd4TjCKAPf9u3AoTaC74ZD+3bYorCCMoKJBGQP9DoJBLGQQjCtrFCJY4AUIId//EcZYSDZhrLDOgP+PQSJDBAtt34IBAoX/7dsGRZxBsAOKEwaVBAoPYQbx0NQakfZYRNBt4LDAon+R4qDDBwPbvkSpMkyQCEOgSYBIJanDHYZBBA4IWKABUPQYk/RhKDYAQJBVgHbv6DBtiDHkB0EsCDLUgNtH4IFB7BBYgJ6GOhaDW7BBXMoVsGRe275BLQYgCBQf6DDh6DXgHf/qDNtu3IJiDCt/27f//yD/QYUNZAKDW7d9MoJBLQaP/2xBDQf5BCZAJBW/Z0C9gQK/p0BsAOKt49C+3bRIPYQf4+BhpEBIKoiBOgVsB5ZxBQZYdCUgTFDQf/4jqDYMQP+QZjyBQZlt34+C/aGBQYX/ICsPQakJkmSpICEoCDIhrOCJQQFB/4ICAQ9/DAIFFIKATCAAnyQYIjC+3fGoPYQYQAaQbGQBwaDFIIKADt//AQQIDboYODIKQdB75BBBxW/F4iDwBxiDHO4T7EKAYCEQwgICQaH/sAFByUAYIMBkgOFSoRBE/4lKABUPQauAoEggEIAoKDM/BBVgCGCQZpBGkmAIIJQDUgSqDILMBQarFBQYYOFQYsNIgJBYMQNsQZaSBYpd//6AB/wCBYrSDWYoWQgMkQZcf/3YIKsAcYSDM/oOBsBQL+3bv6DC23YQb0CrZHCAQeyoCDDXoSSKQYsN3//IKsGHAd/wYoH/1HQYVsiVJkmSAQsDto4BLgiDCADnwKJE/BwaDJBwiDFAYJcCvoCBa4PfbQRrBO4IFBL4P7XgwdBt6ADBAQLDCgwCB9oFDF4KDjAEKDCKwwCFCIIFDSoQOD2//NYJoBAoYRHQwaeB3//96PGDoP/Qf6DCh50DMoOAgAAPgz7E356Dt4oCSQynE+wLCRIP//YXDQYfZkoGB/hAQgEBP8X+5MvQYMf/1Ltme7dsIKhxENAJuBPoKMFAQe/RIdv/wIDtvyrdk+yDB+X/t+zQe+X/ckj/4huXLIVbQaUAfAv//r+ER4r7BO4O2SQJ9B94IDXIO+7Mg2f4juSpMkyVfQevs23Jgvy/EcwAtChZBSQYR3FQAT1CBY6YE7f9BYll+1Il+WrEcQYdP/5HDABsPQcPl2VBvm2vEcBQfLMRO/cwZrFdgPbt/2CgXfOIttE4Pt/4dBSQv/74NBQYOShfl+1YjqDDr5vhACfsyFfluy/EfyxQB+1/bQRiCO4Vt3x9DMoVvBwW2eQRrBOIZ6BOIIXCBwYpBv4aBSoIIDtmC/Nv2XYgfy/d/2aC1AAOX5f9z/AgO+pdszzmEAQT1BeQZrDO4qGCDQ4CJCoILI+Vbsn/4EAv/ZkqC3cAPJl/+gEAh5oH350DeobjD76GCBYgFB/4FCDQIdCBYNvTAQFBv4RDAQ3/+BBBgE/QXAAC/g/BA=")); +} + +function draw() { + var d = new Date(); + var h = d.getHours() % 12 || 12, m = d.getMinutes(), yyyy = d.getFullYear(), mm = d.getMonth(), dd = d.getDate(); + var time = (""+h).substr(-2) + ":" + ("0"+m).substr(-2); + g.reset(); // Reset the state of the graphics library + g.clear(); + g.drawImage(getImg()); //load bg image + //TIME + g.setFont("7x11Numeric7Seg",2); + g.setFontAlign(1,1); + g.setColor(0,0,1); + g.drawString(time, 97, 53, false /*clear background*/); + g.setColor(0,0,0); + g.drawString(time, 96, 52, false /*clear background*/); + //SECONDS + g.setFont("7x11Numeric7Seg",1); + //g.setFont("5x9Numeric7Seg"); + g.setFontAlign(-1,1); // align right bottom + g.setColor(0,0,1); + g.drawString(("0"+d.getSeconds()).substr(-2), 100, 42, 0); + g.setColor(0,0,0); + g.drawString(("0"+d.getSeconds()).substr(-2), 99, 41, 0); + //DATE + g.setFont("5x9Numeric7Seg",1); + g.setFontAlign(1,1); + g.setColor(0,0,1); + g.drawString(yyyy+" "+("0"+mm)+" "+dd, 100, 65, 0); + g.setColor(0,0,0); + g.drawString(yyyy+" "+("0"+mm)+" "+dd, 99, 64, 0); + //BATTERY + g.setColor(0,0,1); + g.drawString(E.getBattery(), 137, 53, 0); + g.setColor(0,0,0); + g.drawString(E.getBattery(), 136, 52, 0); + //STEPS + g.setColor(0,0,1); + g.drawString(Bangle.getHealthStatus("day").steps, 137, 65, 0); + g.setColor(0,0,0); + g.drawString(Bangle.getHealthStatus("day").steps, 136, 64, 0); + //WEEK DAY + g.setFont("8x12"); + g.setColor(0,0,1); + if (d.getDay()==0) { + g.drawString("SU", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("SU", 136, 42, 0); + } else if (d.getDay()==1) { + g.drawString("MO", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("MO", 136, 42, 0); + } else if (d.getDay()==2) { + g.drawString("TU", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("TU", 136, 42, 0); + } else if (d.getDay()==3) { + g.drawString("WE", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("WE", 136, 42, 0); + } else if (d.getDay()==4) { + g.setFont("Dylex7x13"); + g.drawString("TH", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("TH", 136, 42, 0); + } else if (d.getDay()==5) { + g.drawString("FR", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("FR", 136, 42, 0); + } else { + g.drawString("SA", 137, 43, 0); + g.setColor(0,0,0); + g.drawString("SA", 136, 42, 0); + } + if(wizible==1){ + Bangle.drawWidgets(); + } +} + +// Clear the screen once, at startup +g.clear(); +// draw immediately at first +draw(); +var secondInterval = setInterval(draw, 1000); +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (secondInterval) clearInterval(secondInterval); + secondInterval = undefined; + if (on) { + secondInterval = setInterval(draw, 1000); + draw(); // draw immediately + } +}); +// Show launcher when middle button pressed +Bangle.setUI("clock"); + +//Toggle Widgets +Bangle.loadWidgets(); +Bangle.on('touch', function(button) { + if(wizible==0){ + wizible=1; + } + else if(wizible==1){ + wizible=0; + } +}); diff --git a/apps/bradbury/app.png b/apps/bradbury/app.png new file mode 100644 index 000000000..f7141d15e Binary files /dev/null and b/apps/bradbury/app.png differ diff --git a/apps/bradbury/metadata.json b/apps/bradbury/metadata.json new file mode 100644 index 000000000..456daa381 --- /dev/null +++ b/apps/bradbury/metadata.json @@ -0,0 +1,14 @@ +{ "id": "bradbury", + "name": "Bradbury Watch", + "shortName":"Bradbury", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.01", + "description": "A watch face based on the classic Seiko model worn by one of my favorite authors. I didn't follow the original lcd layout exactly, opting for larger font for more easily readable time, and adding date, battery level, and step count; read from the device. Tapping the screen toggles visibility of widgets.", + "type": "clock", + "supports":["BANGLEJS2"], + "storage": [ + {"name":"bradbury.app.js","url":"app.js"}, + {"name":"bradbury.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/bradbury/screenshot.png b/apps/bradbury/screenshot.png new file mode 100644 index 000000000..914266668 Binary files /dev/null and b/apps/bradbury/screenshot.png differ diff --git a/apps/bthrm/ChangeLog b/apps/bthrm/ChangeLog index 58d002f22..41eec666a 100644 --- a/apps/bthrm/ChangeLog +++ b/apps/bthrm/ChangeLog @@ -20,3 +20,4 @@ 0.07: Recorder icon only blue if values actually arive Adds some preset modes and a custom one Restructure the settings menu +0.08: Allow scanning for devices in settings diff --git a/apps/bthrm/boot.js b/apps/bthrm/boot.js index 339f6f8c6..3a1f1cc4c 100644 --- a/apps/bthrm/boot.js +++ b/apps/bthrm/boot.js @@ -23,7 +23,10 @@ } function getCache(){ - return require('Storage').readJSON("bthrm.cache.json", true) || {}; + var cache = require('Storage').readJSON("bthrm.cache.json", true) || {}; + if (settings.btname && settings.btname == cache.name) return cache; + clearCache(); + return {}; } function addNotificationHandler(characteristic){ @@ -361,7 +364,13 @@ var promise; if (!device){ - promise = NRF.requestDevice({ filters: serviceFilters }); + var filters = serviceFilters; + if (settings.btname){ + log("Configured device name", settings.btname); + filters = [{name: settings.btname}]; + } + log("Requesting device with filters", filters); + promise = NRF.requestDevice({ filters: filters }); if (settings.gracePeriodRequest){ log("Add " + settings.gracePeriodRequest + "ms grace period after request"); @@ -488,11 +497,15 @@ if (gatt) { if (gatt.connected){ log("Disconnect with gatt: ", gatt); - gatt.disconnect().then(()=>{ - log("Successful disconnect"); - }).catch((e)=>{ - log("Error during disconnect", e); - }); + try{ + gatt.disconnect().then(()=>{ + log("Successful disconnect"); + }).catch((e)=>{ + log("Error during disconnect promise", e); + }); + } catch (e){ + log("Error during disconnect attempt", e); + } } } } diff --git a/apps/bthrm/metadata.json b/apps/bthrm/metadata.json index 1c21269e2..b35ebd6af 100644 --- a/apps/bthrm/metadata.json +++ b/apps/bthrm/metadata.json @@ -2,7 +2,7 @@ "id": "bthrm", "name": "Bluetooth Heart Rate Monitor", "shortName": "BT HRM", - "version": "0.07", + "version": "0.08", "description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.", "icon": "app.png", "type": "app", diff --git a/apps/bthrm/settings.js b/apps/bthrm/settings.js index beefb00e9..4b564d670 100644 --- a/apps/bthrm/settings.js +++ b/apps/bthrm/settings.js @@ -17,54 +17,73 @@ var settings; readSettings(); - var mainmenu = { - '': { 'title': 'Bluetooth HRM' }, - '< Back': back, - 'Mode': { - value: 0 | settings.mode, - min: 0, - max: 3, - format: v => ["Off", "Default", "Both", "Custom"][v], - onchange: v => { - settings.mode = v; - switch (v){ - case 0: - writeSettings("enabled",false); - break; - case 1: - writeSettings("enabled",true); - writeSettings("replace",true); - writeSettings("debuglog",false); - writeSettings("startWithHrm",true); - writeSettings("allowFallback",true); - writeSettings("fallbackTimeout",10); - break; - case 2: - writeSettings("enabled",true); - writeSettings("replace",false); - writeSettings("debuglog",false); - writeSettings("startWithHrm",false); - writeSettings("allowFallback",false); - break; - case 3: - writeSettings("enabled",true); - writeSettings("replace",settings.custom_replace); - writeSettings("debuglog",settings.custom_debuglog); - writeSettings("startWithHrm",settings.custom_startWithHrm); - writeSettings("allowFallback",settings.custom_allowFallback); - writeSettings("fallbackTimeout",settings.custom_fallbackTimeout); - break; + function buildMainMenu(){ + var mainmenu = { + '': { 'title': 'Bluetooth HRM' }, + '< Back': back, + 'Mode': { + value: 0 | settings.mode, + min: 0, + max: 3, + format: v => ["Off", "Default", "Both", "Custom"][v], + onchange: v => { + settings.mode = v; + switch (v){ + case 0: + writeSettings("enabled",false); + break; + case 1: + writeSettings("enabled",true); + writeSettings("replace",true); + writeSettings("debuglog",false); + writeSettings("startWithHrm",true); + writeSettings("allowFallback",true); + writeSettings("fallbackTimeout",10); + break; + case 2: + writeSettings("enabled",true); + writeSettings("replace",false); + writeSettings("debuglog",false); + writeSettings("startWithHrm",false); + writeSettings("allowFallback",false); + break; + case 3: + writeSettings("enabled",true); + writeSettings("replace",settings.custom_replace); + writeSettings("debuglog",settings.custom_debuglog); + writeSettings("startWithHrm",settings.custom_startWithHrm); + writeSettings("allowFallback",settings.custom_allowFallback); + writeSettings("fallbackTimeout",settings.custom_fallbackTimeout); + break; + } + writeSettings("mode",v); } - writeSettings("mode",v); } - }, - 'Custom Mode': function() { E.showMenu(submenu_custom); }, - 'Debug': function() { E.showMenu(submenu_debug); } - }; + }; + + if (settings.btname){ + var name = "Clear " + settings.btname; + mainmenu[name] = function() { + E.showPrompt("Clear current device name?").then((r)=>{ + if (r) { + writeSettings("btname",undefined); + } + E.showMenu(buildMainMenu()); + }); + }; + } + + mainmenu["BLE Scan"] = ()=> createMenuFromScan(); + mainmenu["Custom Mode"] = function() { E.showMenu(submenu_custom); }; + mainmenu.Debug = function() { E.showMenu(submenu_debug); }; + return mainmenu; + } + + var submenu_debug = { '' : { title: "Debug"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Alert on disconnect': { value: !!settings.warnDisconnect, format: v => settings.warnDisconnect ? "On" : "Off", @@ -81,10 +100,41 @@ }, 'Grace periods': function() { E.showMenu(submenu_grace); } }; + + function createMenuFromScan(){ + E.showMenu(); + E.showMessage("Scanning"); + + var submenu_scan = { + '' : { title: "Scan"}, + '< Back': function() { E.showMenu(buildMainMenu()); } + }; + var packets=10; + var scanStart=Date.now(); + NRF.setScan(function(d) { + packets--; + if (packets<=0 || Date.now() - scanStart > 5000){ + NRF.setScan(); + E.showMenu(submenu_scan); + } else if (d.name){ + print("Found device", d); + submenu_scan[d.name] = function(){ + E.showPrompt("Set "+d.name+"?").then((r)=>{ + if (r) { + writeSettings("btname",d.name); + } + E.showMenu(buildMainMenu()); + }); + }; + } + }, { filters: [{services: [ "180d" ]}]}); + } + + var submenu_custom = { '' : { title: "Custom mode"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Replace HRM': { value: !!settings.custom_replace, format: v => settings.custom_replace ? "On" : "Off", @@ -165,7 +215,7 @@ var submenu = { '' : { title: "Grace periods"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Request': { value: settings.gracePeriodRequest, min: 0, @@ -208,5 +258,5 @@ } }; - E.showMenu(mainmenu); + E.showMenu(buildMainMenu()); }) diff --git a/apps/btmultimeter/ChangeLog b/apps/btmultimeter/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/btmultimeter/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/btmultimeter/README.md b/apps/btmultimeter/README.md new file mode 100644 index 000000000..80fcdcf50 --- /dev/null +++ b/apps/btmultimeter/README.md @@ -0,0 +1,32 @@ +# Bluetooth Multimeter + +Connect to compatible a Bluetooth Multimeters and display the result on your wrist! + +## Compatible Bluetooth meters + +Only the OWON is supported right now - feel free to add support for more! + +### OWON OW18E + +Available [on Amazon](https://www.amazon.co.uk/Bluetooth-Multimeter-Multimeters-Voltmeter-Resistance/dp/B08NJT38SF/ref=sr_1_1) + +Turn the meter on, and long-press the Hz/Duty/Delta/Bluetooth button on the right hand side. Now run the app. + +## Usage + +The app currently only displays the current reading from the volt meter. + +If the app fails to connect you'll need to reload it to reconnect. + +To exit the app, long-press the button. + + +## Future functionality... + +* Logging +* Graphs +* More than one meter + +## Creator + +Gordon Williams (please file issues via GitHub) diff --git a/apps/btmultimeter/app-icon.js b/apps/btmultimeter/app-icon.js new file mode 100644 index 000000000..815929fd1 --- /dev/null +++ b/apps/btmultimeter/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4kA///z3vy067fWlP7/t1r3f33vrU07tdxHKn1mnUEv92DgO75xUwmcziIAIiAWJgYXLiIuLC5dwC6xIJCwP/swAHsIXM5GZAA/HC4kCC42I7oAG3OXC4sgC4/e9v+939AQP7C40iC429C4Pu/wCC9YXHGAYXCzoXC3wCCC5AwDC4WZC4M+909AQM7C5AwCC4ef/tfn/dAQQXIDAQXDAA4XHJIYXBzNVAA9XC4K8CR4QwCC4Mzi93AA1xC4JBCC4QwCC4URABIXBCgaqDC5MWC4YRCI4hfC1VEmMX93u8MR/Pd6J3CCQJ3DR4YXF84XBR4ilDbAYXFagM3iP1R4YQBCwbXER4KHBRYaWBR4TWFYoQXCsKPKCIQuEC452BC6MxiPju8xiYZEI5gXBiLDHO5k+FgKSBCYM+C4inJAAMWmlEolDi8TC4jXJAAQWBAANBUoIXCCQQCBDgQXCn0z+gXDokRjwXCCIQXCDoQuHAAJlBC4ZBBC4kiC4ekC4lBI4ioDYQYXD1QXJXIR3DL4fjn4XBGIdBbQQXFYAYvE0gXEF5DXGC4IYBC4WhC5IFCC4dKC4QDB+KPDC4guCX4n6GAIXCmK/CuAXEAgQvEPAIXC14JDmAXDFwYXEeAKpCBAgXECwYXFeQVDC5AAFC400AwoXQAAwXJgYXWGBoWJAF4A==")) diff --git a/apps/btmultimeter/app.js b/apps/btmultimeter/app.js new file mode 100644 index 000000000..11bcca9fb --- /dev/null +++ b/apps/btmultimeter/app.js @@ -0,0 +1,105 @@ +var decoded; +var gatt; + + +function decode(d) { + var value = d.getUint16(4,1); + if (value&32768) + value = -(value&32767); + var flags = d.getUint8(0); + var flags2 = d.getUint8(1); + // mv dc 27,240 "11xxx" + // mv ac 95,240 "1011xxx" + // v dc 36,240 "100xxx" 36(2dp) 35(20dp) + // v ac 100,240 "1100xxx" 100(2dp) 99(20dp) 97(2000dp) + // ohms 55,241 "110xxx" + // beep 231,242 "11100xxx" + // diode 167,242 "10100xxx" + // capac 76,241 "1001xxx" + // hz 162,241 "10100xxx" + // temp 33,242 "100xxx" + // ncv 96,243 "1100xxx" + // uA 146,240 "10010xxx" + // ma 155,240 "10011xxx" + // A 163,240 "10100xxx" + var dp = flags&7; + var range = (flags>>3)&7; + value *= Math.pow(10, -dp); + var isAC = !!(flags&64); + var mode = "?", units = ""; + if (flags2==240) { + if (flags&128) { + mode = "current"; + units = ["","nA","uA","mA","A","kA","MA",""][range]; + } else { + mode = "voltage"; + units = ["","nV","uV","mV","V","kV","MV",""][range] + " " + (isAC?"AC":"DC"); + } + } else if (flags2==241) { + if (isAC) { + mode = "capacitance"; + units = ["","nF","uF","mF","F","kF","MF",""][range]; + } else if (flags&128) { + mode = "frequency"; + units = "Hz"; + } else { + mode = "resistance"; + units = ["","nOhm","uOhm","mOhm","Ohm","kOhm","MOhm",""][range]; + } + } else if (flags2==242) { + if (flags&128) mode = isAC ? "continuity" : "diode"; + else { + mode = "temperature"; + units = isAC ? "F" : "C"; + } + } else if (flags2==243) mode = "ncv"; + //console.log(mode+" "+value+" "+units,new Uint8Array(d.buffer).slice()); + decoded = { + value : value, + mode : mode, // current/voltage/capacitance/frequency/resistance/temperature + units : units, // eg 'mA' + raw : new Uint8Array(d.buffer).slice(), + }; + updateDisplay(decoded); +} + +function updateDisplay(d) { + var mode = d.mode; + mode = mode.substr(0,1).toUpperCase()+mode.substr(1); + var s = d.value.toString(); + + var R = Bangle.appRect; + g.reset().clearRect(R); + g.setFont("12x20").setFontAlign(-1,-1).drawString(mode, R.x, R.y); + g.setFont("12x20").setFontAlign(1,1).drawString(d.units, R.x+R.w-1, R.y+R.h-1); + var fontSize = 80; + g.setFont("Vector",fontSize).setFontAlign(0,0); + while (g.stringWidth(s) > R.w-20) { + fontSize -= 2; + g.setFont("Vector", fontSize); + } + g.drawString(s, R.x+R.w/2, R.y+R.h/2); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +E.showMessage(/*LANG*/"Connecting..."); + +NRF.requestDevice({ filters: [{ name: 'BDM' }] }).then(function(device) { + return device.gatt.connect(); +}).then(function(g) { + gatt = g; + return gatt.getPrimaryService(0xFFF0); +}).then(function(service) { + return service.getCharacteristic(0xFFF4); +}).then(function(c) { + c.on('characteristicvaluechanged', function(event) { + d = event.target.value; + decode(d); + }); + return c.startNotifications(); +}).then(function() { + E.showMessage(/*LANG*/"Connected."); +}).catch(function(e) { + E.showMessage(e.toString()); +}); diff --git a/apps/btmultimeter/app.png b/apps/btmultimeter/app.png new file mode 100644 index 000000000..e9e75c76e Binary files /dev/null and b/apps/btmultimeter/app.png differ diff --git a/apps/btmultimeter/metadata.json b/apps/btmultimeter/metadata.json new file mode 100644 index 000000000..3a9a72063 --- /dev/null +++ b/apps/btmultimeter/metadata.json @@ -0,0 +1,15 @@ +{ "id": "btmultimeter", + "name": "Bluetooth Multimeter", + "shortName":"BT Meter", + "version":"0.01", + "description": "Connect to compatible a Bluetooth Multimeters and display the result on your wrist!", + "icon": "app.png", + "tags": "bluetooth,tool", + "screenshots" : [ { "url":"screenshot.png" } ], + "supports" : ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"btmultimeter.app.js","url":"app.js"}, + {"name":"btmultimeter.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/btmultimeter/screenshot.png b/apps/btmultimeter/screenshot.png new file mode 100644 index 000000000..dfd64eabf Binary files /dev/null and b/apps/btmultimeter/screenshot.png differ diff --git a/apps/bwclk/ChangeLog b/apps/bwclk/ChangeLog new file mode 100644 index 000000000..89fcea519 --- /dev/null +++ b/apps/bwclk/ChangeLog @@ -0,0 +1,7 @@ +0.01: New App. +0.02: Use build in function for steps and other improvements. +0.03: Adapt colors based on the theme of the user. +0.04: Steps can be hidden now such that the time is even larger. +0.05: Included icons for information. +0.06: Design and usability improvements. +0.07: Improved positioning. \ No newline at end of file diff --git a/apps/bwclk/README.md b/apps/bwclk/README.md new file mode 100644 index 000000000..f282bd187 --- /dev/null +++ b/apps/bwclk/README.md @@ -0,0 +1,16 @@ +# BW Clock + +![](screenshot.png) + +## Features +- Fullscreen on/off +- Tab left/right of screen to show steps, temperature etc. +- Enable / disable lock icon in the settings. +- If the "sched" app is installed tab top / bottom of the screen to set the timer. +- The design is adapted to the theme of your bangle. + +## Thanks to +Icons created by Flaticon + +## Creator +- [David Peer](https://github.com/peerdavid) diff --git a/apps/bwclk/app-icon.js b/apps/bwclk/app-icon.js new file mode 100644 index 000000000..1df0fa6a5 --- /dev/null +++ b/apps/bwclk/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgIcah0EgEB/H8iFsAoOY4kMBYMDhmGgXkAoUGiWkAoQQBoAFCjgnCAoM4hgFDuEI+wpC8EKyg1C/0eAoMAsEAiQvBAAeAApQAB/4Ao+P4v/wn0P8Pgn/wnkH4Pjv/j/nn9PH//n/nj/IFF4F88AXBAoM88EcAoPHj//jlDAoOf/+Y+YFHjnnjAjBEIIjD+BHDO9IALA==")) diff --git a/apps/bwclk/app.js b/apps/bwclk/app.js new file mode 100644 index 000000000..0fe71b240 --- /dev/null +++ b/apps/bwclk/app.js @@ -0,0 +1,451 @@ +/* + * Includes + */ +const locale = require('locale'); +const storage = require('Storage'); + +/* + * Statics + */ +const SETTINGS_FILE = "bwclk.setting.json"; +const TIMER_IDX = "bwclk"; +const W = g.getWidth(); +const H = g.getHeight(); + +/* + * Settings + */ +let settings = { + fullscreen: false, + showLock: true, + showInfo: 0, +}; + +let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; +for (const key in saved_settings) { + settings[key] = saved_settings[key] +} + + +/* + * Assets + */ + +// Manrope font +Graphics.prototype.setLargeFont = function(scale) { + // Actual height 49 (50 - 2) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAP8AAAAAAAAA/wAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAfwAAAAAAAAf/AAAAAAAAf/8AAAAAAAf//wAAAAAAP///AAAAAAP///8AAAAAP////wAAAAP////4AAAAP////8AAAAH////8AAAAH////8AAAAB////8AAAAAH///+AAAAAAf//+AAAAAAB//+AAAAAAAH/+AAAAAAAAf+AAAAAAAAB/AAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///gAAAAAD////4AAAAA/////4AAAAH/////4AAAA//////wAAAH//////gAAA///////AAAH//////+AAA///////4AAD/4AAAH/wAAP+AAAAP/AAB/wAAAAf8AAH/AAAAA/4AAf4AAAAB/gAB/gAAAAH+AAP8AAAAAf4AA/wAAAAB/gAD/AAAAAH+AAP8AAAAAf4AAf4AAAAB/gAB/gAAAAH+AAH+AAAAA/4AAf8AAAAH/AAB/4AAAA/8AAD/4AAAH/wAAP/8AAH/+AAAf//////4AAA///////AAAB//////4AAAD//////AAAAH/////4AAAAP////+AAAAAP////gAAAAAD///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAAAAAB/wAAAAAAAAH/AAAAAAAAA/4AAAAAAAAH/gAAAAAAAAf8AAAAAAAAD/gAAAAAAAAP+AAAAAAAAB///////8AAH///////wAAf///////AAB///////8AAH///////wAAf///////AAB///////8AAH///////wAAP///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAfwAAAH8AAAD/AAAB/wAAAf8AAAP/AAAD/wAAB/8AAAf/AAAP/wAAD/8AAB//AAAf/wAAH/8AAD//AAA//gAAf/8AAD/wAAB//wAAf+AAAP//AAB/wAAB//8AAH+AAAP//wAAf4AAB///AAD/AAAP/v8AAP8AAB/8/wAA/wAAP/j/AAD/AAB/8P8AAH+AAH/g/wAAf4AA/8D/AAB/wAH/gP8AAH/AA/+A/wAAf/AP/wD/AAA//D/+AP8AAD////wA/wAAH///+AD/AAAP///wAP8AAAf//+AA/wAAA///wAD/AAAB//+AAP8AAAB//gAA/wAAAB/4AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAD+AAAAHwAAAf4AAAAfwAAB/gAAAB/gAAH+AAAAP/AAAf4AAAA/8AAB/gAAAD/4AAH+ADAAH/wAAf4AeAAP/AAB/gD+AAP8AAH+Af+AA/4AAf4D/4AB/gAB/gP/AAH+AAH+B/8AAf4AAf4P/wAB/gAB/h//AAH+AAH+P/8AAf4AAf5//wAB/gAB/v//gAP+AAH+//+AA/4AAf//f8AH/AAB//5/8B/8AAH//D////gAAf/4P///+AAB//Af///wAAH/4A///+AAAf/AB///wAAB/4AD//+AAAH/AAH//gAAAP4AAD/4AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAA/+AAAAAAAAP/4AAAAAAAH//gAAAAAAB//+AAAAAAAf//4AAAAAAH///gAAAAAB///+AAAAAAf///4AAAAAH//9/gAAAAD///H+AAAAA///wf4AAAAP//8B/gAAAD///AH+AAAA///wAf4AAAH//8AB/gAAAf//AAH+AAAB//gAAf4AAAH/4AAB/gAAAf+AAAH+AAAB/gAf///8AAH4AB////wAAeAAH////AABgAAf///8AAAAAB////wAAAAAH////AAAAAAf///8AAAAAB////wAAAAAH////AAAAAAAAf4AAAAAAAAB/gAAAAAAAAH+AAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAeAAAAAf//AB+AAAH///+AP8AAAf///4A/4AAB////gD/wAAH////Af/gAAf///8B/+AAB////wB/8AAH///+AB/wAAf4Af4AD/gAB/gB/AAP+AAH+AP8AAf4AAf4A/wAB/gAB/gD+AAH+AAH+AP4AAf4AAf4A/gAB/gAB/gD/AAH+AAH+AP8AAf4AAf4A/wAD/gAB/gD/gAf8AAH+AH/AD/wAAf4Af/Af+AAB/gB////4AAH+AD////AAAf4AH///8AAB/gAP///gAAH+AA///8AAAAAAA///AAAAAAAB//4AAAAAAAB/+AAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAAAAB////4AAAAAf////4AAAAH/////4AAAA//////wAAAH//////gAAA///////AAAH//////+AAAf//////4AAD/4D/wH/wAAP+AP8AP/AAB/wB/gAf8AAH/AH8AA/4AAf4A/wAB/gAB/gD/AAH+AAH8AP4AAf4AA/wA/gAB/gAD/AD+AAH+AAH8AP8AAf4AAf4A/wAB/gAB/gD/AAP+AAH+AP+AB/wAAf8Af8AP/AAA/4B/8B/8AAD/gH////gAAP8AP///8AAAfgAf///wAAA8AB///+AAADgAD///wAAAAAAD//+AAAAAAAH//gAAAAAAAH/4AAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAAAH+AAAAAAAAAf4AAAAAAAAB/gAAAAAAAAH+AAAAAAAAAf4AAAAADgAB/gAAAAA+AAH+AAAAAf4AAf4AAAAH/gAB/gAAAD/+AAH+AAAA//4AAf4AAAf//gAB/gAAH//+AAH+AAD///wAAf4AA///8AAB/gAf//+AAAH+AH///gAAAf4D///wAAAB/g///8AAAAH+f//+AAAAAf////gAAAAB////wAAAAAH///8AAAAAAf//+AAAAAAB///gAAAAAAH//wAAAAAAAf/8AAAAAAAB/+AAAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/wAAAAB/wB//wAAAAf/wf//gAAAD//z///AAAAf/////+AAAD//////8AAAf//////4AAD///////gAAP////w//AAB/+f/8Af8AAH/Af/gA/4AAf4A/8AD/gAB/gB/wAH+AAP8AH+AAf4AA/wAf4AB/gAD/AB/gAH+AAP8AH+AAf4AA/wAf4AB/gAB/gB/wAH+AAH+AP/AAf4AAf8A/+AD/gAB/8f/8Af8AAD////4H/wAAP//////+AAAf//////4AAA///////AAAD//////8AAAH//z///gAAAH/+H//4AAAAH/gH//AAAAAAAAH/wAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAP/wAAAAAAAD//wAAAAAAAf//wAAAAAAH///gABgAAA////AAPAAAD///+AB+AAAf///4AP4AAD////wB/wAAP/AP/AH/AAB/4Af+AP+AAH/AA/4A/4AAf4AB/gB/gAB/gAH+AH+AAP8AAP4Af4AA/wAA/gB/gAD/AAD+AH+AAP8AAP4Af4AA/4AB/gB/gAB/gAH+AH+AAH+AAfwA/4AAf8AD/AH/AAB/4Af4A/8AAD/4H/gP/wAAP//////+AAAf//////wAAA///////AAAB//////4AAAD//////AAAAH/////wAAAAH////+AAAAAH////AAAAAAAf/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4AP8AAAAAB/gA/wAAAAAH+AD/AAAAAAf4AP8AAAAAB/gA/wAAAAAH+AD/AAAAAAf4AP8AAAAAB/gA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ExwqHCYlJyYoIicoFg=="), 64+(scale<<8)+(1<<16)); + return this; +}; + + +Graphics.prototype.setMediumFont = function(scale) { + // Actual height 41 (42 - 2) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAB/AAAAAAAP/AAAAAAD//AAAAAA///AAAAAP///AAAAB///8AAAAf///AAAAH///wAAAB///+AAAAH///gAAAAH//4AAAAAH/+AAAAAAH/wAAAAAAH8AAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAH////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gAAH+AAD+AAAD/AAH8AAAB/AAH4AAAA/gAH4AAAAfgAH4AAAAfgAPwAAAAfgAPwAAAAfgAPwAAAAfgAHwAAAAfgAH4AAAAfgAH4AAAA/gAH8AAAA/AAD+AAAD/AAD/gAAH/AAB/////+AAB/////8AAA/////4AAAf////wAAAH////gAAAB///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAfwAAAAAAA/gAAAAAAA/AAAAAAAB/AAAAAAAD+AAAAAAAD8AAAAAAAH8AAAAAAAH//////AAH//////AAH//////AAH//////AAH//////AAH//////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAA/AAAP4AAB/AAAf4AAD/AAA/4AAD/AAB/4AAH/AAD/4AAP/AAH/AAAf/AAH8AAA//AAH4AAB//AAP4AAD//AAPwAAH+/AAPwAAP8/AAPwAAf4/AAPwAA/4/AAPwAA/w/AAPwAB/g/AAPwAD/A/AAP4AH+A/AAH8AP8A/AAH/A/4A/AAD///wA/AAD///gA/AAB///AA/AAA//+AA/AAAP/8AA/AAAD/wAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAH4AAAHwAAH4AAAH4AAH4AAAH8AAH4AAAP+AAH4AAAH+AAH4A4AB/AAH4A+AA/AAH4B/AA/gAH4D/AAfgAH4H+AAfgAH4P+AAfgAH4f+AAfgAH4/+AAfgAH5/+AAfgAH5//AAfgAH7+/AA/gAH/8/gB/AAH/4f4H/AAH/wf//+AAH/gP//8AAH/AH//8AAH+AD//wAAH8AB//gAAD4AAf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAD/AAAAAAAP/AAAAAAB//AAAAAAH//AAAAAAf//AAAAAB///AAAAAH///AAAAAf/8/AAAAB//w/AAAAH/+A/AAAA//4A/AAAD//gA/AAAH/+AA/AAAH/4AA/AAAH/gAA/AAAH+AAA/AAAHwAAA/AAAHAAf///AAEAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAP/AHgAAH///AP4AAH///gP8AAH///gP8AAH///gP+AAH///gD/AAH/A/AB/AAH4A/AA/gAH4A+AAfgAH4B+AAfgAH4B+AAfgAH4B8AAfgAH4B8AAfgAH4B+AAfgAH4B+AAfgAH4B+AA/gAH4B/AA/AAH4A/gD/AAH4A/4H+AAH4Af//+AAH4AP//8AAH4AP//4AAHwAD//wAAAAAB//AAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAD////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gP4H+AAD/AfgD/AAH8A/AB/AAH8A/AA/gAH4B+AAfgAH4B+AAfgAPwB8AAfgAPwB8AAfgAPwB+AAfgAPwB+AAfgAH4B+AAfgAH4B/AA/gAH8B/AB/AAH+A/wD/AAD+A/8P+AAB8Af//+AAB4AP//8AAAwAH//4AAAAAD//gAAAAAA//AAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAHAAPwAAAA/AAPwAAAD/AAPwAAAf/AAPwAAB//AAPwAAP//AAPwAA//8AAPwAH//wAAPwAf/+AAAPwB//4AAAPwP//AAAAPw//8AAAAP3//gAAAAP//+AAAAAP//wAAAAAP//AAAAAAP/4AAAAAAP/gAAAAAAP+AAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAH+A//gAAAf/h//4AAA//z//8AAB/////+AAD/////+AAD///+H/AAH+H/4B/AAH8B/wA/gAH4A/gAfgAH4A/gAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAH4A/gAfgAH4A/gAfgAH8B/wA/gAH/H/4B/AAD///+H/AAD/////+AAB/////+AAA//z//8AAAf/h//4AAAH+A//gAAAAAAH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAD/8AAAAAAP/+AAAAAAf//AAcAAA///gA8AAB///wB+AAD/x/4B/AAD+AP4B/AAH8AH8A/gAH4AH8A/gAH4AD8AfgAP4AD8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAH4AD8AfgAH4AD4A/gAH8AH4B/AAD+APwD/AAD/g/wP+AAB/////+AAA/////8AAAf////4AAAP////wAAAH////AAAAA///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DxcjFyAfISAiHCAiEg=="), 54+(scale<<8)+(1<<16)); + return this; +}; + +Graphics.prototype.setSmallFont = function(scale) { + // Actual height 28 (27 - 0) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//84D//zgP/+GAAAAAAAAAAAAAAAAAAAD4AAAPgAAA+AAAAAAAAAAAAA+AAAD4AAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAg4AAHDgAAcOCABw54AHD/gAf/8AD/8AB//gAP8OAA9w4YCHD/gAcf+AB//gAf/gAP/uAA/w4ADnDgAAcOAABw4AAHAAAAcAAAAAAAAAAAAAAAIAA+A4AH8HwA/4PgHjgOAcHAcBwcBw/BwH78DgfvwOB8HA4HAOBw8A+HngB4P8ADgfgAAAYAAAAAAAAAAB4AAAf4AQB/gDgOHAeA4cDwDhweAOHDwA88eAB/nwAD88AAAHgAAA8AAAHn4AA8/wAHnvgA8cOAHhg4A8GDgHgcOA8B74BgD/AAAH4AAAAAAAAAAAAAAAAAMAAAH8AD8/4Af/3wB/8HgODwOA4HA4DgODgOAcOA4A44DwDzgHAH8AMAPwAQP+AAA/8AAAB4AAADAAAAAA+AAAD4AAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/8AD//+A/+/+H4AD98AAB3gAADIAAAAAAAAAAAAAIAAABwAAAXwAAHPwAB8P8D/gP//4AH/8AAAAAAAAAAAAAAAAAAAAAAAAGAAAA4gAAB/AAAH8AAD/AAAP8AAAH4AAAfwAADiAAAOAAAAAAAAAAAAAAGAAAAYAAABgAAAGAAAAYAAABgAAD/+AAP/4AABgAAAGAAAAYAAABgAAAGAAAAYAAAAAAAAAAAAAADkAAAPwAAA/AAAAAAAAAAAAAAAAAAAAAAAAABgAAAGAAAAYAAABgAAAGAAAAYAAABgAAAGAAAAYAAAAAAAAAAAAAAAAAAAAAAADgAAAOAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAA4AAA/gAA/+AA//AA//AAP/AAA/AAADAAAAAAAAAAAAAAAAAAA//gAP//gB///AHgA8A8AB4DgADgOAAOA4AA4DgADgPAAeAeADwB///AD//4AD/+AAAAAAAAAAAAAAAA4AAAHgAAAcAAADwAAAP//+A///4D///gAAAAAAAAAAAAAAAAAAAAAAYAeADgD4AeAfAD4DwAfgOAD+A4Ae4DgDzgOAeOA4Dw4DweDgH/wOAP+A4AfwDgAAAAAAAAAAAAIAOAA4A4ADwDggHAOHgOA48A4DnwDgO/AOA7uA4D84HgPh/8A8H/gDgH8AAACAAAAAAAAAAAAAHgAAB+AAA/4AAP7gAD+OAA/g4AP4DgA+AOADAA4AAB/+AAH/4AAf/gAADgAAAOAAAAAAAAAAAAAAAAD4cAP/h4A/+HwDw4HgOHAOA4cA4DhwDgOHAOA4cA4Dh4HAOD58A4H/gAAP8AAAGAAAAAAAAAAAAAAAAD/+AAf/8AD//4AePDwDw4HgOHAOA4cA4DhwDgOHAOA4cB4Bw8PAHD/8AIH/gAAH4AAAAAAAAAADgAAAOAAAA4AAYDgAHgOAD+A4B/wDgf4AOP+AA7/AAD/gAAP4AAA8AAAAAAAAAAAAAAAAAAeH8AD+/4Af//wDz8HgOHgOA4OA4Dg4DgODgOA4eA4Dz8HgH//8AP7/gAeH8AAAAAAAAAAAAAAAA+AAAH+AgB/8HAHh4cA8Dg4DgODgOAcOA4Bw4DgODgPA4eAeHDwB///AD//4AD/+AAAAAAAAAAAAAAAAAAAAAAAAAAODgAA4OAADg4AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwA5AHAD8AcAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAB8AAAP4AAB5wAAPDgAB4HAAHAOAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGMAAAYwAABjAAAGMAAAYwAABjAAAGMAAAYwAABjAAAGMAAAYwAABjAAAGMAAAYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAEAAcA4AB4HAADw4AADnAAAH4AAAPAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAB8AAAHgAAA4AAADgDzgOA/OA4D84DgeAAPHwAAf+AAA/wAAB8AAAAAAAAAAAAAAAAAAD+AAB/+AAP/8AB4B4AOABwBwADgHB8OA4P4cDhxxwMGDDAwYMMDBgwwOHHHA4f4cDh/xwHAHCAcAMAA8AwAB8PAAD/4AAD/AAAAAAAAAAAAAACAAAB4AAB/gAA/8AAf+AAP/wAH/nAA/gcADwBwAPwHAA/4cAA/9wAAf/AAAP/AAAD/gAAB+AAAA4AAAAAAAAAAAAAAD///gP//+A///4DgcDgOBwOA4HA4DgcDgOBwOA4HA4Dg8DgPHwOAf/h4A///AB8f4AAAfAAAAAAAP+AAD/+AAf/8AD4D4AeADwBwAHAOAAOA4AA4DgADgOAAOA4AA4DgADgOAAOAcABwB4APAD4D4AHgPAAOA4AAAAAAAAAAAAAAAP//+A///4D///gOAAOA4AA4DgADgOAAOA4AA4DgADgOAAOA8AB4BwAHAHwB8AP//gAP/4AAP+AAAAAAAAAAAAAAAA///4D///gP//+A4HA4DgcDgOBwOA4HA4DgcDgOBwOA4HA4DgcDgOBgOA4AA4AAAAAAAAAAAAAAD///gP//+A///4DgcAAOBwAA4HAADgcAAOBwAA4HAADgcAAOAwAA4AAAAAAAAAf+AAD/+AA//+ADwB4AeADwDwAHgOAAOA4AA4DgADgOAAOA4AA4DgMDgPAweAcDBwB8MfADw/4AHD/AAAPwAAAAAAAAAAAAAAAP//+A///4D///gABwAAAHAAAAcAAABwAAAHAAAAcAAABwAAAHAAAAcAAABwAA///4D///gP//+AAAAAAAAAAAAAAAAAAAD///gP//+A///4AAAAAAAAAAAADgAAAPAAAA+AAAA4AAADgAAAOAAAA4AAAHgP//8A///wD//8AAAAAAAAAAAAAAAAAAAA///4D///gP//+AAHAAAA+AAAP8AAB54AAPDwAB4HgAPAPAB4AfAPAA+A4AA4DAABgAAACAAAAAAAAAAP//+A///4D///gAAAOAAAA4AAADgAAAOAAAA4AAADgAAAOAAAA4AAADgAAAAAAAAAAAAAAP//+A///4D///gD+AAAD+AAAB+AAAB/AAAB/AAAB/AAAB+AAAH4AAB+AAA/gAAP4AAD+AAA/AAAfwAAD///gP//+A///4AAAAAAAAAAAAAAAAAAAP//+A///4D///gHwAAAPwAAAPgAAAfgAAAfAAAAfAAAA/AAAA+AAAB+AAAB8A///4D///gP//+AAAAAAAAAAAP+AAD/+AAf/8AD4D4AeADwBwAHAOAAOA4AA4DgADgOAAOA4AA4DgADgOAAOAcABwB4APAD4D4AH//AAP/4AAP+AAAAAAAAAAAP//+A///4D///gOAcAA4BwADgHAAOAcAA4BwADgHAAOAcAA4DgAD4eAAH/wAAP+AAAPgAAAAAAAA/4AAP/4AB//wAPgPgB4APAHAAcA4AA4DgADgOAAOA4AA4DgADgOAAOA4AO4BwA/AHgB8APgPwAf//gA//uAA/4QAAAAAAAAAA///4D///gP//+A4BwADgHAAOAcAA4BwADgHAAOAcAA4B8ADgP8APh/8Af/H4A/4HgA+AGAAAAAAAAAAAABgAHwHAA/g+AH/A8A8cBwDg4DgODgOA4OA4DgcDgOBwOA4HA4DwODgHg4cAPh/wAcH+AAwPwAAAAADgAAAOAAAA4AAADgAAAOAAAA4AAAD///gP//+A///4DgAAAOAAAA4AAADgAAAOAAAA4AAADgAAAAAAAAAAAAAAAAAP//AA///AD//+AAAB8AAABwAAADgAAAOAAAA4AAADgAAAOAAAA4AAAHgAAA8A///gD//8AP//gAAAAAAAAAAIAAAA8AAAD+AAAH/AAAD/wAAB/4AAA/8AAAf4AAAPgAAB+AAA/4AAf+AAP/AAH/gAD/wAAP4AAA4AAAAAAAAPAAAA/gAAD/4AAA/+AAAf/AAAH/gAAB+AAAf4AAf/AAf/AAP/gAD/gAAPwAAA/4AAA/+AAAf/AAAH/wAAB/gAAB+AAB/4AA/+AA/+AA/+AAD/AAAPAAAAgAAAAAAAAMAAGA4AA4D4APgHwB8APwfAAPn4AAf+AAAfwAAB/AAAf+AAD4+AA/B8AHwB8A+AD4DgADgMAAGAwAAADwAAAPwAAAPwAAAfgAAAfgAAAf/4AAf/gAH/+AB+AAAPwAAD8AAA/AAADwAAAMAAAAgAAAAAAAAMAACA4AA4DgAPgOAD+A4Af4DgH7gOB+OA4Pw4Dj8DgO/AOA/4A4D+ADgPgAOA4AA4DAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////gAAAOAAAA4AAADAAAAAAAAAAAAAAAAAAAAAAA4AAAD+AAAP/gAAH/4AAB/+AAAf+AAAH4AAABgAAAAAAAAADAAAAOAAAA4AAADgAAAP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAADgAAAcAAADgAAAcAAADgAAAcAAAB4AAADwAAADgAAAHAAAAOAAAAYAAAAAAAAAAAAAAAAAAAAMAAAAwAAADAAAAMAAAAwAAADAAAAMAAAAwAAADAAAAMAAAAwAAADAAAAMAAAAwAAADAAAAMAAAAwAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAHH8AA8/4AHzjgAcMOABxwYAHHBgAccOABxwwAHGHAAP/4AA//4AA//gAAAAAAAAAAAAAAAAAAA///4D///gP//+AA4BwAHADgAcAOABwA4AHADgAcAOAB4B4ADwPAAP/8AAf/AAAf4AAAAAAAAAAAAPwAAD/wAAf/gADwPAAeAeABwA4AHADgAcAOABwA4AHADgAeAeAA8DwABwOAADAwAAAAAAAAAAAA/AAAP/AAD//AAPA8AB4B4AHADgAcAOABwA4AHADgAcAOAA4BwD///gP//+A///4AAAAAAAAAAAAAAAAPwAAD/wAAf/gAD2PAAeYeABxg4AHGDgAcYOABxg4AHGDgAeYeAA/jwAB+OAAD4wAABgAAAAAAAAAAABgAAAGAAAB//+Af//4D///gPcAAA5gAADGAAAMYAAAAAAAAAPwAAD/wMA//w4DwPHgeAePBwA4cHADhwcAOHBwA4cHADhwOAcPB///4H///Af//wAAAAAAAAAAAAAAAAAAD///gP//+AA//4ADgAAAcAAABwAAAHAAAAcAAABwAAAHgAAAP/+AAf/4AA//gAAAAAAAAAAAAAAMf/+A5//4Dn//gAAAAAAAAAAAAAAAAAAHAAAAfn///+f//+5///wAAAAAAAAAAAAAAAAAAP//+A///4D///gAAcAAAD8AAAf4AADzwAAeHgAHwPAAeAeABgA4AEABgAAAAAAAAAD///gP//+A///4AAAAAAAAAAAAAAAAAAAAf/+AB//4AH//gAOAAABwAAAHAAAAcAAABwAAAHgAAAP/+AA//4AB//gAOAAABwAAAHAAAAcAAABwAAAHgAAAf/+AA//4AA//gAAAAAAAAAAAAAAAf/+AB//4AD//gAOAAABwAAAHAAAAcAAABwAAAHAAAAeAAAA//4AB//gAD/+AAAAAAAAAAAAAAAAD8AAA/8AAH/4AA8DwAHgHgAcAOABwA4AHADgAcAOABwA4AHgHgAPh8AAf/gAA/8AAA/AAAAAAAAAAAAAAAAB///8H///wf///A4BwAHADgAcAOABwA4AHADgAcAOAB4B4ADwPAAP/8AAf/AAAf4AAAAAAAAAAAAPwAAD/wAA//wADwPAAeAeABwA4AHADgAcAOABwA4AHADgAOAcAB///8H///wf///AAAAAAAAAAAAAAAAAAAH//gAf/+AB//4ADwAAAcAAABwAAAHAAAAcAAAAAAAAAAMAAHw4AA/jwAH+HgAcYOABxw4AHHDgAcMOABw44AHjjgAPH+AA8fwAAw+AAAAAABgAAAGAAAAcAAAf//wB///AH//+ABgA4AGADgAYAOABgA4AAAAAAAAAAAAAAAH/AAAf/wAB//wAAB/AAAAeAAAA4AAADgAAAOAAAA4AAADgAAAcAB//4AH//gAf/+AAAAAAAAAAAAAAABwAAAH4AAAf8AAAP8AAAH+AAAD+AAAD4AAA/gAAf8AAP+AAH/AAAfgAABwAAAAAAAAAAAABwAAAH8AAAf+AAAP/gAAD/gAAB+AAAf4AAP8AAP+AAB/AAAH4AAAf8AAAP+AAAD/gAAB+AAAf4AAf/AAP/AAB/gAAHgAAAQAAABAAIAHADgAeAeAA8HwAB8+AAD/gAAD8AAAPwAAD/gAAfPgADwfAAeAeABwA4AEAAgAAAAABAAAAHgAAAfwAAA/wAAAf4BwAP4/AAP/8AAP+AAD/AAB/wAA/4AAP8AAB+AAAHAAAAQAAAAAAIAHADgAcAeABwD4AHA/gAcHuABx84AHPDgAf4OAB/A4AHwDgAeAOABgA4AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAH4Af//////n//AAAA4AAADgAAAAAAAAAAAAAAAAAP//+A///4D///gAAAAAAAAAAAAAAAAAAA4AAADgAAAOAAAA//5/9////wAH4AAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAeAAAD4AAAOAAAA4AAADgAAAHAAAAcAAAA4AAADgAAAOAAAD4AAAPAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 32, atob("BgkMGhEZEgYMDAwQCAwICxILEBAREBEOEREJCREVEQ8ZEhEUExAOFBQHDREPGBMUERQSEhEUERsREBIMCwwTEg4QERAREQoREQcHDgcYEREREQoPDBEPFg8PDwwIDBMc"), 28+(scale<<8)+(1<<16)); + return this; +}; + +var imgLock = { + width : 16, height : 16, bpp : 1, + transparent : 0, + buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w=")) +}; + +var imgSteps = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/H///wv4CBn4CD8ACCj4IBj8f+Eeh/wjgCBngCCg/4nEH//4h/+jEP/gRBAQX+jkf/wgB//8GwP4FoICDHgICCBwIA==")) +}; + +var imgBattery = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/4AN4EAg4TBgd///9oEAAQv8ARQRDDQQgCEwQ4OA")) +}; + +var imgBpm = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/4AOn4CD/wCCjgCCv/8jF/wGYgOA5MB//BC4PDAQnjAQPnAQgANA")) +}; + +var imgTemperature = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("//D///wICBjACBngCNkgCP/0kv/+s1//nDn/8wICEBAIOC/08v//IYJECA==")) +}; + +var imgWind = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/0f//8h///Pn//zAQXzwf/88B//mvGAh18gEevn/DIICB/PwgEBAQMHBAIADFwM/wEAGAP/54CD84CE+eP//wIQU/A==")) +}; + +var imgTimer = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/+B/4CD84CEBAPygFP+F+h/x/+P+fz5/n+HnAQNn5/wuYCBmYCC5kAAQfOgFz80As/ngHn+fD54mC/F+j/+gF/HAQA==")) +}; + +var imgCharging = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("//+v///k///4AQPwBANgBoMxBoMb/P+h/w/kH8H4gfB+EBwfggHH4EAt4CBn4CBj4CBh4FCCIO/8EB//Agf/wEH/8Gh//x////fAQIA=")) +}; + +var imgWatch = { + width : 24, height : 24, bpp : 1, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/8B//+ARANB/l4//5/1/+f/n/n5+fAQnf9/P44CC8/n7/n+YOB/+fDQQgCEwQsCHBBEC")) +}; + + +/* + * INFO ENTRIES + */ +var infoArray = [ + function(){ return [ null, null, "left" ] }, + function(){ return [ "Bangle", imgWatch, "right" ] }, + function(){ return [ E.getBattery() + "%", imgBattery, "left" ] }, + function(){ return [ getSteps(), imgSteps, "left" ] }, + function(){ return [ Math.round(Bangle.getHealthStatus("last").bpm) + " bpm", imgBpm, "left"] }, + function(){ return [ getWeather().temp, imgTemperature, "left" ] }, + function(){ return [ getWeather().wind, imgWind, "left" ] }, +]; +const NUM_INFO=infoArray.length; + + +function getInfoEntry(){ + if(isAlarmEnabled()){ + return [getAlarmMinutes() + " min.", imgTimer, "left"] + } else if(Bangle.isCharging()){ + return [E.getBattery() + "%", imgCharging, "left"] + } else{ + return infoArray[settings.showInfo](); + } +} + + +/* + * Helper + */ +function getSteps() { + var steps = 0; + try{ + if (WIDGETS.wpedom !== undefined) { + steps = WIDGETS.wpedom.getSteps(); + } else if (WIDGETS.activepedom !== undefined) { + steps = WIDGETS.activepedom.getSteps(); + } else { + steps = Bangle.getHealthStatus("day").steps; + } + } catch(ex) { + // In case we failed, we can only show 0 steps. + } + + steps = Math.round(steps/100) / 10; // This ensures that we do not show e.g. 15.0k and 15k instead + return steps + "k"; +} + + +function getWeather(){ + var weatherJson; + + try { + weatherJson = storage.readJSON('weather.json'); + var weather = weatherJson.weather; + + // Temperature + weather.temp = locale.temp(weather.temp-273.15); + + // Humidity + weather.hum = weather.hum + "%"; + + // Wind + const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); + weather.wind = Math.round(wind[1]) + " km/h"; + + return weather + + } catch(ex) { + // Return default + } + + return { + temp: "? °C", + hum: "-", + txt: "-", + wind: "? km/h", + wdir: "-", + wrose: "-" + }; +} + +function isAlarmEnabled(){ + try{ + var alarm = require('sched'); + var alarmObj = alarm.getAlarm(TIMER_IDX); + if(alarmObj===undefined || !alarmObj.on){ + return false; + } + + return true; + + } catch(ex){ } + return false; +} + +function getAlarmMinutes(){ + if(!isAlarmEnabled()){ + return -1; + } + + var alarm = require('sched'); + var alarmObj = alarm.getAlarm(TIMER_IDX); + return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); +} + +function increaseAlarm(){ + try{ + var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0; + var alarm = require('sched') + alarm.setAlarm(TIMER_IDX, { + timer : (minutes+5)*60*1000, + }); + alarm.reload(); + } catch(ex){ } +} + +function decreaseAlarm(){ + try{ + var minutes = getAlarmMinutes(); + minutes -= 5; + + var alarm = require('sched') + alarm.setAlarm(TIMER_IDX, undefined); + + if(minutes > 0){ + alarm.setAlarm(TIMER_IDX, { + timer : minutes*60*1000, + }); + } + + alarm.reload(); + } catch(ex){ } +} + + +/* + * DRAW functions + */ + +function draw() { + // Queue draw again + queueDraw(); + + // Draw clock + drawDate(); + drawTime(); + drawLock(); + drawWidgets(); +} + + +function drawDate(){ + // Draw background + var y = H/5*2; + g.reset().clearRect(0,0,W,W); + + // Draw date + y = parseInt(y/2); + y += settings.fullscreen ? 0 : 15; + var date = new Date(); + var dateStr = date.getDate(); + dateStr = ("0" + dateStr).substr(-2); + g.setMediumFont(); // Needed to compute the width correctly + var dateW = g.stringWidth(dateStr); + + g.setSmallFont(); + var dayStr = locale.dow(date, true); + var monthStr = locale.month(date, 1); + var dayW = Math.max(g.stringWidth(dayStr), g.stringWidth(monthStr)); + var fullDateW = dateW + 10 + dayW; + + g.setFontAlign(-1,0); + g.setMediumFont(); + g.setColor(g.theme.fg); + g.drawString(dateStr, W/2 - fullDateW / 2, y+5); + + g.setSmallFont(); + g.drawString(monthStr, W/2 - fullDateW/2 + 10 + dateW, y+14); + g.drawString(dayStr, W/2 - fullDateW/2 + 10 + dateW, y-10); +} + + +function drawTime(){ + // Draw background + var y = H/5*2 + (settings.fullscreen ? 0 : 8); + g.setColor(g.theme.fg); + g.fillRect(0,y,W,H); + var date = new Date(); + + // Draw time + g.setColor(g.theme.bg); + g.setFontAlign(0,0); + var timeStr = locale.time(date,1); + y += parseInt((H - y)/2) + 5; + + var infoEntry = getInfoEntry(); + var infoStr = infoEntry[0]; + var infoImg = infoEntry[1]; + var printImgLeft = infoEntry[2] == "left"; + + // Show large or small time depending on info entry + if(infoStr == null){ + g.setLargeFont(); + } else { + y -= 15; + g.setMediumFont(); + } + g.drawString(timeStr, W/2, y); + + // Draw info if set + if(infoStr == null){ + return; + } + + y += 35; + g.setFontAlign(0,0); + g.setSmallFont(); + var imgWidth = 0; + if(infoImg !== undefined){ + imgWidth = infoImg.width; + var strWidth = g.stringWidth(infoStr); + g.drawImage( + infoImg, + W/2 + (printImgLeft ? -strWidth/2-2 : strWidth/2+2) - infoImg.width/2, + y - infoImg.height/2 + ); + } + g.drawString(infoStr, printImgLeft ? W/2 + imgWidth/2 + 2 : W/2 - imgWidth/2 - 2, y+3); +} + + +function drawLock(){ + if(settings.showLock && Bangle.isLocked()){ + g.setColor(g.theme.fg); + g.drawImage(imgLock, W-16, 2); + } +} + + +function drawWidgets(){ + if(settings.fullscreen){ + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} + } else { + Bangle.drawWidgets(); + } +} + + + +/* + * Draw timeout + */ +// timeout used to update every minute +var drawTimeout; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + + +/* + * Load clock, widgets and listen for events + */ +Bangle.loadWidgets(); + +// Clear the screen once, at startup and set the correct theme. +var bgOrig = g.theme.bg +var fgOrig = g.theme.fg +g.setTheme({bg:fgOrig,fg:bgOrig}).clear(); +draw(); + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.on('lock', function(isLocked) { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + draw(); +}); + +Bangle.on('charging',function(charging) { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + draw(); +}); + +Bangle.on('touch', function(btn, e){ + var left = parseInt(g.getWidth() * 0.2); + var right = g.getWidth() - left; + var upper = parseInt(g.getHeight() * 0.2); + var lower = g.getHeight() - upper; + + var is_left = e.x < left; + var is_right = e.x > right; + var is_upper = e.y < upper; + var is_lower = e.y > lower; + + if(is_upper){ + Bangle.buzz(40, 0.6); + increaseAlarm(); + drawTime(); + } + + if(is_lower){ + Bangle.buzz(40, 0.6); + decreaseAlarm(); + drawTime(); + } + + if(is_right){ + Bangle.buzz(40, 0.6); + settings.showInfo = (settings.showInfo+1) % NUM_INFO; + drawTime(); + } + + if(is_left){ + Bangle.buzz(40, 0.6); + settings.showInfo = settings.showInfo-1; + settings.showInfo = settings.showInfo < 0 ? NUM_INFO-1 : settings.showInfo; + drawTime(); + } +}); + + +E.on("kill", function(){ + storage.write(SETTINGS_FILE, settings); +}); + + +// Show launcher when middle button pressed +Bangle.setUI("clock"); diff --git a/apps/bwclk/app.png b/apps/bwclk/app.png new file mode 100644 index 000000000..5073f0ed0 Binary files /dev/null and b/apps/bwclk/app.png differ diff --git a/apps/bwclk/metadata.json b/apps/bwclk/metadata.json new file mode 100644 index 000000000..59d044eb3 --- /dev/null +++ b/apps/bwclk/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "bwclk", + "name": "BW Clock", + "version": "0.07", + "description": "BW Clock.", + "readme": "README.md", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"bwclk.app.js","url":"app.js"}, + {"name":"bwclk.img","url":"app-icon.js","evaluate":true}, + {"name":"bwclk.settings.js","url":"settings.js"} + ] +} diff --git a/apps/bwclk/screenshot.png b/apps/bwclk/screenshot.png new file mode 100644 index 000000000..b30ba4166 Binary files /dev/null and b/apps/bwclk/screenshot.png differ diff --git a/apps/bwclk/screenshot_2.png b/apps/bwclk/screenshot_2.png new file mode 100644 index 000000000..ea2dc780b Binary files /dev/null and b/apps/bwclk/screenshot_2.png differ diff --git a/apps/bwclk/screenshot_3.png b/apps/bwclk/screenshot_3.png new file mode 100644 index 000000000..fb5b153b8 Binary files /dev/null and b/apps/bwclk/screenshot_3.png differ diff --git a/apps/bwclk/settings.js b/apps/bwclk/settings.js new file mode 100644 index 000000000..0fdaf1a28 --- /dev/null +++ b/apps/bwclk/settings.js @@ -0,0 +1,40 @@ +(function(back) { + const SETTINGS_FILE = "bwclk.setting.json"; + + // initialize with default settings... + const storage = require('Storage') + let settings = { + fullscreen: false, + showLock: true, + }; + let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; + for (const key in saved_settings) { + settings[key] = saved_settings[key] + } + + function save() { + storage.write(SETTINGS_FILE, settings) + } + + + E.showMenu({ + '': { 'title': 'BW Clock' }, + '< Back': back, + 'Fullscreen': { + value: settings.fullscreen, + format: () => (settings.fullscreen ? 'Yes' : 'No'), + onchange: () => { + settings.fullscreen = !settings.fullscreen; + save(); + }, + }, + 'Show Lock': { + value: settings.showLock, + format: () => (settings.showLock ? 'Yes' : 'No'), + onchange: () => { + settings.showLock = !settings.showLock; + save(); + }, + } + }); + }) diff --git a/apps/calculator/metadata.json b/apps/calculator/metadata.json index 3d1310859..e78e4d54f 100644 --- a/apps/calculator/metadata.json +++ b/apps/calculator/metadata.json @@ -8,6 +8,7 @@ "screenshots": [{"url":"screenshot_calculator.png"}], "tags": "app,tool", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "storage": [ {"name":"calculator.app.js","url":"app.js"}, {"name":"calculator.img","url":"calculator-icon.js","evaluate":true} diff --git a/apps/calendar/ChangeLog b/apps/calendar/ChangeLog index beba4ed95..ea8934f84 100644 --- a/apps/calendar/ChangeLog +++ b/apps/calendar/ChangeLog @@ -4,3 +4,6 @@ 0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed). 0.05: Update calendar weekend colors for start on Sunday 0.06: Use larger font for dates +0.07: Fix off-by-one-error on previous month +0.08: Do not register as watch, manually start clock on button + read start of week from system settings diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 62702e349..fc7e93cf5 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -18,8 +18,7 @@ const blue = "#0000ff"; const yellow = "#ffff00"; let settings = require('Storage').readJSON("calendar.json", true) || {}; -if (settings.startOnSun === undefined) - settings.startOnSun = false; +let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0; if (settings.ndColors === undefined) if (process.env.HWVERSION == 2) { settings.ndColors = true; @@ -50,14 +49,14 @@ function getDowLbls(locale) { case "de_AT": case "de_CH": case "de_DE": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]; } else { dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]; } break; case "nl_NL": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"]; } else { dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"]; @@ -66,14 +65,14 @@ function getDowLbls(locale) { case "fr_BE": case "fr_CH": case "fr_FR": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"]; } else { dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"]; } break; case "sv_SE": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"]; } else { dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"]; @@ -81,21 +80,21 @@ function getDowLbls(locale) { break; case "it_CH": case "it_IT": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"]; } else { dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"]; } break; case "oc_FR": - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"]; } else { dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"]; } break; default: - if (settings.startOnSun) { + if (startOnSun) { dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; } else { dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]; @@ -110,7 +109,7 @@ function drawCalendar(date) { g.clearRect(0, 0, maxX, maxY); g.setBgColor(bgColorMonth); g.clearRect(0, 0, maxX, headerH); - if (settings.startOnSun){ + if (startOnSun){ g.setBgColor(bgColorWeekend); g.clearRect(0, headerH + rowH, colW, maxY); g.setBgColor(bgColorDow); @@ -150,7 +149,7 @@ function drawCalendar(date) { }); date.setDate(1); - const dow = date.getDay() + (settings.startOnSun ? 1 : 0); + const dow = date.getDay() + (startOnSun ? 1 : 0); const dowNorm = dow === 0 ? 7 : dow; const monthMaxDayMap = { @@ -171,7 +170,7 @@ function drawCalendar(date) { let days = []; let nextMonthDay = 1; let thisMonthDay = 51; - let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm; + let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm + 1; for (let i = 0; i < colN * (rowN - 1) + 1; i++) { if (i < dowNorm) { days.push(prevMonthDay); @@ -242,5 +241,5 @@ Bangle.on("touch", area => { }); // Show launcher when button pressed -Bangle.setUI("clock"); // TODO: ideally don't set 'clock' mode +setWatch(() => load(), process.env.HWVERSION === 2 ? BTN : BTN3, { repeat: false, edge: "falling" }); // No space for widgets! diff --git a/apps/calendar/metadata.json b/apps/calendar/metadata.json index 5531c03c3..5f968b364 100644 --- a/apps/calendar/metadata.json +++ b/apps/calendar/metadata.json @@ -1,7 +1,7 @@ { "id": "calendar", "name": "Calendar", - "version": "0.06", + "version": "0.08", "description": "Simple calendar", "icon": "calendar.png", "screenshots": [{"url":"screenshot_calendar.png"}], diff --git a/apps/calendar/settings.js b/apps/calendar/settings.js index 3c8f7d8e8..192d2ece0 100644 --- a/apps/calendar/settings.js +++ b/apps/calendar/settings.js @@ -1,8 +1,6 @@ (function (back) { var FILE = "calendar.json"; var settings = require('Storage').readJSON(FILE, true) || {}; - if (settings.startOnSun === undefined) - settings.startOnSun = false; if (settings.ndColors === undefined) if (process.env.HWVERSION == 2) { settings.ndColors = true; @@ -17,14 +15,6 @@ E.showMenu({ "": { "title": "Calendar" }, "< Back": () => back(), - 'Start Sunday': { - value: settings.startOnSun, - format: v => v ? "Yes" : "No", - onchange: v => { - settings.startOnSun = v; - writeSettings(); - } - }, 'B2 Colors': { value: settings.ndColors, format: v => v ? "Yes" : "No", diff --git a/apps/calibration/README.md b/apps/calibration/README.md new file mode 100644 index 000000000..37f637d21 --- /dev/null +++ b/apps/calibration/README.md @@ -0,0 +1,11 @@ +# Banglejs - Touchscreen calibration +A simple calibration app for the touchscreen + +## Usage + +Once lauched touch the cross that appear on the screen to make +another spawn elsewhere. + +each new touch on the screen will help to calibrate the offset +of your finger on the screen. After five or more input, press +the button to save the calibration and close the application. \ No newline at end of file diff --git a/apps/calibration/app-icon.js b/apps/calibration/app-icon.js new file mode 100644 index 000000000..af66c3f68 --- /dev/null +++ b/apps/calibration/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkB/4AJ+EPBhQXg+BBDCyJaGGR5zIDBoQEL4QYOLYR3GBIouJR5AYBGBILBU5QMGFwgiFX4wwIEI4XGGBAgHd44+HD44XHNw4XWM5IIHCIoXWV5IXICQgXvLxAAKCYYXh5nMC6n8C4PPC5MAAA8PC4ZxBACAXOI653hU5zvJABASEC5PwHI4XcMBIXICIoXXJBAXHCAwXXJBAXHB5AfGC4ygJEAwXGQ5BoIQxoiDBYgXECwIuIBgb5ECIQJFGBQmCC4QHEDBwAFCxoYICx5ZELZoZJFiIXpA=")) \ No newline at end of file diff --git a/apps/calibration/app.js b/apps/calibration/app.js new file mode 100644 index 000000000..d3823de63 --- /dev/null +++ b/apps/calibration/app.js @@ -0,0 +1,85 @@ +class BanglejsApp { + constructor() { + this.x = 0; + this.y = 0; + this.settings = { + xoffset: 0, + yoffset: 0, + }; + } + + load_settings() { + let settings = require('Storage').readJSON('calibration.json', true) || {active: false}; + + // do nothing if the calibration is deactivated + if (settings.active === true) { + // cancel the calibration offset + Bangle.on('touch', function(button, xy) { + xy.x += settings.xoffset; + xy.y += settings.yoffset; + }); + } + if (!settings.xoffset) settings.xoffset = 0; + if (!settings.yoffset) settings.yoffset = 0; + + console.log('loaded settings:'); + console.log(settings); + + return settings; + } + + save_settings() { + this.settings.active = true; + this.settings.reload = false; + require('Storage').writeJSON('calibration.json', this.settings); + + console.log('saved settings:'); + console.log(this.settings); + } + + explain() { + /* + * TODO: + * Present how to use the application + * + */ + } + + drawTarget() { + this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32)); + this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80)); + + g.clearRect(0, 24, g.getWidth(), g.getHeight() - 24); + g.drawLine(this.x, this.y - 5, this.x, this.y + 5); + g.drawLine(this.x - 5, this.y, this.x + 5, this.y); + g.setFont('Vector', 10); + g.drawString('current offset: ' + this.settings.xoffset + ', ' + this.settings.yoffset, 0, 24); + } + + setOffset(xy) { + this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2); + this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2); + } +} + + +E.srand(Date.now()); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +calibration = new BanglejsApp(); +calibration.load_settings(); + +let modes = { + mode : 'custom', + btn : function(n) { + calibration.save_settings(this.settings); + load(); + }, + touch : function(btn, xy) { + calibration.setOffset(xy); + calibration.drawTarget(); + }, +}; +Bangle.setUI(modes); +calibration.drawTarget(); diff --git a/apps/calibration/boot.js b/apps/calibration/boot.js new file mode 100644 index 000000000..237fb2e0d --- /dev/null +++ b/apps/calibration/boot.js @@ -0,0 +1,14 @@ +let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false}; +Bangle.on('touch', function(button, xy) { + // do nothing if the calibration is deactivated + if (cal_settings.active === false) return; + + // reload the calibration offset at each touch event /!\ bad for the flash memory + if (cal_settings.reload === true) { + cal_settings = require('Storage').readJSON("calibration.json", true); + } + + // apply the calibration offset + xy.x += cal_settings.xoffset; + xy.y += cal_settings.yoffset; +}); diff --git a/apps/calibration/calibration.png b/apps/calibration/calibration.png new file mode 100644 index 000000000..3fb44beee Binary files /dev/null and b/apps/calibration/calibration.png differ diff --git a/apps/calibration/metadata.json b/apps/calibration/metadata.json new file mode 100644 index 000000000..122a2c175 --- /dev/null +++ b/apps/calibration/metadata.json @@ -0,0 +1,17 @@ +{ "id": "calibration", + "name": "Touchscreen Calibration", + "shortName":"Calibration", + "icon": "calibration.png", + "version":"1.00", + "description": "A simple calibration app for the touchscreen", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "tags": "tool", + "storage": [ + {"name":"calibration.app.js","url":"app.js"}, + {"name":"calibration.boot.js","url":"boot.js"}, + {"name":"calibration.settings.js","url":"settings.js"}, + {"name":"calibration.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name":"calibration.json"}] +} diff --git a/apps/calibration/settings.js b/apps/calibration/settings.js new file mode 100644 index 000000000..6db8dd3bb --- /dev/null +++ b/apps/calibration/settings.js @@ -0,0 +1,23 @@ +(function(back) { + var FILE = "calibration.json"; + var settings = Object.assign({ + active: true, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + E.showMenu({ + "" : { "title" : "Calibration" }, + "< Back" : () => back(), + 'Active': { + value: !!settings.active, + format: v => v? "On":"Off", + onchange: v => { + settings.active = v; + writeSettings(); + } + }, + }); +}) \ No newline at end of file diff --git a/apps/choozi/ChangeLog b/apps/choozi/ChangeLog index 5560f00bc..03f7ef832 100644 --- a/apps/choozi/ChangeLog +++ b/apps/choozi/ChangeLog @@ -1 +1,3 @@ 0.01: New App! +0.02: Support Bangle.js 2 +0.03: Fix bug for Bangle.js 2 where g.flip was not being called. diff --git a/apps/choozi/appb2.js b/apps/choozi/appb2.js new file mode 100644 index 000000000..5f217f638 --- /dev/null +++ b/apps/choozi/appb2.js @@ -0,0 +1,207 @@ +/* Choozi - Choose people or things at random using Bangle.js. + * Inspired by the "Chwazi" Android app + * + * James Stanley 2021 + */ + +var colours = ['#ff0000', '#ff8080', '#00ff00', '#80ff80', '#0000ff', '#8080ff', '#ffff00', '#00ffff', '#ff00ff', '#ff8000', '#ff0080', '#8000ff', '#0080ff']; + +var stepAngle = 0.18; // radians - resolution of polygon +var gapAngle = 0.035; // radians - gap between segments +var perimMin = 80; // px - min. radius of perimeter +var perimMax = 87; // px - max. radius of perimeter + +var segmentMax = 70; // px - max radius of filled-in segment +var segmentStep = 5; // px - step size of segment fill animation +var circleStep = 4; // px - step size of circle fill animation + +// rolling ball animation: +var maxSpeed = 0.08; // rad/sec +var minSpeed = 0.001; // rad/sec +var animStartSteps = 300; // how many steps before it can start slowing? +var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate +var ballSize = 3; // px - ball radius +var ballTrack = 75; // px - radius of ball path + +var centreX = 88; // px - centre of screen +var centreY = 88; // px - centre of screen + +var fontSize = 50; // px + +var radians = 2*Math.PI; // radians per circle + +var defaultN = 3; // default value for N +var minN = 2; +var maxN = colours.length; +var N; +var arclen; + +// https://www.frankmitchell.org/2015/01/fisher-yates/ +function shuffle (array) { + var i = 0 + , j = 0 + , temp = null; + + for (i = array.length - 1; i > 0; i -= 1) { + j = Math.floor(Math.random() * (i + 1)); + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} + +// draw an arc between radii minR and maxR, and between +// angles minAngle and maxAngle +function arc(minR, maxR, minAngle, maxAngle) { + var step = stepAngle; + var angle = minAngle; + var inside = []; + var outside = []; + var c, s; + while (angle < maxAngle) { + c = Math.cos(angle); + s = Math.sin(angle); + inside.push(centreX+c*minR); // x + inside.push(centreY+s*minR); // y + // outside coordinates are built up in reverse order + outside.unshift(centreY+s*maxR); // y + outside.unshift(centreX+c*maxR); // x + angle += step; + } + c = Math.cos(maxAngle); + s = Math.sin(maxAngle); + inside.push(centreX+c*minR); + inside.push(centreY+s*minR); + outside.unshift(centreY+s*maxR); + outside.unshift(centreX+c*maxR); + + var vertices = inside.concat(outside); + g.fillPoly(vertices, true); +} + +// draw the arc segments around the perimeter +function drawPerimeter() { + g.clear(); + for (var i = 0; i < N; i++) { + g.setColor(colours[i%colours.length]); + var minAngle = (i/N)*radians; + arc(perimMin,perimMax,minAngle,minAngle+arclen); + } +} + +// animate a ball rolling around and settling at "target" radians +function animateChoice(target) { + var angle = 0; + var speed = 0; + var oldx = -10; + var oldy = -10; + var decelFromAngle = -1; + var allowDecel = false; + for (var i = 0; true; i++) { + angle = angle + speed; + if (angle > radians) angle -= radians; + if (i < animStartSteps || (speed < maxSpeed && !allowDecel)) { + speed = speed + accel; + if (speed > maxSpeed) { + speed = maxSpeed; + /* when we reach max speed, we know how long it takes + * to accelerate, and therefore how long to decelerate, so + * we can work out what angle to start decelerating from */ + if (decelFromAngle < 0) { + decelFromAngle = target-angle; + while (decelFromAngle < 0) decelFromAngle += radians; + while (decelFromAngle > radians) decelFromAngle -= radians; + } + } + } else { + if (!allowDecel && (angle < decelFromAngle) && (angle+speed >= decelFromAngle)) allowDecel = true; + if (allowDecel) speed = speed - accel; + if (speed < minSpeed) speed = minSpeed; + if (speed == minSpeed && angle < target && angle+speed >= target) return; + } + + var r = i/2; + if (r > ballTrack) r = ballTrack; + var x = centreX+Math.cos(angle)*r; + var y = centreY+Math.sin(angle)*r; + g.setColor('#000000'); + g.fillCircle(oldx,oldy,ballSize+1); + g.setColor('#ffffff'); + g.fillCircle(x, y, ballSize); + oldx=x; + oldy=y; + g.flip(); + } +} + +// choose a winning segment and animate its selection +function choose() { + var chosen = Math.floor(Math.random()*N); + var minAngle = (chosen/N)*radians; + var maxAngle = minAngle + arclen; + animateChoice((minAngle+maxAngle)/2); + g.setColor(colours[chosen%colours.length]); + for (var i = segmentMax-segmentStep; i >= 0; i -= segmentStep) + arc(i, perimMax, minAngle, maxAngle); + arc(0, perimMax, minAngle, maxAngle); + for (var r = 1; r < segmentMax; r += circleStep) + g.fillCircle(centreX,centreY,r); + g.fillCircle(centreX,centreY,segmentMax); +} + +// draw the current value of N in the middle of the screen, with +// up/down arrows +function drawN() { + g.setColor(g.theme.fg); + g.setFont("Vector",fontSize); + g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2); + if (N < maxN) + g.fillPoly([centreX-6,centreY-fontSize/2-7, centreX+6,centreY-fontSize/2-7, centreX, centreY-fontSize/2-14]); + if (N > minN) + g.fillPoly([centreX-6,centreY+fontSize/2+5, centreX+6,centreY+fontSize/2+5, centreX, centreY+fontSize/2+12]); +} + +// update number of segments, with min/max limit, "arclen" update, +// and screen reset +function setN(n) { + N = n; + if (N < minN) N = minN; + if (N > maxN) N = maxN; + arclen = radians/N - gapAngle; + drawPerimeter(); +} + +// save N to choozi.txt +function writeN() { + var file = require("Storage").open("choozi.txt","w"); + file.write(N); +} + +// load N from choozi.txt +function readN() { + var file = require("Storage").open("choozi.txt","r"); + var n = file.readLine(); + if (n !== undefined) setN(parseInt(n)); + else setN(defaultN); +} + +shuffle(colours); // is this really best? +Bangle.setLCDTimeout(0); // keep screen on +readN(); +drawN(); + +setWatch(() => { + writeN(); + drawPerimeter(); + choose(); +}, BTN1, {repeat:true}); + +Bangle.on('touch', function(zone,e) { + if(e.x>+88){ + setN(N-1); + drawN(); + }else{ + setN(N+1); + drawN(); + } +}); diff --git a/apps/choozi/metadata.json b/apps/choozi/metadata.json index b75ef062a..79af76fa2 100644 --- a/apps/choozi/metadata.json +++ b/apps/choozi/metadata.json @@ -1,16 +1,17 @@ { "id": "choozi", "name": "Choozi", - "version": "0.01", + "version": "0.03", "description": "Choose people or things at random using Bangle.js.", "icon": "app.png", "tags": "tool", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "allow_emulator": true, "screenshots": [{"url":"bangle1-choozi-screenshot1.png"},{"url":"bangle1-choozi-screenshot2.png"}], "storage": [ - {"name":"choozi.app.js","url":"app.js"}, + {"name":"choozi.app.js","url":"app.js","supports": ["BANGLEJS"]}, + {"name":"choozi.app.js","url":"appb2.js","supports": ["BANGLEJS2"]}, {"name":"choozi.img","url":"app-icon.js","evaluate":true} ] } diff --git a/apps/cliock/metadata.json b/apps/cliock/metadata.json index c5d3fa49e..2df48892e 100644 --- a/apps/cliock/metadata.json +++ b/apps/cliock/metadata.json @@ -9,6 +9,7 @@ "type": "clock", "tags": "clock,cli,command,bash,shell", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"cliock.app.js","url":"app.js"}, diff --git a/apps/clockcal/ChangeLog b/apps/clockcal/ChangeLog index e874c8c67..8b40a87ac 100644 --- a/apps/clockcal/ChangeLog +++ b/apps/clockcal/ChangeLog @@ -1 +1,3 @@ 0.01: Initial upload +0.02: Added scrollable calendar and swipe gestures +0.03: Configurable drag gestures diff --git a/apps/clockcal/README.md b/apps/clockcal/README.md index c19ee54a6..d30205be0 100644 --- a/apps/clockcal/README.md +++ b/apps/clockcal/README.md @@ -3,19 +3,30 @@ This is my "Hello World". I first made this watchface almost 10 years ago for my original Pebble and Pebble Time and I missed this so much, that I had to write it for the BangleJS2. I know that it seems redundant because there already **is** a *time&cal*-app, but it didn't fit my style. -- locked screen with only one minimal update/minute -- ![locked screen](https://foostuff.github.io/BangleApps/apps/clockcal/screenshot.png) -- unlocked screen (twist?) with seconds -- ![unlocked screen](https://foostuff.github.io/BangleApps/apps/clockcal/screenshot2.png) +|Screenshot|description| +|:--:|:-| +|![locked screen](screenshot.png)|locked: triggers only one minimal update/min| +|![unlocked screen](screenshot2.png)|unlocked: smaller clock, but with seconds| +|![big calendar](screenshot3.png)|swipe up for big calendar, (up down to scroll, left/right to exit)| ## Configurable Features - Number of calendar rows (weeks) - Buzz on connect/disconnect (I know, this should be an extra widget, but for now, it is included) -- Clock Mode (24h/12h). Doesn't have an am/pm indicator. It's only there because it was easy. +- Clock Mode (24h/12h). (No am/pm indicator) - First day of the week -- Red Saturday -- Red Sunday +- Red Saturday/Sunday +- Swipe/Drag gestures to launch features or apps. + +## Auto detects your message/music apps: +- swiping down will search your files for an app with the string "message" in its filename and launch it. (configurable) +- swiping right will search your files for an app with the string "music" in its filename and launch it. (configurable) ## Feedback The clock works for me in a 24h/MondayFirst/WeekendFree environment but is not well-tested with other settings. So if something isn't working, please tell me: https://github.com/foostuff/BangleApps/issues + +## Planned features: + - Internal lightweight music control, because switching apps has a loading time. + - Clean up settings + - Maybe am/pm indicator for 12h-users + - Step count (optional) diff --git a/apps/clockcal/app.js b/apps/clockcal/app.js index fc299912f..5e8c7f796 100644 --- a/apps/clockcal/app.js +++ b/apps/clockcal/app.js @@ -4,18 +4,120 @@ var s = Object.assign({ CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually MODE24: true, //24h mode vs 12h mode - FIRSTDAYOFFSET: 6, //First day of the week: 0-6: Sun, Sat, Fri, Thu, Wed, Tue, Mon + FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su REDSUN: true, // Use red color for sunday? REDSAT: true, // Use red color for saturday? + DRAGDOWN: "[AI:messg]", + DRAGRIGHT: "[AI:music]", + DRAGLEFT: "[ignore]", + DRAGUP: "[calend.]" }, require('Storage').readJSON("clockcal.json", true) || {}); const h = g.getHeight(); const w = g.getWidth(); const CELL_W = w / 7; +const CELL2_W = w / 8;//full calendar const CELL_H = 15; const CAL_Y = h - s.CAL_ROWS * CELL_H; const DEBUG = false; +var state = "watch"; +var monthOffset = 0; +/* + * Calendar features + */ +function drawFullCalendar(monthOffset) { + addMonths = function (_d, _am) { + var ay = 0, m = _d.getMonth(), y = _d.getFullYear(); + while ((m + _am) > 11) { ay++; _am -= 12; } + while ((m + _am) < 0) { ay--; _am += 12; } + n = new Date(_d.getTime()); + n.setMonth(m + _am); + n.setFullYear(y + ay); + return n; + }; + monthOffset = (typeof monthOffset == "undefined") ? 0 : monthOffset; + state = "calendar"; + var start = Date().getTime(); + const months = ['Jan.', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec.']; + const monthclr = ['#0f0', '#f0f', '#00f', '#ff0', '#0ff', '#fff']; + if (typeof dayInterval !== "undefined") clearTimeout(dayInterval); + if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); + if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval); + d = addMonths(Date(), monthOffset); + tdy = Date().getDate() + "." + Date().getMonth(); + newmonth = false; + c_y = 0; + g.reset(); + g.setBgColor(0); + g.clear(); + var prevmonth = addMonths(d, -1); + const today = prevmonth.getDate(); + var rD = new Date(prevmonth.getTime()); + rD.setDate(rD.getDate() - (today - 1)); + const dow = (s.FIRSTDAY + rD.getDay()) % 7; + rD.setDate(rD.getDate() - dow); + var rDate = rD.getDate(); + bottomrightY = c_y - 3; + clrsun = s.REDSUN ? '#f00' : '#fff'; + clrsat = s.REDSUN ? '#f00' : '#fff'; + var fg = [clrsun, '#fff', '#fff', '#fff', '#fff', '#fff', clrsat]; + for (var y = 1; y <= 11; y++) { + bottomrightY += CELL_H; + bottomrightX = -2; + for (var x = 1; x <= 7; x++) { + bottomrightX += CELL2_W; + rMonth = rD.getMonth(); + rDate = rD.getDate(); + if (tdy == rDate + "." + rMonth) { + caldrawToday(rDate); + } else if (rDate == 1) { + caldrawFirst(rDate); + } else { + caldrawNormal(rDate, fg[rD.getDay()]); + } + if (newmonth && x == 7) { + caldrawMonth(rDate, monthclr[rMonth % 6], months[rMonth], rD); + } + rD.setDate(rDate + 1); + } + } + delete addMonths; + if (DEBUG) console.log("Calendar performance (ms):" + (Date().getTime() - start)); +} +function caldrawMonth(rDate, c, m, rD) { + g.setColor(c); + g.setFont("Vector", 18); + g.setFontAlign(-1, 1, 1); + drawyear = ((rMonth % 11) == 0) ? String(rD.getFullYear()).substr(-2) : ""; + g.drawString(m + drawyear, bottomrightX, bottomrightY - CELL_H, 1); + newmonth = false; +} +function caldrawToday(rDate) { + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + g.setColor('#0f0'); + g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); + g.setColor('#000'); + g.drawString(rDate, bottomrightX, bottomrightY); +} +function caldrawFirst(rDate) { + g.flip(); + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + bottomrightY += 3; + newmonth = true; + g.setColor('#0ff'); + g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); + g.setColor('#000'); + g.drawString(rDate, bottomrightX, bottomrightY); +} +function caldrawNormal(rDate, c) { + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + g.setColor(c); + g.drawString(rDate, bottomrightX, bottomrightY);//100 +} function drawMinutes() { if (DEBUG) console.log("|-->minutes"); var d = new Date(); @@ -52,15 +154,17 @@ function drawSeconds() { if (!dimSeconds) secondInterval = setTimeout(drawSeconds, 1000); } -function drawCalendar() { +function drawWatch() { if (DEBUG) console.log("CALENDAR"); + monthOffset = 0; + state = "watch"; var d = new Date(); g.reset(); g.setBgColor(0); g.clear(); drawMinutes(); if (!dimSeconds) drawSeconds(); - const dow = (s.FIRSTDAYOFFSET + d.getDay()) % 7; //MO=0, SU=6 + const dow = (s.FIRSTDAY + d.getDay()) % 7; //MO=0, SU=6 const today = d.getDate(); var rD = new Date(d.getTime()); rD.setDate(rD.getDate() - dow); @@ -91,7 +195,7 @@ function drawCalendar() { var nextday = (3600 * 24) - (d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds() + 1); if (DEBUG) console.log("Next Day:" + (nextday / 3600)); if (typeof dayInterval !== "undefined") clearTimeout(dayInterval); - dayInterval = setTimeout(drawCalendar, nextday * 1000); + dayInterval = setTimeout(drawWatch, nextday * 1000); } function BTevent() { @@ -102,18 +206,105 @@ function BTevent() { setTimeout(function () { Bangle.buzz(interval); }, interval * 3); } } +function action(a) { + g.reset(); + if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); + if (DEBUG) console.log("action:" + a); + switch (a) { + case "[ignore]": + break; + case "[calend.]": + drawFullCalendar(); + break; + case "[AI:music]": + l = require("Storage").list(RegExp("music.*app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert("Music app not found", "Not found").then(drawWatch); + break; + case "[AI:messg]": + l = require("Storage").list(RegExp("message.*app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert("Message app not found", "Not found").then(drawWatch); + break; + default: + l = require("Storage").list(RegExp(a + ".app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert(a + ": App not found", "Not found").then(drawWatch); + break; + } +} +function input(dir) { + Bangle.buzz(100, 1); + if (DEBUG) console.log("swipe:" + dir); + switch (dir) { + case "r": + if (state == "calendar") { + drawWatch(); + } else { + action(s.DRAGRIGHT); + } + break; + case "l": + if (state == "calendar") { + drawWatch(); + } else { + action(s.DRAGLEFT); + } + break; + case "d": + if (state == "calendar") { + monthOffset--; + drawFullCalendar(monthOffset); + } else { + action(s.DRAGDOWN); + } + break; + case "u": + if (state == "calendar") { + monthOffset++; + drawFullCalendar(monthOffset); + } else { + action(s.DRAGUP); + } + break; + default: + if (state == "calendar") { + drawWatch(); + } + break; + + } +} + +let drag; +Bangle.on("drag", e => { + if (!drag) { + drag = { x: e.x, y: e.y }; + } else if (!e.b) { + const dx = e.x - drag.x, dy = e.y - drag.y; + var dir = "t"; + if (Math.abs(dx) > Math.abs(dy) + 20) { + dir = (dx > 0) ? "r" : "l"; + } else if (Math.abs(dy) > Math.abs(dx) + 20) { + dir = (dy > 0) ? "d" : "u"; + } + drag = null; + input(dir); + } +}); //register events Bangle.on('lock', locked => { if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); dimSeconds = locked; //dim seconds if lock=on - drawCalendar(); + drawWatch(); }); NRF.on('connect', BTevent); NRF.on('disconnect', BTevent); - dimSeconds = Bangle.isLocked(); -drawCalendar(); - +drawWatch(); Bangle.setUI("clock"); diff --git a/apps/clockcal/metadata.json b/apps/clockcal/metadata.json index ccc84a980..3998215d7 100644 --- a/apps/clockcal/metadata.json +++ b/apps/clockcal/metadata.json @@ -1,7 +1,7 @@ { "id": "clockcal", "name": "Clock & Calendar", - "version": "0.01", + "version": "0.03", "description": "Clock with Calendar", "readme":"README.md", "icon": "app.png", diff --git a/apps/clockcal/screenshot3.png b/apps/clockcal/screenshot3.png new file mode 100644 index 000000000..ab34f4306 Binary files /dev/null and b/apps/clockcal/screenshot3.png differ diff --git a/apps/clockcal/settings.js b/apps/clockcal/settings.js index cc2a78181..abedad99b 100644 --- a/apps/clockcal/settings.js +++ b/apps/clockcal/settings.js @@ -1,16 +1,22 @@ (function (back) { var FILE = "clockcal.json"; - - settings = Object.assign({ + defaults={ CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually MODE24: true, //24h mode vs 12h mode FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su REDSUN: true, // Use red color for sunday? REDSAT: true, // Use red color for saturday? - }, require('Storage').readJSON(FILE, true) || {}); - + DRAGDOWN: "[AI:messg]", + DRAGRIGHT: "[AI:music]", + DRAGLEFT: "[ignore]", + DRAGUP: "[calend.]" + }; + settings = Object.assign(defaults, require('Storage').readJSON(FILE, true) || {}); + actions = ["[ignore]","[calend.]","[AI:music]","[AI:messg]"]; + require("Storage").list(RegExp(".app.js")).forEach(element => actions.push(element.replace(".app.js",""))); + function writeSettings() { require('Storage').writeJSON(FILE, settings); } @@ -67,26 +73,55 @@ writeSettings(); } }, + 'Drag Up ': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGUP), + format: v => actions[v], + onchange: v => { + settings.DRAGUP = actions[v]; + writeSettings(); + } + }, + 'Drag Right': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGRIGHT), + format: v => actions[v], + onchange: v => { + settings.DRAGRIGHT = actions[v]; + writeSettings(); + } + }, + 'Drag Down': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGDOWN), + format: v => actions[v], + onchange: v => { + settings.DRGDOWN = actions[v]; + writeSettings(); + } + }, + 'Drag Left': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGLEFT), + format: v => actions[v], + onchange: v => { + settings.DRAGLEFT = actions[v]; + writeSettings(); + } + }, 'Load deafauls?': { value: 0, min: 0, max: 1, format: v => ["No", "Yes"][v], onchange: v => { if (v == 1) { - settings = { - CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. - BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. - MODE24: true, //24h mode vs 12h mode - FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su - REDSUN: true, // Use red color for sunday? - REDSAT: true, // Use red color for saturday? - }; + settings = defaults; writeSettings(); - load() + load(); } } }, - } + }; // Show the menu E.showMenu(menu); -}) +}); diff --git a/apps/compass/ChangeLog b/apps/compass/ChangeLog index 4bb7838ac..d1adafc4c 100644 --- a/apps/compass/ChangeLog +++ b/apps/compass/ChangeLog @@ -3,3 +3,4 @@ 0.03: Eliminate flickering 0.04: Fix for Bangle.js 2 and themes 0.05: Fix bearing not clearing correctly (visible in single or double digit bearings) +0.06: Add button for force compass calibration diff --git a/apps/compass/README.md b/apps/compass/README.md new file mode 100644 index 000000000..d60192f73 --- /dev/null +++ b/apps/compass/README.md @@ -0,0 +1,29 @@ +# Compass + +This app uses Bangle.js's built-in magnetometer as a compass. + +## Usage + +Hold your Bangle.js **face up** (so the display is parallel to the ground), +and the red arrow will point north, with the heading in degrees printed at +the top of the screen. + +This compass app does not include tilt compensation - so much like a real +compass you should always keep it face up when taking a reading. + +The first time you run the compass after your Bangle has booted (or if +you move to an area with a substantially different magnetic field) you will +need to recalibrate your compass (even if a heading is shown). + +## Calibration + +Press the button next to the `RESET` label on the screen. The North/South marker +will disappear and a message will appear asking you to rotate the watch 360 degrees. + +* Hold the watch face up, so the display is parallel to the ground +* Rotate it around slowly, all 360 degrees (with the display still parallel to the ground) +* The `Uncalibrated` message will disappear before you have finished rotating the full 360 degrees - but you should still complete the full rotation in order for the compass to work properly. + +Once you've rotated the full 360 degrees your compass should now work fine, +and calibration is stored between runs of the app. However if you go near +to a strong magnet you may still need to recalibrate. diff --git a/apps/compass/compass.js b/apps/compass/compass.js index 65ad83c4f..4730111ac 100644 --- a/apps/compass/compass.js +++ b/apps/compass/compass.js @@ -38,7 +38,7 @@ Bangle.on('mag', function(m) { if (!wasUncalibrated) { g.clearRect(0,24,W,48); g.setFontAlign(0,-1).setFont("6x8"); - g.drawString("Uncalibrated\nturn 360° around",M,24+4); + g.drawString(/*LANG*/"Uncalibrated\nturn 360° around",M,24+4); wasUncalibrated = true; } } else { @@ -64,7 +64,12 @@ Bangle.on('mag', function(m) { oldHeading = m.heading; }); -g.clear(); +g.clear(1); +g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"RESET", g.getWidth()-5, g.getHeight()/2); +setWatch(function() { + Bangle.resetCompass(); +}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true}); + Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.setCompassPower(1); diff --git a/apps/compass/metadata.json b/apps/compass/metadata.json index 318d90c86..3e3b37f72 100644 --- a/apps/compass/metadata.json +++ b/apps/compass/metadata.json @@ -1,12 +1,13 @@ { "id": "compass", "name": "Compass", - "version": "0.05", + "version": "0.06", "description": "Simple compass that points North", "icon": "compass.png", "screenshots": [{"url":"screenshot_compass.png"}], "tags": "tool,outdoors", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "storage": [ {"name":"compass.app.js","url":"compass.js"}, {"name":"compass.img","url":"compass-icon.js","evaluate":true} diff --git a/apps/contourclock/ChangeLog b/apps/contourclock/ChangeLog index 032edc9b5..d415a604d 100644 --- a/apps/contourclock/ChangeLog +++ b/apps/contourclock/ChangeLog @@ -6,3 +6,4 @@ 0.24: Added previews to the customizer. 0.25: Fixed a bug that would let widgets change the color of the clock. 0.26: Time formatted to locale +0.27: Fixed the timing code, which sometimes did not update for one minute diff --git a/apps/contourclock/app.js b/apps/contourclock/app.js index cdfadd217..d5c97edfa 100644 --- a/apps/contourclock/app.js +++ b/apps/contourclock/app.js @@ -7,6 +7,13 @@ if (settings.fontIndex==undefined) { require('Storage').writeJSON("myapp.json", settings); } +function queueDraw() { + setTimeout(function() { + draw(); + queueDraw(); + }, 60000 - (Date.now() % 60000)); +} + function draw() { var date = new Date(); // Draw day of the week @@ -24,7 +31,5 @@ Bangle.setUI("clock"); g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); +queueDraw(); draw(); -setTimeout(function() { - setInterval(draw,60000); -}, 60000 - Date.now() % 60000); diff --git a/apps/contourclock/metadata.json b/apps/contourclock/metadata.json index ec29c390b..eb0dd39fb 100644 --- a/apps/contourclock/metadata.json +++ b/apps/contourclock/metadata.json @@ -1,7 +1,7 @@ { "id": "contourclock", "name": "Contour Clock", "shortName" : "Contour Clock", - "version":"0.26", + "version":"0.27", "icon": "app.png", "description": "A Minimalist clockface with large Digits. Now with more fonts!", "screenshots" : [{"url":"cc-screenshot-1.png"},{"url":"cc-screenshot-2.png"}], diff --git a/apps/crowclk/ChangeLog b/apps/crowclk/ChangeLog index b7e18abe3..4f48bdd14 100644 --- a/apps/crowclk/ChangeLog +++ b/apps/crowclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up". +0.03: Fix the clock for dark mode. diff --git a/apps/crowclk/crow_clock.js b/apps/crowclk/crow_clock.js index d06369fa8..eee1653cb 100644 --- a/apps/crowclk/crow_clock.js +++ b/apps/crowclk/crow_clock.js @@ -76,7 +76,7 @@ function draw_clock(){ // g.drawLine(clock_center.x - radius, clock_center.y, clock_center.x + radius, clock_center.y); // g.drawLine(clock_center.x, clock_center.y - radius, clock_center.x, clock_center.y + radius); - g.setColor(g.theme.fg); + g.setColor(g.theme.dark ? g.theme.bg : g.theme.fg); let ticks = [0, 90, 180, 270]; ticks.forEach((item)=>{ let agl = item+180; @@ -92,13 +92,13 @@ function draw_clock(){ let minute_agl = minute_angle(date); g.drawImage(hour_hand, hour_pos_x(hour_agl), hour_pos_y(hour_agl), {rotate:hour_agl*p180}); // g.drawImage(minute_hand, minute_pos_x(minute_agl), minute_pos_y(minute_agl), {rotate:minute_agl*p180}); // - g.setColor(g.theme.fg); + g.setColor(g.theme.dark ? g.theme.bg : g.theme.fg); g.fillCircle(clock_center.x, clock_center.y, 6); - g.setColor(g.theme.bg); + g.setColor(g.theme.dark ? g.theme.fg : g.theme.bg); g.fillCircle(clock_center.x, clock_center.y, 3); // draw minute ticks. Takes long time to draw! - g.setColor(g.theme.fg); + g.setColor(g.theme.dark ? g.theme.bg : g.theme.fg); for (var i=0; i<60; i++){ let agl = i*6+180; g.drawImage(tick1.asImage(), rotate_around_x(big_wheel_x(i*6), agl, tick1), rotate_around_y(big_wheel_y(i*6), agl, tick1), {rotate:agl*p180}); diff --git a/apps/crowclk/metadata.json b/apps/crowclk/metadata.json index 752e30fb0..6985cf11a 100644 --- a/apps/crowclk/metadata.json +++ b/apps/crowclk/metadata.json @@ -1,7 +1,7 @@ { "id": "crowclk", "name": "Crow Clock", - "version": "0.02", + "version": "0.03", "description": "A simple clock based on Bold Clock that has MST3K's Crow T. Robot for a face", "icon": "crow_clock.png", "screenshots": [{"url":"screenshot_crow.png"}], diff --git a/apps/cscsensor/ChangeLog b/apps/cscsensor/ChangeLog index 8f23fa9f3..a98be5c0f 100644 --- a/apps/cscsensor/ChangeLog +++ b/apps/cscsensor/ChangeLog @@ -5,3 +5,4 @@ 0.05: Add cadence sensor support 0.06: Now read wheel rev as well as cadence sensor Improve connection code +0.07: Make Bangle.js 2 compatible diff --git a/apps/cscsensor/README.md b/apps/cscsensor/README.md index 9740fd9cf..3828e8e3e 100644 --- a/apps/cscsensor/README.md +++ b/apps/cscsensor/README.md @@ -11,9 +11,9 @@ Currently the app displays the following data: - total distance traveled - an icon with the battery status of the remote sensor -Button 1 resets all measurements except total distance traveled. The latter gets preserved by being written to storage every 0.1 miles and upon exiting the app. -If the watch app has not received an update from the sensor for at least 10 seconds, pushing button 3 will attempt to reconnect to the sensor. -Button 2 switches between the display for cycling speed and cadence. +Button 1 (swipe up on Bangle.js 2) resets all measurements except total distance traveled. The latter gets preserved by being written to storage every 0.1 miles and upon exiting the app. +If the watch app has not received an update from the sensor for at least 10 seconds, pushing button 3 (swipe down on Bangle.js 2) will attempt to reconnect to the sensor. +Button 2 (tap on Bangle.js 2) switches between the display for cycling speed and cadence. Values displayed are imperial or metric (depending on locale), cadence is in RPM, the wheel circumference can be adjusted in the global settings app. diff --git a/apps/cscsensor/cscsensor.app.js b/apps/cscsensor/cscsensor.app.js index e2af0db16..4ebe7d57e 100644 --- a/apps/cscsensor/cscsensor.app.js +++ b/apps/cscsensor/cscsensor.app.js @@ -7,6 +7,11 @@ const SETTINGS_FILE = 'cscsensor.json'; const storage = require('Storage'); const W = g.getWidth(); const H = g.getHeight(); +const yStart = 48; +const rowHeight = (H-yStart)/6; +const yCol1 = W/2.7586; +const fontSizeLabel = W/12.632; +const fontSizeValue = W/9.2308; class CSCSensor { constructor() { @@ -22,7 +27,6 @@ class CSCSensor { this.speed = 0; this.maxSpeed = 0; this.lastSpeed = 0; - this.qUpdateScreen = true; this.lastRevsStart = -1; this.qMetric = !require("locale").speed(1).toString().endsWith("mph"); this.speedUnit = this.qMetric ? "km/h" : "mph"; @@ -49,6 +53,7 @@ class CSCSensor { toggleDisplayCadence() { this.showCadence = !this.showCadence; this.screenInit = true; + g.setBgColor(0, 0, 0); } setBatteryLevel(level) { @@ -63,14 +68,16 @@ class CSCSensor { } drawBatteryIcon() { - g.setColor(1, 1, 1).drawRect(10, 55, 20, 75).fillRect(14, 53, 16, 55).setColor(0).fillRect(11, 56, 19, 74); + g.setColor(1, 1, 1).drawRect(10*W/240, yStart+0.029167*H, 20*W/240, yStart+0.1125*H) + .fillRect(14*W/240, yStart+0.020833*H, 16*W/240, yStart+0.029167*H) + .setColor(0).fillRect(11*W/240, yStart+0.033333*H, 19*W/240, yStart+0.10833*H); if (this.batteryLevel!=-1) { if (this.batteryLevel<25) g.setColor(1, 0, 0); else if (this.batteryLevel<50) g.setColor(1, 0.5, 0); else g.setColor(0, 1, 0); - g.fillRect(11, 74-18*this.batteryLevel/100, 19, 74); + g.fillRect(11*W/240, (yStart+0.10833*H)-18*this.batteryLevel/100, 19*W/240, yStart+0.10833*H); } - else g.setFontVector(14).setFontAlign(0, 0, 0).setColor(0xffff).drawString("?", 16, 66); + else g.setFontVector(W/17.143).setFontAlign(0, 0, 0).setColor(0xffff).drawString("?", 16*W/240, yStart+0.075*H); } updateScreenRevs() { @@ -88,36 +95,36 @@ class CSCSensor { for (var i=0; i<6; ++i) { if ((i&1)==0) g.setColor(0, 0, 0); else g.setColor(0x30cd); - g.fillRect(0, 48+i*32, 86, 48+(i+1)*32); + g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight); if ((i&1)==1) g.setColor(0); else g.setColor(0x30cd); - g.fillRect(87, 48+i*32, 239, 48+(i+1)*32); - g.setColor(0.5, 0.5, 0.5).drawRect(87, 48+i*32, 239, 48+(i+1)*32).drawLine(0, 239, 239, 239);//.drawRect(0, 48, 87, 239); - g.moveTo(0, 80).lineTo(30, 80).lineTo(30, 48).lineTo(87, 48).lineTo(87, 239).lineTo(0, 239).lineTo(0, 80); + g.fillRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight); + g.setColor(0.5, 0.5, 0.5).drawRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight).drawLine(0, H-1, W-1, H-1); + g.moveTo(0, yStart+0.13333*H).lineTo(30*W/240, yStart+0.13333*H).lineTo(30*W/240, yStart).lineTo(yCol1, yStart).lineTo(yCol1, H-1).lineTo(0, H-1).lineTo(0, yStart+0.13333*H); } - g.setFontAlign(1, 0, 0).setFontVector(19).setColor(1, 1, 0); - g.drawString("Time:", 87, 66); - g.drawString("Speed:", 87, 98); - g.drawString("Ave spd:", 87, 130); - g.drawString("Max spd:", 87, 162); - g.drawString("Trip:", 87, 194); - g.drawString("Total:", 87, 226); + g.setFontAlign(1, 0, 0).setFontVector(fontSizeLabel).setColor(1, 1, 0); + g.drawString("Time:", yCol1, yStart+rowHeight/2+0*rowHeight); + g.drawString("Speed:", yCol1, yStart+rowHeight/2+1*rowHeight); + g.drawString("Avg spd:", yCol1, yStart+rowHeight/2+2*rowHeight); + g.drawString("Max spd:", yCol1, yStart+rowHeight/2+3*rowHeight); + g.drawString("Trip:", yCol1, yStart+rowHeight/2+4*rowHeight); + g.drawString("Total:", yCol1, yStart+rowHeight/2+5*rowHeight); this.drawBatteryIcon(); this.screenInit = false; } - g.setFontAlign(-1, 0, 0).setFontVector(26); - g.setColor(0x30cd).fillRect(88, 49, 238, 79); - g.setColor(0xffff).drawString(dmins+":"+dsecs, 92, 66); - g.setColor(0).fillRect(88, 81, 238, 111); - g.setColor(0xffff).drawString(dspeed+" "+this.speedUnit, 92, 98); - g.setColor(0x30cd).fillRect(88, 113, 238, 143); - g.setColor(0xffff).drawString(avespeed + " " + this.speedUnit, 92, 130); - g.setColor(0).fillRect(88, 145, 238, 175); - g.setColor(0xffff).drawString(maxspeed + " " + this.speedUnit, 92, 162); - g.setColor(0x30cd).fillRect(88, 177, 238, 207); - g.setColor(0xffff).drawString(ddist + " " + this.distUnit, 92, 194); - g.setColor(0).fillRect(88, 209, 238, 238); - g.setColor(0xffff).drawString(tdist + " " + this.distUnit, 92, 226); + g.setFontAlign(-1, 0, 0).setFontVector(fontSizeValue); + g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*0, 238, 47+1*rowHeight); + g.setColor(0xffff).drawString(dmins+":"+dsecs, yCol1+5, 50+rowHeight/2+0*rowHeight); + g.setColor(0).fillRect(yCol1+1, 49+rowHeight*1, 238, 47+2*rowHeight); + g.setColor(0xffff).drawString(dspeed+" "+this.speedUnit, yCol1+5, 50+rowHeight/2+1*rowHeight); + g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*2, 238, 47+3*rowHeight); + g.setColor(0xffff).drawString(avespeed + " " + this.speedUnit, yCol1+5, 50+rowHeight/2+2*rowHeight); + g.setColor(0).fillRect(yCol1+1, 49+rowHeight*3, 238, 47+4*rowHeight); + g.setColor(0xffff).drawString(maxspeed + " " + this.speedUnit, yCol1+5, 50+rowHeight/2+3*rowHeight); + g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*4, 238, 47+5*rowHeight); + g.setColor(0xffff).drawString(ddist + " " + this.distUnit, yCol1+5, 50+rowHeight/2+4*rowHeight); + g.setColor(0).fillRect(yCol1+1, 49+rowHeight*5, 238, 47+6*rowHeight); + g.setColor(0xffff).drawString(tdist + " " + this.distUnit, yCol1+5, 50+rowHeight/2+5*rowHeight); } updateScreenCadence() { @@ -125,21 +132,21 @@ class CSCSensor { for (var i=0; i<2; ++i) { if ((i&1)==0) g.setColor(0, 0, 0); else g.setColor(0x30cd); - g.fillRect(0, 48+i*32, 86, 48+(i+1)*32); + g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight); if ((i&1)==1) g.setColor(0); else g.setColor(0x30cd); - g.fillRect(87, 48+i*32, 239, 48+(i+1)*32); - g.setColor(0.5, 0.5, 0.5).drawRect(87, 48+i*32, 239, 48+(i+1)*32).drawLine(0, 239, 239, 239);//.drawRect(0, 48, 87, 239); - g.moveTo(0, 80).lineTo(30, 80).lineTo(30, 48).lineTo(87, 48).lineTo(87, 239).lineTo(0, 239).lineTo(0, 80); + g.fillRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight); + g.setColor(0.5, 0.5, 0.5).drawRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight).drawLine(0, H-1, W-1, H-1); + g.moveTo(0, yStart+0.13333*H).lineTo(30*W/240, yStart+0.13333*H).lineTo(30*W/240, yStart).lineTo(yCol1, yStart).lineTo(yCol1, H-1).lineTo(0, H-1).lineTo(0, yStart+0.13333*H); } - g.setFontAlign(1, 0, 0).setFontVector(19).setColor(1, 1, 0); - g.drawString("Cadence:", 87, 98); + g.setFontAlign(1, 0, 0).setFontVector(fontSizeLabel).setColor(1, 1, 0); + g.drawString("Cadence:", yCol1, yStart+rowHeight/2+1*rowHeight); this.drawBatteryIcon(); this.screenInit = false; } - g.setFontAlign(-1, 0, 0).setFontVector(26); - g.setColor(0).fillRect(88, 81, 238, 111); - g.setColor(0xffff).drawString(Math.round(this.cadence), 92, 98); + g.setFontAlign(-1, 0, 0).setFontVector(fontSizeValue); + g.setColor(0).fillRect(yCol1+1, 49+rowHeight*1, 238, 47+2*rowHeight); + g.setColor(0xffff).drawString(Math.round(this.cadence), yCol1+5, 50+rowHeight/2+1*rowHeight); } updateScreen() { @@ -163,45 +170,45 @@ class CSCSensor { } this.lastCrankRevs = crankRevs; this.lastCrankTime = crankTime; - } - // wheel revolution - var wheelRevs = event.target.value.getUint32(1, true); - var dRevs = (this.lastRevs>0 ? wheelRevs-this.lastRevs : 0); - if (dRevs>0) { - qChanged = true; - this.totaldist += dRevs*this.wheelCirc/63360.0; - if ((this.totaldist-this.settings.totaldist)>0.1) { - this.settings.totaldist = this.totaldist; - storage.writeJSON(SETTINGS_FILE, this.settings); + } else { + // wheel revolution + var wheelRevs = event.target.value.getUint32(1, true); + var dRevs = (this.lastRevs>0 ? wheelRevs-this.lastRevs : 0); + if (dRevs>0) { + qChanged = true; + this.totaldist += dRevs*this.wheelCirc/63360.0; + if ((this.totaldist-this.settings.totaldist)>0.1) { + this.settings.totaldist = this.totaldist; + storage.writeJSON(SETTINGS_FILE, this.settings); + } } - } - this.lastRevs = wheelRevs; - if (this.lastRevsStart<0) this.lastRevsStart = wheelRevs; - var wheelTime = event.target.value.getUint16(5, true); - var dT = (wheelTime-this.lastTime)/1024; - var dBT = (Date.now()-this.lastBangleTime)/1000; - this.lastBangleTime = Date.now(); - if (dT<0) dT+=64; - if (Math.abs(dT-dBT)>3) dT = dBT; - this.lastTime = wheelTime; - this.speed = this.lastSpeed; - if (dRevs>0 && dT>0) { - this.speed = (dRevs*this.wheelCirc/63360.0)*3600/dT; - this.speedFailed = 0; - this.movingTime += dT; - } - else { - this.speedFailed++; - qChanged = false; - if (this.speedFailed>3) { - this.speed = 0; - qChanged = (this.lastSpeed>0); + this.lastRevs = wheelRevs; + if (this.lastRevsStart<0) this.lastRevsStart = wheelRevs; + var wheelTime = event.target.value.getUint16(5, true); + var dT = (wheelTime-this.lastTime)/1024; + var dBT = (Date.now()-this.lastBangleTime)/1000; + this.lastBangleTime = Date.now(); + if (dT<0) dT+=64; + if (Math.abs(dT-dBT)>3) dT = dBT; + this.lastTime = wheelTime; + this.speed = this.lastSpeed; + if (dRevs>0 && dT>0) { + this.speed = (dRevs*this.wheelCirc/63360.0)*3600/dT; + this.speedFailed = 0; + this.movingTime += dT; + } else if (!this.showCadence) { + this.speedFailed++; + qChanged = false; + if (this.speedFailed>3) { + this.speed = 0; + qChanged = (this.lastSpeed>0); + } } + this.lastSpeed = this.speed; + if (this.speed>this.maxSpeed && (this.movingTime>3 || this.speed<20) && this.speed<50) this.maxSpeed = this.speed; } - this.lastSpeed = this.speed; - if (this.speed>this.maxSpeed && (this.movingTime>3 || this.speed<20) && this.speed<50) this.maxSpeed = this.speed; } - if (qChanged && this.qUpdateScreen) this.updateScreen(); + if (qChanged) this.updateScreen(); } } @@ -253,9 +260,9 @@ E.on('kill',()=>{ }); NRF.on('disconnect', connection_setup); // restart if disconnected Bangle.setUI("updown", d=>{ - if (d<0) { mySensor.reset(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } - if (d==0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); } - if (d>0) { mySensor.toggleDisplayCadence(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } + if (d<0) { mySensor.reset(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); } + else if (d>0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); } + else { mySensor.toggleDisplayCadence(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); } }); Bangle.loadWidgets(); diff --git a/apps/cscsensor/metadata.json b/apps/cscsensor/metadata.json index af338c59e..4006789ef 100644 --- a/apps/cscsensor/metadata.json +++ b/apps/cscsensor/metadata.json @@ -2,11 +2,11 @@ "id": "cscsensor", "name": "Cycling speed sensor", "shortName": "CSCSensor", - "version": "0.06", + "version": "0.07", "description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch", "icon": "icons8-cycling-48.png", "tags": "outdoors,exercise,ble,bluetooth", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"cscsensor.app.js","url":"cscsensor.app.js"}, diff --git a/apps/custom/custom.html b/apps/custom/custom.html index 684f813ae..307f2fd2f 100644 --- a/apps/custom/custom.html +++ b/apps/custom/custom.html @@ -16,12 +16,12 @@

Type your javascript code here

-

Then click

+

Then click  

diff --git a/apps/cycling/ChangeLog b/apps/cycling/ChangeLog new file mode 100644 index 000000000..ec66c5568 --- /dev/null +++ b/apps/cycling/ChangeLog @@ -0,0 +1 @@ +0.01: Initial version diff --git a/apps/cycling/README.md b/apps/cycling/README.md new file mode 100644 index 000000000..7ba8ee224 --- /dev/null +++ b/apps/cycling/README.md @@ -0,0 +1,34 @@ +# Cycling +> Displays data from a BLE Cycling Speed and Cadence sensor. + +*This is a fork of the CSCSensor app using the layout library and separate module for CSC functionality. It also drops persistence of total distance on the Bangle, as this information is also persisted on the sensor itself. Further, it allows configuration of display units (metric/imperial) independent of chosen locale. Finally, multiple sensors can be used and wheel circumference can be configured for each sensor individually.* + +The following data are displayed: +- curent speed +- moving time +- average speed +- maximum speed +- trip distance +- total distance + +Other than in the original version of the app, total distance is not stored on the Bangle, but instead is calculated from the CWR (cumulative wheel revolutions) reported by the sensor. This metric is, according to the BLE spec, an absolute value that persists throughout the lifetime of the sensor and never rolls over. + +**Cadence / Crank features are currently not implemented** + +## Usage +Open the app and connect to a CSC sensor. + +Upon first connection, close the app afain and enter the settings app to configure the wheel circumference. The total circumference is (cm + mm) - it is split up into two values for ease of configuration. Check the status screen inside the Cycling app while connected to see the address of the currently connected sensor (if you need to differentiate between multiple sensors). + +Inside the Cycling app, use button / tap screen to: +- cycle through screens (if connected) +- reconnect (if connection aborted) + +## TODO +* Sensor battery status +* Implement crank events / show cadence +* Bangle.js 1 compatibility +* Allow setting CWR on the sensor (this is a feature intended by the BLE CSC spec, in case the sensor is replaced or transferred to a different bike) + +## Development +There is a "mock" version of the `blecsc` module, which can be used to test features in the emulator. Check `blecsc-emu.js` for usage. diff --git a/apps/cycling/blecsc-emu.js b/apps/cycling/blecsc-emu.js new file mode 100644 index 000000000..ca5058545 --- /dev/null +++ b/apps/cycling/blecsc-emu.js @@ -0,0 +1,111 @@ +// UUID of the Bluetooth CSC Service +const SERVICE_UUID = "1816"; +// UUID of the CSC measurement characteristic +const MEASUREMENT_UUID = "2a5b"; + +// Wheel revolution present bit mask +const FLAGS_WREV_BM = 0x01; +// Crank revolution present bit mask +const FLAGS_CREV_BM = 0x02; + +/** + * Fake BLECSC implementation for the emulator, where it's hard to test + * with actual hardware. Generates "random" wheel events (no crank). + * + * To upload as a module, paste the entire file in the console using this + * command: require("Storage").write("blecsc-emu",``); + */ +class BLECSCEmulator { + constructor() { + this.timeout = undefined; + this.interval = 500; + this.ccr = 0; + this.lwt = 0; + this.handlers = { + // value + // disconnect + // wheelEvent + // crankEvent + }; + } + + getDeviceAddress() { + return 'fa:ke:00:de:vi:ce'; + } + + /** + * Callback for the GATT characteristicvaluechanged event. + * Consumers must not call this method! + */ + onValue(event) { + // Not interested in non-CSC characteristics + if (event.target.uuid != "0x" + MEASUREMENT_UUID) return; + + // Notify the generic 'value' handler + if (this.handlers.value) this.handlers.value(event); + + const flags = event.target.value.getUint8(0, true); + // Notify the 'wheelEvent' handler + if ((flags & FLAGS_WREV_BM) && this.handlers.wheelEvent) this.handlers.wheelEvent({ + cwr: event.target.value.getUint32(1, true), // cumulative wheel revolutions + lwet: event.target.value.getUint16(5, true), // last wheel event time + }); + + // Notify the 'crankEvent' handler + if ((flags & FLAGS_CREV_BM) && this.handlers.crankEvent) this.handlers.crankEvent({ + ccr: event.target.value.getUint16(7, true), // cumulative crank revolutions + lcet: event.target.value.getUint16(9, true), // last crank event time + }); + } + + /** + * Register an event handler. + * + * @param {string} event value|disconnect + * @param {function} handler handler function that receives the event as its first argument + */ + on(event, handler) { + this.handlers[event] = handler; + } + + fakeEvent() { + this.interval = Math.max(50, Math.min(1000, this.interval + Math.random()*40-20)); + this.lwt = (this.lwt + this.interval) % 0x10000; + this.ccr++; + + var buffer = new ArrayBuffer(8); + var view = new DataView(buffer); + view.setUint8(0, 0x01); // Wheel revolution data present bit + view.setUint32(1, this.ccr, true); // Cumulative crank revolutions + view.setUint16(5, this.lwt, true); // Last wheel event time + + this.onValue({ + target: { + uuid: "0x2a5b", + value: view, + }, + }); + + this.timeout = setTimeout(this.fakeEvent.bind(this), this.interval); + } + + /** + * Find and connect to a device which exposes the CSC service. + * + * @return {Promise} + */ + connect() { + this.timeout = setTimeout(this.fakeEvent.bind(this), this.interval); + return Promise.resolve(true); + } + + /** + * Disconnect the device. + */ + disconnect() { + if (!this.timeout) return; + clearTimeout(this.timeout); + } +} + +exports = BLECSCEmulator; diff --git a/apps/cycling/blecsc.js b/apps/cycling/blecsc.js new file mode 100644 index 000000000..7a47108e5 --- /dev/null +++ b/apps/cycling/blecsc.js @@ -0,0 +1,150 @@ +const SERVICE_UUID = "1816"; +// UUID of the CSC measurement characteristic +const MEASUREMENT_UUID = "2a5b"; + +// Wheel revolution present bit mask +const FLAGS_WREV_BM = 0x01; +// Crank revolution present bit mask +const FLAGS_CREV_BM = 0x02; + +/** + * This class communicates with a Bluetooth CSC peripherial using the Espruino NRF library. + * + * ## Usage: + * 1. Register event handlers using the \`on(eventName, handlerFunction)\` method + * You can subscribe to the \`wheelEvent\` and \`crankEvent\` events or you can + * have raw characteristic values passed through using the \`value\` event. + * 2. Search and connect to a BLE CSC peripherial by calling the \`connect()\` method + * 3. To tear down the connection, call the \`disconnect()\` method + * + * ## Events + * - \`wheelEvent\` - the peripharial sends a notification containing wheel event data + * - \`crankEvent\` - the peripharial sends a notification containing crank event data + * - \`value\` - the peripharial sends any CSC characteristic notification (including wheel & crank event) + * - \`disconnect\` - the peripherial ends the connection or the connection is lost + * + * Each event can only have one handler. Any call to \`on()\` will + * replace a previously registered handler for the same event. + */ +class BLECSC { + constructor() { + this.device = undefined; + this.ccInterval = undefined; + this.gatt = undefined; + this.handlers = { + // wheelEvent + // crankEvent + // value + // disconnect + }; + } + + getDeviceAddress() { + if (!this.device || !this.device.id) + return '00:00:00:00:00:00'; + return this.device.id.split(" ")[0]; + } + + checkConnection() { + if (!this.device) + console.log("no device"); + // else + // console.log("rssi: " + this.device.rssi); + } + + /** + * Callback for the GATT characteristicvaluechanged event. + * Consumers must not call this method! + */ + onValue(event) { + // Not interested in non-CSC characteristics + if (event.target.uuid != "0x" + MEASUREMENT_UUID) return; + + // Notify the generic 'value' handler + if (this.handlers.value) this.handlers.value(event); + + const flags = event.target.value.getUint8(0, true); + // Notify the 'wheelEvent' handler + if ((flags & FLAGS_WREV_BM) && this.handlers.wheelEvent) this.handlers.wheelEvent({ + cwr: event.target.value.getUint32(1, true), // cumulative wheel revolutions + lwet: event.target.value.getUint16(5, true), // last wheel event time + }); + + // Notify the 'crankEvent' handler + if ((flags & FLAGS_CREV_BM) && this.handlers.crankEvent) this.handlers.crankEvent({ + ccr: event.target.value.getUint16(7, true), // cumulative crank revolutions + lcet: event.target.value.getUint16(9, true), // last crank event time + }); + } + + /** + * Callback for the NRF disconnect event. + * Consumers must not call this method! + */ + onDisconnect(event) { + console.log("disconnected"); + if (this.ccInterval) + clearInterval(this.ccInterval); + + if (!this.handlers.disconnect) return; + this.handlers.disconnect(event); + } + + /** + * Register an event handler. + * + * @param {string} event wheelEvent|crankEvent|value|disconnect + * @param {function} handler function that will receive the event as its first argument + */ + on(event, handler) { + this.handlers[event] = handler; + } + + /** + * Find and connect to a device which exposes the CSC service. + * + * @return {Promise} + */ + connect() { + // Register handler for the disconnect event to be passed throug + NRF.on('disconnect', this.onDisconnect.bind(this)); + + // Find a device, then get the CSC Service and subscribe to + // notifications on the CSC Measurement characteristic. + // NRF.setLowPowerConnection(true); + return NRF.requestDevice({ + timeout: 5000, + filters: [{ services: [SERVICE_UUID] }], + }).then(device => { + this.device = device; + this.device.on('gattserverdisconnected', this.onDisconnect.bind(this)); + this.ccInterval = setInterval(this.checkConnection.bind(this), 2000); + return device.gatt.connect(); + }).then(gatt => { + this.gatt = gatt; + return gatt.getPrimaryService(SERVICE_UUID); + }).then(service => { + return service.getCharacteristic(MEASUREMENT_UUID); + }).then(characteristic => { + characteristic.on('characteristicvaluechanged', this.onValue.bind(this)); + return characteristic.startNotifications(); + }); + } + + /** + * Disconnect the device. + */ + disconnect() { + if (this.ccInterval) + clearInterval(this.ccInterval); + + if (!this.gatt) return; + try { + this.gatt.disconnect(); + } catch { + // + } + } +} + +exports = BLECSC; diff --git a/apps/cycling/cycling.app.js b/apps/cycling/cycling.app.js new file mode 100644 index 000000000..268284a29 --- /dev/null +++ b/apps/cycling/cycling.app.js @@ -0,0 +1,453 @@ +const Layout = require('Layout'); +const storage = require('Storage'); + +const SETTINGS_FILE = 'cycling.json'; +const SETTINGS_DEFAULT = { + sensors: {}, + metric: true, +}; + +const RECONNECT_TIMEOUT = 4000; +const MAX_CONN_ATTEMPTS = 2; + +class CSCSensor { + constructor(blecsc, display) { + // Dependency injection + this.blecsc = blecsc; + this.display = display; + + // Load settings + this.settings = storage.readJSON(SETTINGS_FILE, true) || SETTINGS_DEFAULT; + this.wheelCirc = undefined; + + // CSC runtime variables + this.movingTime = 0; // unit: s + this.lastBangleTime = Date.now(); // unit: ms + this.lwet = 0; // last wheel event time (unit: s/1024) + this.cwr = -1; // cumulative wheel revolutions + this.cwrTrip = 0; // wheel revolutions since trip start + this.speed = 0; // unit: m/s + this.maxSpeed = 0; // unit: m/s + this.speedFailed = 0; + + // Other runtime variables + this.connected = false; + this.failedAttempts = 0; + this.failed = false; + + // Layout configuration + this.layout = 0; + this.display.useMetricUnits(true); + this.deviceAddress = undefined; + this.display.useMetricUnits((this.settings.metric)); + } + + onDisconnect(event) { + console.log("disconnected ", event); + + this.connected = false; + this.wheelCirc = undefined; + + this.setLayout(0); + this.display.setDeviceAddress("unknown"); + + if (this.failedAttempts >= MAX_CONN_ATTEMPTS) { + this.failed = true; + this.display.setStatus("Connection failed after " + MAX_CONN_ATTEMPTS + " attempts."); + } else { + this.display.setStatus("Disconnected"); + setTimeout(this.connect.bind(this), RECONNECT_TIMEOUT); + } + } + + loadCircumference() { + if (!this.deviceAddress) return; + + // Add sensor to settings if not present + if (!this.settings.sensors[this.deviceAddress]) { + this.settings.sensors[this.deviceAddress] = { + cm: 223, + mm: 0, + }; + storage.writeJSON(SETTINGS_FILE, this.settings); + } + + const high = this.settings.sensors[this.deviceAddress].cm || 223; + const low = this.settings.sensors[this.deviceAddress].mm || 0; + this.wheelCirc = (10*high + low) / 1000; + } + + connect() { + this.connected = false; + this.setLayout(0); + this.display.setStatus("Connecting"); + console.log("Trying to connect to BLE CSC"); + + // Hook up events + this.blecsc.on('wheelEvent', this.onWheelEvent.bind(this)); + this.blecsc.on('disconnect', this.onDisconnect.bind(this)); + + // Scan for BLE device and connect + this.blecsc.connect() + .then(function() { + this.failedAttempts = 0; + this.failed = false; + this.connected = true; + this.deviceAddress = this.blecsc.getDeviceAddress(); + console.log("Connected to " + this.deviceAddress); + + this.display.setDeviceAddress(this.deviceAddress); + this.display.setStatus("Connected"); + + this.loadCircumference(); + + // Switch to speed screen in 2s + setTimeout(function() { + this.setLayout(1); + this.updateScreen(); + }.bind(this), 2000); + }.bind(this)) + .catch(function(e) { + this.failedAttempts++; + this.onDisconnect(e); + }.bind(this)); + } + + disconnect() { + this.blecsc.disconnect(); + this.reset(); + this.setLayout(0); + this.display.setStatus("Disconnected"); + } + + setLayout(num) { + this.layout = num; + if (this.layout == 0) { + this.display.updateLayout("status"); + } else if (this.layout == 1) { + this.display.updateLayout("speed"); + } else if (this.layout == 2) { + this.display.updateLayout("distance"); + } + } + + reset() { + this.connected = false; + this.failed = false; + this.failedAttempts = 0; + this.wheelCirc = undefined; + } + + interact(d) { + // Only interested in tap / center button + if (d) return; + + // Reconnect in failed state + if (this.failed) { + this.reset(); + this.connect(); + } else if (this.connected) { + this.setLayout((this.layout + 1) % 3); + } + } + + updateScreen() { + var tripDist = this.cwrTrip * this.wheelCirc; + var avgSpeed = this.movingTime > 3 ? tripDist / this.movingTime : 0; + + this.display.setTotalDistance(this.cwr * this.wheelCirc); + this.display.setTripDistance(tripDist); + this.display.setSpeed(this.speed); + this.display.setAvg(avgSpeed); + this.display.setMax(this.maxSpeed); + this.display.setTime(Math.floor(this.movingTime)); + } + + onWheelEvent(event) { + // Calculate number of revolutions since last wheel event + var dRevs = (this.cwr > 0 ? event.cwr - this.cwr : 0); + this.cwr = event.cwr; + + // Increment the trip revolutions counter + this.cwrTrip += dRevs; + + // Calculate time delta since last wheel event + var dT = (event.lwet - this.lwet)/1024; + var now = Date.now(); + var dBT = (now-this.lastBangleTime)/1000; + this.lastBangleTime = now; + if (dT<0) dT+=64; // wheel event time wraps every 64s + if (Math.abs(dT-dBT)>3) dT = dBT; // not sure about the reason for this + this.lwet = event.lwet; + + // Recalculate current speed + if (dRevs>0 && dT>0) { + this.speed = dRevs * this.wheelCirc / dT; + this.speedFailed = 0; + this.movingTime += dT; + } else { + this.speedFailed++; + if (this.speedFailed>3) { + this.speed = 0; + } + } + + // Update max speed + if (this.speed>this.maxSpeed + && (this.movingTime>3 || this.speed<20) + && this.speed<50 + ) this.maxSpeed = this.speed; + + this.updateScreen(); + } +} + +class CSCDisplay { + constructor() { + this.metric = true; + this.fontLabel = "6x8"; + this.fontSmall = "15%"; + this.fontMed = "18%"; + this.fontLarge = "32%"; + this.currentLayout = "status"; + this.layouts = {}; + this.layouts.speed = new Layout({ + type: "v", + c: [ + { + type: "h", + id: "speed_g", + fillx: 1, + filly: 1, + pad: 4, + bgCol: "#fff", + c: [ + {type: undefined, width: 32, halign: -1}, + {type: "txt", id: "speed", label: "00.0", font: this.fontLarge, bgCol: "#fff", col: "#000", width: 122}, + {type: "txt", id: "speed_u", label: " km/h", font: this.fontLabel, col: "#000", width: 22, r: 90}, + ] + }, + { + type: "h", + id: "time_g", + fillx: 1, + pad: 4, + bgCol: "#000", + height: 36, + c: [ + {type: undefined, width: 32, halign: -1}, + {type: "txt", id: "time", label: "00:00", font: this.fontMed, bgCol: "#000", col: "#fff", width: 122}, + {type: "txt", id: "time_u", label: "mins", font: this.fontLabel, bgCol: "#000", col: "#fff", width: 22, r: 90}, + ] + }, + { + type: "h", + id: "stats_g", + fillx: 1, + bgCol: "#fff", + height: 36, + c: [ + { + type: "v", + pad: 4, + bgCol: "#fff", + c: [ + {type: "txt", id: "max_l", label: "MAX", font: this.fontLabel, col: "#000"}, + {type: "txt", id: "max", label: "00.0", font: this.fontSmall, bgCol: "#fff", col: "#000", width: 69}, + ], + }, + { + type: "v", + pad: 4, + bgCol: "#fff", + c: [ + {type: "txt", id: "avg_l", label: "AVG", font: this.fontLabel, col: "#000"}, + {type: "txt", id: "avg", label: "00.0", font: this.fontSmall, bgCol: "#fff", col: "#000", width: 69}, + ], + }, + {type: "txt", id: "stats_u", label: " km/h", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 22, r: 90}, + ] + }, + ], + }); + this.layouts.distance = new Layout({ + type: "v", + bgCol: "#fff", + c: [ + { + type: "h", + id: "tripd_g", + fillx: 1, + pad: 4, + bgCol: "#fff", + height: 32, + c: [ + {type: "txt", id: "tripd_l", label: "TRP", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 36}, + {type: "txt", id: "tripd", label: "0", font: this.fontMed, bgCol: "#fff", col: "#000", width: 118}, + {type: "txt", id: "tripd_u", label: "km", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 22, r: 90}, + ] + }, + { + type: "h", + id: "totald_g", + fillx: 1, + pad: 4, + bgCol: "#fff", + height: 32, + c: [ + {type: "txt", id: "totald_l", label: "TTL", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 36}, + {type: "txt", id: "totald", label: "0", font: this.fontMed, bgCol: "#fff", col: "#000", width: 118}, + {type: "txt", id: "totald_u", label: "km", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 22, r: 90}, + ] + }, + ], + }); + this.layouts.status = new Layout({ + type: "v", + c: [ + { + type: "h", + id: "status_g", + fillx: 1, + bgCol: "#fff", + height: 100, + c: [ + {type: "txt", id: "status", label: "Bangle Cycling", font: this.fontSmall, bgCol: "#fff", col: "#000", width: 176, wrap: 1}, + ] + }, + { + type: "h", + id: "addr_g", + fillx: 1, + pad: 4, + bgCol: "#fff", + height: 32, + c: [ + { type: "txt", id: "addr_l", label: "ADDR", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 36 }, + { type: "txt", id: "addr", label: "unknown", font: this.fontLabel, bgCol: "#fff", col: "#000", width: 140 }, + ] + }, + ], + }); + } + + updateLayout(layout) { + this.currentLayout = layout; + + g.clear(); + this.layouts[layout].update(); + this.layouts[layout].render(); + Bangle.drawWidgets(); + } + + renderIfLayoutActive(layout, node) { + if (layout != this.currentLayout) return; + this.layouts[layout].render(node); + } + + useMetricUnits(metric) { + this.metric = metric; + + // console.log("using " + (metric ? "metric" : "imperial") + " units"); + + var speedUnit = metric ? "km/h" : "mph"; + this.layouts.speed.speed_u.label = speedUnit; + this.layouts.speed.stats_u.label = speedUnit; + + var distanceUnit = metric ? "km" : "mi"; + this.layouts.distance.tripd_u.label = distanceUnit; + this.layouts.distance.totald_u.label = distanceUnit; + + this.updateLayout(this.currentLayout); + } + + convertDistance(meters) { + if (this.metric) return meters / 1000; + return meters / 1609.344; + } + + convertSpeed(mps) { + if (this.metric) return mps * 3.6; + return mps * 2.23694; + } + + setSpeed(speed) { + this.layouts.speed.speed.label = this.convertSpeed(speed).toFixed(1); + this.renderIfLayoutActive("speed", this.layouts.speed.speed_g); + } + + setAvg(speed) { + this.layouts.speed.avg.label = this.convertSpeed(speed).toFixed(1); + this.renderIfLayoutActive("speed", this.layouts.speed.stats_g); + } + + setMax(speed) { + this.layouts.speed.max.label = this.convertSpeed(speed).toFixed(1); + this.renderIfLayoutActive("speed", this.layouts.speed.stats_g); + } + + setTime(seconds) { + var time = ''; + var hours = Math.floor(seconds/3600); + if (hours) { + time += hours + ":"; + this.layouts.speed.time_u.label = " hrs"; + } else { + this.layouts.speed.time_u.label = "mins"; + } + + time += String(Math.floor((seconds%3600)/60)).padStart(2, '0') + ":"; + time += String(seconds % 60).padStart(2, '0'); + + this.layouts.speed.time.label = time; + this.renderIfLayoutActive("speed", this.layouts.speed.time_g); + } + + setTripDistance(distance) { + this.layouts.distance.tripd.label = this.convertDistance(distance).toFixed(1); + this.renderIfLayoutActive("distance", this.layouts.distance.tripd_g); + } + + setTotalDistance(distance) { + distance = this.convertDistance(distance); + if (distance >= 1000) { + this.layouts.distance.totald.label = String(Math.round(distance)); + } else { + this.layouts.distance.totald.label = distance.toFixed(1); + } + this.renderIfLayoutActive("distance", this.layouts.distance.totald_g); + } + + setDeviceAddress(address) { + this.layouts.status.addr.label = address; + this.renderIfLayoutActive("status", this.layouts.status.addr_g); + } + + setStatus(status) { + this.layouts.status.status.label = status; + this.renderIfLayoutActive("status", this.layouts.status.status_g); + } +} + +var BLECSC; +if (process.env.BOARD === "EMSCRIPTEN" || process.env.BOARD === "EMSCRIPTEN2") { + // Emulator + BLECSC = require("blecsc-emu"); +} else { + // Actual hardware + BLECSC = require("blecsc"); +} +var blecsc = new BLECSC(); +var display = new CSCDisplay(); +var sensor = new CSCSensor(blecsc, display); + +E.on('kill',()=>{ + sensor.disconnect(); +}); + +Bangle.setUI("updown", d => { + sensor.interact(d); +}); + +Bangle.loadWidgets(); +sensor.connect(); diff --git a/apps/cycling/cycling.icon.js b/apps/cycling/cycling.icon.js new file mode 100644 index 000000000..12c597956 --- /dev/null +++ b/apps/cycling/cycling.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH/OAAIuuGFYuEGFQv/ADOlwV8wK/qwN8AAelGAguiFogACWsulFw6SERcwAFSISLnSMuAFZWCGENWllWLRSZC0vOAAovWmUslkyvbqJwIuHGC4uBAARiDdAwueL4YACMQLmfX5IAFqwwoMIowpMQ4wpGIcywDiYAA2IAAgwGq2kFwIvGC5YtPDJIuCF4gXPFxQHLF44XQFxAKOF4oXRBg4LOFwYvEEag7OBgReQNZzLNF5IXPBJlXq4vVC5Qv8R9TXQFwbvYJBgLlNbYXRBoYOEA44XfCAgAFCxgXYDI4VPC7IA/AH4A/AH4AWA")) diff --git a/apps/cycling/icons8-cycling-48.png b/apps/cycling/icons8-cycling-48.png new file mode 100644 index 000000000..0bc83859f Binary files /dev/null and b/apps/cycling/icons8-cycling-48.png differ diff --git a/apps/cycling/metadata.json b/apps/cycling/metadata.json new file mode 100644 index 000000000..cb4260bb2 --- /dev/null +++ b/apps/cycling/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "cycling", + "name": "Bangle Cycling", + "shortName": "Cycling", + "version": "0.01", + "description": "Display live values from a BLE CSC sensor", + "icon": "icons8-cycling-48.png", + "tags": "outdoors,exercise,ble,bluetooth", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"cycling.app.js","url":"cycling.app.js"}, + {"name":"cycling.settings.js","url":"settings.js"}, + {"name":"blecsc","url":"blecsc.js"}, + {"name":"cycling.img","url":"cycling.icon.js","evaluate": true} + ] +} diff --git a/apps/cycling/settings.js b/apps/cycling/settings.js new file mode 100644 index 000000000..76303379d --- /dev/null +++ b/apps/cycling/settings.js @@ -0,0 +1,57 @@ +// This file should contain exactly one function, which shows the app's settings +/** + * @param {function} back Use back() to return to settings menu + */ +(function(back) { + const storage = require('Storage') + const SETTINGS_FILE = 'cycling.json' + + // Set default values and merge with stored values + let settings = Object.assign({ + metric: true, + sensors: {}, + }, (storage.readJSON(SETTINGS_FILE, true) || {})); + + const menu = { + '': { 'title': 'Cycling' }, + '< Back': back, + 'Units': { + value: settings.metric, + format: v => v ? 'metric' : 'imperial', + onchange: (metric) => { + settings.metric = metric; + storage.writeJSON(SETTINGS_FILE, settings); + }, + }, + } + + const sensorMenus = {}; + for (var addr of Object.keys(settings.sensors)) { + // Define sub menu + sensorMenus[addr] = { + '': { title: addr }, + '< Back': () => E.showMenu(menu), + 'cm': { + value: settings.sensors[addr].cm, + min: 80, max: 240, step: 1, + onchange: (v) => { + settings.sensors[addr].cm = v; + storage.writeJSON(SETTINGS_FILE, settings); + }, + }, + '+ mm': { + value: settings.sensors[addr].mm, + min: 0, max: 9, step: 1, + onchange: (v) => { + settings.sensors[addr].mm = v; + storage.writeJSON(SETTINGS_FILE, settings); + }, + }, + }; + + // Add entry to main menu + menu[addr] = () => E.showMenu(sensorMenus[addr]); + } + + E.showMenu(menu); +}) diff --git a/apps/daisy/ChangeLog b/apps/daisy/ChangeLog index 115c8f2ff..d5844c62b 100644 --- a/apps/daisy/ChangeLog +++ b/apps/daisy/ChangeLog @@ -2,3 +2,5 @@ 0.02: added settings menu to change color 0.03: fix metadata.json to allow setting as clock 0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs +0.05: changed text to uppercase, just looks better, removed colons on text +0.06: better contrast for light theme, use fg color instead of dithered for ring diff --git a/apps/daisy/README.md b/apps/daisy/README.md index 12a55ddfd..491ed697f 100644 --- a/apps/daisy/README.md +++ b/apps/daisy/README.md @@ -28,5 +28,6 @@ See [#1248](https://github.com/espruino/BangleApps/issues/1248) ## Screenshots ![](screenshot_daisy1.png) +![](screenshot_daisy3.png) -It is worth looking at the real thing though as the screenshot does not do it justice. +It is worth looking at the real thing though as the screenshots do not do it justice. diff --git a/apps/daisy/app.js b/apps/daisy/app.js index 01d177a32..7c513726f 100644 --- a/apps/daisy/app.js +++ b/apps/daisy/app.js @@ -41,10 +41,17 @@ Graphics.prototype.setFontRoboto20 = function(scale) { }; function assignPalettes() { - // palette for 0-40% - pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]); - // palette for 50-100% - pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]); + if (g.theme.dark) { + // palette for 0-40% + pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]); + // palette for 50-100% + pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]); + } else { + // palette for 0-40% + pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(settings.fg), g.toColor("#00f")]); + // palette for 50-100% + pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.theme.fg, g.toColor("#00f")]); + } } function setSmallFont20() { @@ -109,10 +116,10 @@ function updateSunRiseSunSet(now, lat, lon, line){ const infoData = { ID_DATE: { calc: () => {var d = (new Date()).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} }, ID_DAY: { calc: () => {var d = require("locale").dow(new Date()).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} }, - ID_SR: { calc: () => 'Sunrise: ' + sunRise }, - ID_SS: { calc: () => 'Sunset: ' + sunSet }, - ID_STEP: { calc: () => 'Steps: ' + getSteps() }, - ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' }, + ID_SR: { calc: () => 'SUNRISE ' + sunRise }, + ID_SS: { calc: () => 'SUNSET ' + sunSet }, + ID_STEP: { calc: () => 'STEPS ' + getSteps() }, + ID_BATT: { calc: () => 'BATTERY ' + E.getBattery() + '%' }, ID_HRM: { calc: () => hrmCurrent } }; @@ -158,7 +165,7 @@ function drawInfo() { g.setColor('#f00'); // red drawHeartIcon(); } else { - g.drawString((infoData[infoMode].calc()), w/2, infoLine); + g.drawString((infoData[infoMode].calc().toUpperCase()), w/2, infoLine); } } @@ -225,7 +232,7 @@ function drawSteps() { setSmallFont(); g.setFontAlign(0,0); g.setColor(g.theme.fg); - g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4); + g.drawString('STEPS ' + getSteps(), w/2, (3*h/4) - 4); drawingSteps = false; } diff --git a/apps/daisy/metadata.json b/apps/daisy/metadata.json index 5e53f2d5e..5073db603 100644 --- a/apps/daisy/metadata.json +++ b/apps/daisy/metadata.json @@ -1,13 +1,13 @@ { "id": "daisy", "name": "Daisy", - "version":"0.04", + "version":"0.06", "dependencies": {"mylocation":"app"}, - "description": "A clock based on the Pastel clock with large ring guage for steps", + "description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times", "icon": "app.png", "type": "clock", "tags": "clock", "supports" : ["BANGLEJS2"], - "screenshots": [{"url":"screenshot_daisy2.jpg"}], + "screenshots": [{"url":"screenshot_daisy3.png"}], "readme": "README.md", "storage": [ {"name":"daisy.app.js","url":"app.js"}, diff --git a/apps/daisy/screenshot_daisy2.jpg b/apps/daisy/screenshot_daisy2.jpg deleted file mode 100644 index fec6a4c7b..000000000 Binary files a/apps/daisy/screenshot_daisy2.jpg and /dev/null differ diff --git a/apps/daisy/screenshot_daisy2.png b/apps/daisy/screenshot_daisy2.png new file mode 100644 index 000000000..3636f3766 Binary files /dev/null and b/apps/daisy/screenshot_daisy2.png differ diff --git a/apps/daisy/screenshot_daisy3.png b/apps/daisy/screenshot_daisy3.png new file mode 100644 index 000000000..b5d55a037 Binary files /dev/null and b/apps/daisy/screenshot_daisy3.png differ diff --git a/apps/dane_tcr/app.js b/apps/dane_tcr/app.js index aa25379d3..ce75c55cb 100644 --- a/apps/dane_tcr/app.js +++ b/apps/dane_tcr/app.js @@ -244,7 +244,7 @@ function run(){ Bangle.setLCDMode(); g.clear(); g.flip(); - E.showMessage("Loading..."); + E.showMessage(/*LANG*/"Loading..."); load(app.src); } diff --git a/apps/diceroll/ChangeLog b/apps/diceroll/ChangeLog new file mode 100644 index 000000000..89dff4011 --- /dev/null +++ b/apps/diceroll/ChangeLog @@ -0,0 +1 @@ +0.01: App created \ No newline at end of file diff --git a/apps/diceroll/app-icon.js b/apps/diceroll/app-icon.js new file mode 100644 index 000000000..4d6e7da16 --- /dev/null +++ b/apps/diceroll/app-icon.js @@ -0,0 +1 @@ +E.toArrayBuffer(atob("ICABAAAAAAAAAAAAAAAAAAHAAAAP8AAAfn4AA/APwA+DwfAPg8HwD+AH8Az4HzAMPnwwDAfgMAwBgDAMCYAwDA2YMAwhmDAMIZAwDCGDMA2BgzAMgYAwDAGAMA8BgPADwYPAAPGPgAB9ngAAH/gAAAfgAAABgAAAAAAAAAAAAAAAAAA=")) diff --git a/apps/diceroll/app.js b/apps/diceroll/app.js new file mode 100644 index 000000000..d514ce92f --- /dev/null +++ b/apps/diceroll/app.js @@ -0,0 +1,108 @@ +var init_message = true; +var acc_data; +var die_roll = 1; +var selected_die = 0; +var roll = 0; +const dices = [4, 6, 10, 12, 20]; + +g.setFontAlign(0,0); + +Bangle.on('touch', function(button, xy) { + // Change die if not rolling + if(roll < 1){ + if(selected_die <= 3){ + selected_die++; + }else{ + selected_die = 0; + } + } + //Disable initial message + init_message = false; +}); + +function rect(){ + x1 = g.getWidth()/2 - 35; + x2 = g.getWidth()/2 + 35; + y1 = g.getHeight()/2 - 35; + y2 = g.getHeight()/2 + 35; + g.drawRect(x1, y1, x2, y2); +} + +function pentagon(){ + x1 = g.getWidth()/2; + y1 = g.getHeight()/2 - 50; + x2 = g.getWidth()/2 - 50; + y2 = g.getHeight()/2 - 10; + x3 = g.getWidth()/2 - 30; + y3 = g.getHeight()/2 + 30; + x4 = g.getWidth()/2 + 30; + y4 = g.getHeight()/2 + 30; + x5 = g.getWidth()/2 + 50; + y5 = g.getHeight()/2 - 10; + g.drawPoly([x1, y1, x2, y2, x3, y3, x4, y4, x5, y5], true); +} + +function triangle(){ + x1 = g.getWidth()/2; + y1 = g.getHeight()/2 - 57; + x2 = g.getWidth()/2 - 50; + y2 = g.getHeight()/2 + 23; + x3 = g.getWidth()/2 + 50; + y3 = g.getHeight()/2 + 23; + g.drawPoly([x1, y1, x2, y2, x3, y3], true); +} + +function drawDie(variant) { + if(variant == 1){ + //Rect, 6 + rect(); + }else if(variant == 3){ + //Pentagon, 12 + pentagon(); + }else{ + //Triangle, 4, 10, 20 + triangle(); + } +} + +function initMessage(){ + g.setFont("6x8", 2); + g.drawString("Dice-n-Roll", g.getWidth()/2, 20); + g.drawString("Shake to roll", g.getWidth()/2, 60); + g.drawString("Tap to change", g.getWidth()/2, 80); + g.drawString("Tap to start", g.getWidth()/2, 150); +} + +function rollDie(){ + acc_data = Bangle.getAccel(); + if(acc_data.diff > 0.3){ + roll = 3; + } + //Mange the die "roll" by chaning the number a few times + if(roll > 0){ + g.drawString("Rolling!", g.getWidth()/2, 150); + die_roll = Math.abs(E.hwRand()) % dices[selected_die] + 1; + roll--; + } + //Draw dice graphics + drawDie(selected_die); + //Draw dice number + g.setFontAlign(0,0); + g.setFont("Vector", 45); + g.drawString(die_roll, g.getWidth()/2, g.getHeight()/2); + //Draw selected die in right corner + g.setFont("6x8", 2); + g.drawString(dices[selected_die], g.getWidth()-15, 15); +} + +function main() { + g.clear(); + if(init_message){ + initMessage(); + }else{ + rollDie(); + } + Bangle.setLCDPower(1); +} + +var interval = setInterval(main, 300); \ No newline at end of file diff --git a/apps/diceroll/app.png b/apps/diceroll/app.png new file mode 100644 index 000000000..b695b7080 Binary files /dev/null and b/apps/diceroll/app.png differ diff --git a/apps/diceroll/diceroll_screenshot.png b/apps/diceroll/diceroll_screenshot.png new file mode 100644 index 000000000..71024edbb Binary files /dev/null and b/apps/diceroll/diceroll_screenshot.png differ diff --git a/apps/diceroll/metadata.json b/apps/diceroll/metadata.json new file mode 100644 index 000000000..81a2f8bfd --- /dev/null +++ b/apps/diceroll/metadata.json @@ -0,0 +1,14 @@ +{ "id": "diceroll", + "name": "Dice-n-Roll", + "shortName":"Dice-n-Roll", + "icon": "app.png", + "version":"0.01", + "description": "A dice app with a few different dice.", + "screenshots": [{"url":"diceroll_screenshot.png"}], + "tags": "game", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"diceroll.app.js","url":"app.js"}, + {"name":"diceroll.img","url":"app-icon.js","evaluate":true} + ] + } \ No newline at end of file diff --git a/apps/doztime/ChangeLog b/apps/doztime/ChangeLog index 6c4a25b26..77d82eff9 100644 --- a/apps/doztime/ChangeLog +++ b/apps/doztime/ChangeLog @@ -2,3 +2,6 @@ 0.02: added emulator capability and display of widgets 0.03: bug of advancing time fixed; doztime now correct within ca. 1 second 0.04: changed time colour from slightly off white to pure white +0.05: extraneous comments and code removed + display improved + now supports Adjust Clock widget, if installed diff --git a/apps/doztime/app-bangle2.js b/apps/doztime/app-bangle2.js index b77e5201a..8a315118f 100644 --- a/apps/doztime/app-bangle2.js +++ b/apps/doztime/app-bangle2.js @@ -1,23 +1,23 @@ // Positioning values for graphics buffers const g_height = 80; // total graphics height -const g_x_off = 0; // position from left was 16, then 8 here -const g_y_off = (184 - g_height)/2; // vertical center for graphics region was 240 +const g_x_off = 0; // position from left +const g_y_off = (180 - g_height)/2; // vertical center for graphics region const g_width = 240 - 2 * g_x_off; // total graphics width -const g_height_d = 28; // height of date region was 32 +const g_height_d = 28; // height of date region const g_y_off_d = 0; // y position of date region within graphics region -const spacing = 0; // space between date and time in graphics region +const spacing = 6; // space between date and time in graphics region const g_y_off_t = g_y_off_d + g_height_d + spacing; // y position of time within graphics region -const g_height_t = 44; // height of time region was 48 +const g_height_t = 44; // height of time region // Other vars const A1 = [30,30,30,30,31,31,31,31,31,31,30,30]; const B1 = [30,30,30,30,30,31,31,31,31,31,30,30]; const B2 = [30,30,30,30,31,31,31,31,31,30,30,30]; const timeColour = "#ffffff"; -const dateColours = ["#ff0000","#ffa500","#ffff00","#00b800","#8383ff","#ff00ff","#ff0080"]; //blue was 0000ff -const calen10 = {"size":26,"pt0":[18-g_x_off,16],"step":[16,0],"dx":-4.5,"dy":-4.5}; // positioning for usual calendar line ft w 32, 32-g, step 20 -const calen7 = {"size":26,"pt0":[48-g_x_off,16],"step":[16,0],"dx":-4.5,"dy":-4.5}; // positioning for S-day calendar line ft w 32, 62-g, step 20 -const time5 = {"size":42,"pt0":[39-g_x_off,24],"step":[26,0],"dx":-6.5,"dy":-6.5}; // positioning for lull time line ft w 48, 64-g, step 30 +const dateColours = ["#ff0000","#ff8000","#ffff00","#00ff00","#0080ff","#ff00ff","#ffffff"]; +const calen10 = {"size":26,"pt0":[18-g_x_off,16],"step":[16,0],"dx":-4.5,"dy":-4.5}; // positioning for usual calendar line +const calen7 = {"size":26,"pt0":[48-g_x_off,16],"step":[16,0],"dx":-4.5,"dy":-4.5}; // positioning for S-day calendar line +const time5 = {"size":42,"pt0":[39-g_x_off,24],"step":[26,0],"dx":-6.5,"dy":-6.5}; // positioning for lull time line const time6 = {"size":42,"pt0":[26-g_x_off,24],"step":[26,0],"dx":-6.5,"dy":-6.5}; // positioning for twinkling time line ft w 48, 48-g, step 30 const baseYear = 11584; const baseDate = Date(2020,11,21); // month values run from 0 to 11 @@ -42,28 +42,25 @@ var g_t = Graphics.createArrayBuffer(g_width,g_height_t,1,{'msb':true}); g.clear(); // start with blank screen g.flip = function() { - g.setBgColor(0,0,0); + g.setBgColor(0,0,0); g.setColor(dateColour); - g.drawImage( - { - width:g_width, - height:g_height_d, - buffer:g_d.buffer - }, g_x_off, g_y_off + g_y_off_d); - g.setColor(timeColour2); - g.drawImage( - { - width:g_width, - height:g_height_t, - buffer:g_t.buffer - }, g_x_off, g_y_off + g_y_off_t); + g.drawImage( + { + width:g_width, + height:g_height_d, + buffer:g_d.buffer + }, g_x_off, g_y_off + g_y_off_d); + g.setColor(timeColour2); + g.drawImage( + { + width:g_width, + height:g_height_t, + buffer:g_t.buffer + }, g_x_off, g_y_off + g_y_off_t); }; -setWatch(function(){ modeTime(); }, BTN, {repeat:true} ); //was BTN1 -setWatch(function(){ Bangle.showLauncher(); }, BTN, { repeat: false, edge: "falling" }); //was BTN2 -//setWatch(function(){ modeWeather(); }, BTN3, {repeat:true}); -//setWatch(function(){ toggleTimeDigits(); }, BTN4, {repeat:true}); -//setWatch(function(){ toggleDateFormat(); }, BTN5, {repeat:true}); +setWatch(function(){ modeTime(); }, BTN, {repeat:true} ); +setWatch(function(){ Bangle.showLauncher(); }, BTN, { repeat: false, edge: "falling" }); Bangle.on('touch', function(button, xy) { //from Gordon Williams if (button==1) toggleTimeDigits(); @@ -71,10 +68,10 @@ Bangle.on('touch', function(button, xy) { //from Gordon Williams }); function buildSequence(targ){ - for(let i=0;i n > dt)-1; - let year = baseYear+parseInt(index/12); - let month = index % 12; - let day = parseInt((dt-sequence[index])/86400000); - let colour = dateColours[day % 6]; - if(day==30){ colour=dateColours[6]; } - return({"year":year,"month":month,"day":day,"colour":colour}); + let index = sequence.findIndex(n => n > dt)-1; + let year = baseYear+parseInt(index/12); + let month = index % 12; + let day = parseInt((dt-sequence[index])/86400000); + let colour = dateColours[day % 6]; + if(day==30){ colour=dateColours[6]; } + return({"year":year,"month":month,"day":day,"colour":colour}); } function toggleTimeDigits(){ - addTimeDigit = !addTimeDigit; - modeTime(); + addTimeDigit = !addTimeDigit; + modeTime(); } function toggleDateFormat(){ - dateFormat = !dateFormat; - modeTime(); + dateFormat = !dateFormat; + modeTime(); } function formatDate(res,dateFormat){ - let yyyy = res.year.toString(12); - calenDef = calen10; - if(!dateFormat){ //ordinal format - let mm = ("0"+(res.month+1).toString(12)).substr(-2); - let dd = ("0"+(res.day+1).toString(12)).substr(-2); - if(res.day==30){ - calenDef = calen7; - let m = ((res.month+1).toString(12)).substr(-2); - return(yyyy+"-"+"S"+m); // ordinal format - } - return(yyyy+"-"+mm+"-"+dd); - } - let m = res.month.toString(12); // cardinal format - let w = parseInt(res.day/6); - let d = res.day%6; - //return(yyyy+"-"+res.month+"-"+w+"-"+d); - return(yyyy+"-"+m+"-"+w+"-"+d); + let yyyy = res.year.toString(12); + calenDef = calen10; + if(!dateFormat){ //ordinal format + let mm = ("0"+(res.month+1).toString(12)).substr(-2); + let dd = ("0"+(res.day+1).toString(12)).substr(-2); + if(res.day==30){ + calenDef = calen7; + let m = ((res.month+1).toString(12)).substr(-2); + return(yyyy+"-"+"S"+m); // ordinal format + } + return(yyyy+"-"+mm+"-"+dd); + } + let m = res.month.toString(12); // cardinal format + let w = parseInt(res.day/6); + let d = res.day%6; + //return(yyyy+"-"+res.month+"-"+w+"-"+d); + return(yyyy+"-"+m+"-"+w+"-"+d); } function writeDozTime(text,def){ - let pts = def.pts; - let x=def.pt0[0]; - let y=def.pt0[1]; - g_t.clear(); + let pts = def.pts; + let x=def.pt0[0]; + let y=def.pt0[1]; + g_t.clear(); g_t.setFont("Vector",def.size); - for(let i in text){ - if(text[i]=="a"){ g_t.setFontAlign(0,0,2); g_t.drawString("2",x+2+def.dx,y+1+def.dy); } //+1s are new - else if(text[i]=="b"){ g_t.setFontAlign(0,0,2); g_t.drawString("3",x+2+def.dx,y+1+def.dy); } //+1s are new - else{ g_t.setFontAlign(0,0,0); g_t.drawString(text[i],x,y); } - x = x+def.step[0]; - y = y+def.step[1]; - } + for(let i in text){ + if(text[i]=="a"){ g_t.setFontAlign(0,0,2); g_t.drawString("2",x+2+def.dx,y+1+def.dy); } + else if(text[i]=="b"){ g_t.setFontAlign(0,0,2); g_t.drawString("3",x+2+def.dx,y+1+def.dy); } + else{ g_t.setFontAlign(0,0,0); g_t.drawString(text[i],x,y); } + x = x+def.step[0]; + y = y+def.step[1]; + } } function writeDozDate(text,def,colour){ - - dateColour = colour; - let pts = def.pts; - let x=def.pt0[0]; - let y=def.pt0[1]; - g_d.clear(); - g_d.setFont("Vector",def.size); - for(let i in text){ - if(text[i]=="a"){ g_d.setFontAlign(0,0,2); g_d.drawString("2",x+2+def.dx,y+1+def.dy); } //+1s new - else if(text[i]=="b"){ g_d.setFontAlign(0,0,2); g_d.drawString("3",x+2+def.dx,y+1+def.dy); } //+1s new - else{ g_d.setFontAlign(0,0,0); g_d.drawString(text[i],x,y); } - x = x+def.step[0]; - y = y+def.step[1]; - } + + dateColour = colour; + let pts = def.pts; + let x=def.pt0[0]; + let y=def.pt0[1]; + g_d.clear(); + g_d.setFont("Vector",def.size); + for(let i in text){ + if(text[i]=="a"){ g_d.setFontAlign(0,0,2); g_d.drawString("2",x+2+def.dx,y+1+def.dy); } + else if(text[i]=="b"){ g_d.setFontAlign(0,0,2); g_d.drawString("3",x+2+def.dx,y+1+def.dy); } + else{ g_d.setFontAlign(0,0,0); g_d.drawString(text[i],x,y); } + x = x+def.step[0]; + y = y+def.step[1]; + } } +Bangle.loadWidgets(); +//for malaire's Adjust Clock widget, if used +function adjustedNow() { + return WIDGETS.adjust ? new Date(WIDGETS.adjust.now()) : new Date(); +} +Bangle.drawWidgets(); + // Functions for time mode function drawTime() { - let dt = new Date(); - let date = ""; - let timeDef; - let x = 0; - dt.setDate(dt.getDate()); - if(addTimeDigit){ - x = - 10368*dt.getHours()+172.8*dt.getMinutes()+2.88*dt.getSeconds()+0.00288*dt.getMilliseconds(); - let msg = "00000"+Math.floor(x).toString(12); - let time = msg.substr(-5,3)+"."+msg.substr(-2); - let wait = 347*(1-(x%1)); - timeDef = time6; - } else { - x = - 864*dt.getHours()+14.4*dt.getMinutes()+0.24*dt.getSeconds()+0.00024*dt.getMilliseconds(); - let msg = "0000"+Math.floor(x).toString(12); - let time = msg.substr(-4,3)+"."+msg.substr(-1); - let wait = 4167*(1-(x%1)); - timeDef = time5; - } - if(lastX > x){ res = getDate(dt); } // calculate date once at start-up and once when turning over to a new day - date = formatDate(res,dateFormat); - if(dt x){ res = getDate(dt); } // calculate date once at start-up and once when turning over to a new day + date = formatDate(res,dateFormat); + if(dt2200)) { - } else { - // We have a GPS time. Set time - setTime(g.time.getTime()/1000); - } - }); - Bangle.setGPSPower(1,"time"); - setTimeout(fixTime, 10*60*1000); // every 10 minutes -} -// Start time fixing with GPS on next 10 minute interval -setTimeout(fixTime, ((60-(new Date()).getMinutes()) % 10) * 60 * 1000); diff --git a/apps/doztime/metadata.json b/apps/doztime/metadata.json index d206cb0c3..a05bf1470 100644 --- a/apps/doztime/metadata.json +++ b/apps/doztime/metadata.json @@ -1,9 +1,9 @@ { "id": "doztime", - "name": "Dozenal Time", - "shortName": "Dozenal Time", - "version": "0.04", - "description": "A dozenal Holocene calendar and dozenal diurnal clock", + "name": "Dozenal Digital Time", + "shortName": "Dozenal Digital", + "version": "0.05", + "description": "A dozenal Holocene calendar and dozenal diurnal digital clock", "icon": "app.png", "type": "clock", "tags": "clock", diff --git a/apps/dragboard/ChangeLog b/apps/dragboard/ChangeLog new file mode 100644 index 000000000..48a1ffb03 --- /dev/null +++ b/apps/dragboard/ChangeLog @@ -0,0 +1,5 @@ +0.01: New App! +0.02: Added some missing code. +0.03: Made the code shorter and somewhat more readable by writing some functions. Also made it work as a library where it returns the text once finished. The keyboard is now made to exit correctly when the 'back' event is called. The keyboard now uses theme colors correctly, although it still looks best with dark theme. The numbers row is now solidly green - except for highlights. +0.04: Now displays the opened text string at launch. +0.05: Now scrolls text when string gets longer than screen width. diff --git a/apps/dragboard/README.md b/apps/dragboard/README.md new file mode 100644 index 000000000..8960e5749 --- /dev/null +++ b/apps/dragboard/README.md @@ -0,0 +1,16 @@ +Swipe along the red field and release to select a letter. + +Do the same for green field to select number or punctuation. + +Release on left or right part of black field for backspace or space. + +Swiping between the different fields is possible! + +The drag in Dragboard is a nod to the javascript 'drag' event, which is used to select the characters. Also, you can't help but feel somewhat glamorous and risque when this is your keyboard! + +Known bugs: +- Initially developed for use with dark theme set on Bangle.js 2 - that is still the preferred way to view it although it now works with other themes. +- When repeatedly doing 'del' on an empty text-string, the letter case is changed back and forth between upper and lower case. + +To do: +- Possibly provide a dragboard.settings.js file diff --git a/apps/dragboard/app.png b/apps/dragboard/app.png new file mode 100644 index 000000000..26e751896 Binary files /dev/null and b/apps/dragboard/app.png differ diff --git a/apps/dragboard/lib.js b/apps/dragboard/lib.js new file mode 100644 index 000000000..b9b19f982 --- /dev/null +++ b/apps/dragboard/lib.js @@ -0,0 +1,261 @@ +//Keep banglejs screen on for 100 sec at 0.55 power level for development purposes +//Bangle.setLCDTimeout(30); +//Bangle.setLCDPower(1); + +exports.input = function(options) { + options = options||{}; + var text = options.text; + if ("string"!=typeof text) text=""; + + var BGCOLOR = g.theme.bg; + var HLCOLOR = g.theme.fg; + var ABCCOLOR = g.toColor(1,0,0);//'#FF0000'; + var NUMCOLOR = g.toColor(0,1,0);//'#00FF00'; + var BIGFONT = '6x8:3'; + var BIGFONTWIDTH = parseInt(BIGFONT.charAt(0)*parseInt(BIGFONT.charAt(-1))); + var SMALLFONT = '6x8:1'; + var SMALLFONTWIDTH = parseInt(SMALLFONT.charAt(0)*parseInt(SMALLFONT.charAt(-1))); + + var ABC = 'abcdefghijklmnopqrstuvwxyz'.toUpperCase(); + var ABCPADDING = (g.getWidth()-6*ABC.length)/2; + + var NUM = ' 1234567890!?,.- '; + var NUMHIDDEN = ' 1234567890!?,.- '; + var NUMPADDING = (g.getWidth()-6*NUM.length)/2; + + var rectHeight = 40; + + + var delSpaceLast; + + function drawAbcRow() { + g.clear(); + g.setFont(SMALLFONT); + g.setColor(ABCCOLOR); + g.drawString(ABC, ABCPADDING, g.getHeight()/2); + g.fillRect(0, g.getHeight()-26, g.getWidth(), g.getHeight()); + } + + function drawNumRow() { + g.setFont(SMALLFONT); + g.setColor(NUMCOLOR); + g.drawString(NUM, NUMPADDING, g.getHeight()/4); + + g.fillRect(NUMPADDING, g.getHeight()-rectHeight*4/3, g.getWidth()-NUMPADDING, g.getHeight()-rectHeight*2/3); + } + + function updateTopString() { + "ram" + g.setColor(BGCOLOR); + g.fillRect(0,4+20,176,13+20); + g.setColor(0.2,0,0); + var rectLen = text.length<27? text.length*6:27*6; + g.fillRect(3,4+20,5+rectLen,13+20); + g.setColor(0.7,0,0); + g.fillRect(rectLen+5,4+20,rectLen+10,13+20); + g.setColor(1,1,1); + g.drawString(text.length<=27? text.substr(-27, 27) : '<- '+text.substr(-24,24), 5, 5+20); + } + + drawAbcRow(); + drawNumRow(); + updateTopString(); + + var abcHL; + var abcHLPrev = -10; + var numHL; + var numHLPrev = -10; + var type = ''; + var typePrev = ''; + var largeCharOffset = 6; + + function resetChars(char, HLPrev, typePadding, heightDivisor, rowColor) { + "ram" + // Small character in list + g.setColor(rowColor); + g.setFont(SMALLFONT); + g.drawString(char, typePadding + HLPrev*6, g.getHeight()/heightDivisor); + // Large character + g.setColor(BGCOLOR); + g.fillRect(0,g.getHeight()/3,176,g.getHeight()/3+24); + //g.drawString(charSet.charAt(HLPrev), typePadding + HLPrev*6 -largeCharOffset, g.getHeight()/3);; //Old implementation where I find the shape and place of letter to remove instead of just a rectangle. + // mark in the list + } + function showChars(char, HL, typePadding, heightDivisor) { + "ram" + // mark in the list + g.setColor(HLCOLOR); + g.setFont(SMALLFONT); + if (char != 'del' && char != 'space') g.drawString(char, typePadding + HL*6, g.getHeight()/heightDivisor); + // show new large character + g.setFont(BIGFONT); + g.drawString(char, typePadding + HL*6 -largeCharOffset, g.getHeight()/3); + g.setFont(SMALLFONT); + } + + function changeCase(abcHL) { + g.setColor(BGCOLOR); + g.drawString(ABC, ABCPADDING, g.getHeight()/2); + if (ABC.charAt(abcHL) == ABC.charAt(abcHL).toUpperCase()) ABC = ABC.toLowerCase(); + else ABC = ABC.toUpperCase(); + g.setColor(ABCCOLOR); + g.drawString(ABC, ABCPADDING, g.getHeight()/2); + } + return new Promise((resolve,reject) => { + // Interpret touch input + Bangle.setUI({ + mode: 'custom', + back: ()=>{ + Bangle.setUI(); + g.clearRect(Bangle.appRect); + resolve(text); + }, + drag: function(event) { + + // ABCDEFGHIJKLMNOPQRSTUVWXYZ + // Choose character by draging along red rectangle at bottom of screen + if (event.y >= ( g.getHeight() - 12 )) { + // Translate x-position to character + if (event.x < ABCPADDING) { abcHL = 0; } + else if (event.x >= 176-ABCPADDING) { abcHL = 25; } + else { abcHL = Math.floor((event.x-ABCPADDING)/6); } + + // Datastream for development purposes + //print(event.x, event.y, event.b, ABC.charAt(abcHL), ABC.charAt(abcHLPrev)); + + // Unmark previous character and mark the current one... + // Handling switching between letters and numbers/punctuation + if (typePrev != 'abc') resetChars(NUM.charAt(numHLPrev), numHLPrev, NUMPADDING, 4, NUMCOLOR); + + if (abcHL != abcHLPrev) { + resetChars(ABC.charAt(abcHLPrev), abcHLPrev, ABCPADDING, 2, ABCCOLOR); + showChars(ABC.charAt(abcHL), abcHL, ABCPADDING, 2); + } + // Print string at top of screen + if (event.b == 0) { + text = text + ABC.charAt(abcHL); + updateTopString(); + + // Autoswitching letter case + if (ABC.charAt(abcHL) == ABC.charAt(abcHL).toUpperCase()) changeCase(abcHL); + } + // Update previous character to current one + abcHLPrev = abcHL; + typePrev = 'abc'; + } + + + + + + + + + + // 12345678901234567890 + // Choose number or puctuation by draging on green rectangle + else if ((event.y < ( g.getHeight() - 12 )) && (event.y > ( g.getHeight() - 52 ))) { + // Translate x-position to character + if (event.x < NUMPADDING) { numHL = 0; } + else if (event.x > 176-NUMPADDING) { numHL = NUM.length-1; } + else { numHL = Math.floor((event.x-NUMPADDING)/6); } + + // Datastream for development purposes + //print(event.x, event.y, event.b, NUM.charAt(numHL), NUM.charAt(numHLPrev)); + + // Unmark previous character and mark the current one... + // Handling switching between letters and numbers/punctuation + if (typePrev != 'num') resetChars(ABC.charAt(abcHLPrev), abcHLPrev, ABCPADDING, 2, ABCCOLOR); + + if (numHL != numHLPrev) { + resetChars(NUM.charAt(numHLPrev), numHLPrev, NUMPADDING, 4, NUMCOLOR); + showChars(NUM.charAt(numHL), numHL, NUMPADDING, 4); + } + // Print string at top of screen + if (event.b == 0) { + g.setColor(HLCOLOR); + // Backspace if releasing before list of numbers/punctuation + if (event.x < NUMPADDING) { + // show delete sign + showChars('del', 0, g.getWidth()/2 +6 -27 , 4); + delSpaceLast = 1; + text = text.slice(0, -1); + updateTopString(); + //print(text); + } + // Append space if releasing after list of numbers/punctuation + else if (event.x > g.getWidth()-NUMPADDING) { + //show space sign + showChars('space', 0, g.getWidth()/2 +6 -6*3*5/2 , 4); + delSpaceLast = 1; + text = text + ' '; + updateTopString(); + //print(text); + } + // Append selected number/punctuation + else { + text = text + NUMHIDDEN.charAt(numHL); + updateTopString(); + + // Autoswitching letter case + if ((text.charAt(text.length-1) == '.') || (text.charAt(text.length-1) == '!')) changeCase(); + } + } + // Update previous character to current one + numHLPrev = numHL; + typePrev = 'num'; + } + + + + + + + + + // Make a space or backspace by swiping right or left on screen above green rectangle + else if (event.y > 20+4) { + if (event.b == 0) { + g.setColor(HLCOLOR); + if (event.x < g.getWidth()/2) { + resetChars(ABC.charAt(abcHLPrev), abcHLPrev, ABCPADDING, 2, ABCCOLOR); + resetChars(NUM.charAt(numHLPrev), numHLPrev, NUMPADDING, 4, NUMCOLOR); + + // show delete sign + showChars('del', 0, g.getWidth()/2 +6 -27 , 4); + delSpaceLast = 1; + + // Backspace and draw string upper right corner + text = text.slice(0, -1); + updateTopString(); + if (text.length==0) changeCase(abcHL); + //print(text, 'undid'); + } + else { + resetChars(ABC.charAt(abcHLPrev), abcHLPrev, ABCPADDING, 2, ABCCOLOR); + resetChars(NUM.charAt(numHLPrev), numHLPrev, NUMPADDING, 4, NUMCOLOR); + + //show space sign + showChars('space', 0, g.getWidth()/2 +6 -6*3*5/2 , 4); + delSpaceLast = 1; + + // Append space and draw string upper right corner + text = text + NUMHIDDEN.charAt(0); + updateTopString(); + //print(text, 'made space'); + } + } + } + } + }); +}); +/* return new Promise((resolve,reject) => { + Bangle.setUI({mode:"custom", back:()=>{ + Bangle.setUI(); + g.clearRect(Bangle.appRect); + Bangle.setUI(); + resolve(text); + }}); + }); */ + +}; diff --git a/apps/dragboard/metadata.json b/apps/dragboard/metadata.json new file mode 100644 index 000000000..f9c73ddde --- /dev/null +++ b/apps/dragboard/metadata.json @@ -0,0 +1,14 @@ +{ "id": "dragboard", + "name": "Dragboard", + "version":"0.05", + "description": "A library for text input via swiping keyboard", + "icon": "app.png", + "type":"textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS2"], + "screenshots": [{"url":"screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"textinput","url":"lib.js"} + ] +} diff --git a/apps/dragboard/screenshot.png b/apps/dragboard/screenshot.png new file mode 100644 index 000000000..dbe27f408 Binary files /dev/null and b/apps/dragboard/screenshot.png differ diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog index 811784b39..09804b82e 100644 --- a/apps/dtlaunch/ChangeLog +++ b/apps/dtlaunch/ChangeLog @@ -8,3 +8,7 @@ 0.08: Optimize line wrapping for Bangle 2 0.09: fix the trasparent widget bar if there are no widgets for Bangle 2 0.10: added "one click exit" setting for Bangle 2 +0.11: Fix bangle.js 1 white icons not displaying +0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock. +0.13: Added swipeExit setting so that left-right to exit is an option +0.14: Don't move pages when doing exit swipe. diff --git a/apps/dtlaunch/README.md b/apps/dtlaunch/README.md index bea20ef65..55c9f53b8 100644 --- a/apps/dtlaunch/README.md +++ b/apps/dtlaunch/README.md @@ -29,6 +29,6 @@ Bangle 2: **Touch** - icon to select, scond touch launches app -**Swipe Left** - move to next page of app icons +**Swipe Left/Up** - move to next page of app icons -**Swipe Right** - move to previous page of app icons +**Swipe Right/Down** - move to previous page of app icons diff --git a/apps/dtlaunch/app-b1.js b/apps/dtlaunch/app-b1.js index ec0569127..ed9cc778e 100644 --- a/apps/dtlaunch/app-b1.js +++ b/apps/dtlaunch/app-b1.js @@ -48,6 +48,7 @@ function draw_icon(p,n,selected) { var x = (n%3)*80; var y = n>2?130:40; (selected?g.setColor(0.3,0.3,0.3):g.setColor(0,0,0)).fillRect(x,y,x+79,y+89); + g.setColor(g.theme.fg); g.drawImage(s.read(apps[p*6+n].icon),x+10,y+10,{scale:1.25}); g.setColor(-1).setFontAlign(0,-1,0).setFont("6x8",1); var txt = apps[p*6+n].name.split(" "); diff --git a/apps/dtlaunch/app-b2.js b/apps/dtlaunch/app-b2.js index e0f7f825f..46194ec5d 100644 --- a/apps/dtlaunch/app-b2.js +++ b/apps/dtlaunch/app-b2.js @@ -6,7 +6,8 @@ var settings = Object.assign({ showClocks: true, showLaunchers: true, direct: false, - oneClickExit:false + oneClickExit:false, + swipeExit: false }, require('Storage').readJSON("dtlaunch.json", true) || {}); if( settings.oneClickExit) @@ -85,18 +86,25 @@ function drawPage(p){ g.flip(); } -Bangle.on("swipe",(dir)=>{ +Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{ selected = 0; oldselected=-1; - if (dir<0){ + if(settings.swipeExit && dirLeftRight==1) showClock(); + if (dirUpDown==-1||dirLeftRight==-1){ ++page; if (page>maxPage) page=0; drawPage(page); - } else { + } else if (dirUpDown==1||(dirLeftRight==1 && !settings.swipeExit)){ --page; if (page<0) page=maxPage; drawPage(page); - } + } }); +function showClock(){ + var app = require("Storage").readJSON('setting.json', 1).clock; + if (app) load(app); + else E.showMessage("clock\nnot found"); +} + function isTouched(p,n){ if (n<0 || n>3) return false; var x1 = (n%2)*72+XOFF; var y1 = n>1?72+YOFF:YOFF; diff --git a/apps/dtlaunch/metadata.json b/apps/dtlaunch/metadata.json index 7a4094e54..4a0b8067c 100644 --- a/apps/dtlaunch/metadata.json +++ b/apps/dtlaunch/metadata.json @@ -1,7 +1,7 @@ { "id": "dtlaunch", "name": "Desktop Launcher", - "version": "0.10", + "version": "0.14", "description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.", "screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}], "icon": "icon.png", diff --git a/apps/dtlaunch/settings-b2.js b/apps/dtlaunch/settings-b2.js index 8eca46a7e..7ead63be0 100644 --- a/apps/dtlaunch/settings-b2.js +++ b/apps/dtlaunch/settings-b2.js @@ -5,7 +5,8 @@ showClocks: true, showLaunchers: true, direct: false, - oneClickExit:false + oneClickExit:false, + swipeExit: false }, require('Storage').readJSON(FILE, true) || {}); function writeSettings() { @@ -39,6 +40,14 @@ writeSettings(); } }, + 'Swipe Exit': { + value: settings.swipeExit, + format: v => v?"On":"Off", + onchange: v => { + settings.swipeExit = v; + writeSettings(); + } + }, 'One click exit': { value: settings.oneClickExit, format: v => v?"On":"Off", diff --git a/apps/ffcniftyb/metadata.json b/apps/ffcniftyb/metadata.json index e4e099a51..73f93ed36 100644 --- a/apps/ffcniftyb/metadata.json +++ b/apps/ffcniftyb/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"ffcniftyb.app.js","url":"app.js"}, diff --git a/apps/flipper/ChangeLog b/apps/flipper/ChangeLog index 9db0e26c5..2e94a2286 100644 --- a/apps/flipper/ChangeLog +++ b/apps/flipper/ChangeLog @@ -1 +1,2 @@ 0.01: first release +0.02: updated dark theme bg2 color value diff --git a/apps/flipper/flipper.app.js b/apps/flipper/app.js similarity index 95% rename from apps/flipper/flipper.app.js rename to apps/flipper/app.js index 7171306b1..ad5aa383c 100644 --- a/apps/flipper/flipper.app.js +++ b/apps/flipper/app.js @@ -18,7 +18,7 @@ function flipTheme() { if (!g.theme.dark) { upd({ fg:cl("#fff"), bg:cl("#000"), - fg2:cl("#0ff"), bg2:cl("#000"), + fg2:cl("#fff"), bg2:cl("#004"), fgH:cl("#fff"), bgH:cl("#00f"), dark:true }); diff --git a/apps/flipper/flipper.png b/apps/flipper/app.png similarity index 100% rename from apps/flipper/flipper.png rename to apps/flipper/app.png diff --git a/apps/flipper/flipper.icon.js b/apps/flipper/icon.js similarity index 100% rename from apps/flipper/flipper.icon.js rename to apps/flipper/icon.js diff --git a/apps/flipper/metadata.json b/apps/flipper/metadata.json index aac4f1643..366145d95 100644 --- a/apps/flipper/metadata.json +++ b/apps/flipper/metadata.json @@ -2,17 +2,17 @@ { "id": "flipper", "name": "flipper", - "version": "0.01", + "version": "0.02", "description": "Switch between dark and light theme and vice versa, combine with pattern launcher and swipe to flip.", "readme":"README.md", - "screenshots": [{"url":"flipper.png"}], - "icon": "flipper.png", + "screenshots": [{"url":"app.png"}], + "icon": "app.png", "type": "app", - "tags": "game", + "tags": "tool", "supports": ["BANGLEJS2"], "allow_emulator": true, "storage": [ - {"name":"flipper.app.js","url":"flipper.app.js"}, - {"name":"flipper.img","url":"flipper.icon.js","evaluate":true} + {"name":"flipper.app.js","url":"app.js"}, + {"name":"flipper.img","url":"icon.js","evaluate":true} ] } diff --git a/apps/floralclk/metadata.json b/apps/floralclk/metadata.json index d4848b0d8..33ab6b8ae 100644 --- a/apps/floralclk/metadata.json +++ b/apps/floralclk/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"floralclk.app.js","url":"app.js"}, diff --git a/apps/ftclock/.gitignore b/apps/ftclock/.gitignore index b384cf1f2..304792757 100644 --- a/apps/ftclock/.gitignore +++ b/apps/ftclock/.gitignore @@ -1,4 +1,5 @@ -timezonedb.csv.zip +TimeZoneDB.csv.zip country.csv -zone.csv -timezone.csv +time_zone.csv +database.sql +readme.txt diff --git a/apps/ftclock/ChangeLog b/apps/ftclock/ChangeLog index c944dd9ac..83ec21ee6 100644 --- a/apps/ftclock/ChangeLog +++ b/apps/ftclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: first release 0.02: RAM efficient version of `fourTwentyTz.js` (as suggested by @gfwilliams). +0.03: `mkFourTwentyTz.js` now handles new timezonedb.com CSV format diff --git a/apps/ftclock/fourTwentyTz.js b/apps/ftclock/fourTwentyTz.js index 5fa6cdab7..7ef7418f6 100644 --- a/apps/ftclock/fourTwentyTz.js +++ b/apps/ftclock/fourTwentyTz.js @@ -1,33 +1,33 @@ // Generated by mkFourTwentyTz.js -// Wed Jan 12 2022 19:35:36 GMT+0200 (Israel Standard Time) -// Data source: https://timezonedb.com/files/timezonedb.csv.zip +// Sun Mar 27 2022 14:10:08 GMT+0300 (Israel Daylight Time) +// Data source: https://timezonedb.com/files/TimeZoneDB.csv.zip exports.offsets = [1380,1320,1260,1200,1140,1080,1020,960,900,840,780,720,660,600,540,480,420,360,300,240,180,120,60,0]; exports.timezones = function(offs) { switch (offs) { - case 1380: return ["Cape Verde, Cabo Verde","Scoresbysund, Greenland","Azores, Portugal"]; - case 1320: return ["Noronha, Brazil","South Georgia, South Georgia and the South Sandwich Islands"]; - case 1260: return ["Palmer, Antarctica","Rothera, Antarctica","Buenos Aires, Argentina","Cordoba, Argentina","Salta, Argentina","Jujuy, Argentina","Tucuman, Argentina","Catamarca, Argentina","La Rioja, Argentina","San Juan, Argentina","Mendoza, Argentina","San Luis, Argentina","Rio Gallegos, Argentina","Ushuaia, Argentina","Belem, Brazil","Fortaleza, Brazil","Recife, Brazil","Araguaina, Brazil","Maceio, Brazil","Bahia, Brazil","Sao Paulo, Brazil","Santarem, Brazil","Santiago, Chile","Punta Arenas, Chile","Stanley, Falkland Islands (Malvinas)","Cayenne, French Guiana","Nuuk, Greenland","Miquelon, Saint Pierre and Miquelon","Asuncion, Paraguay","Paramaribo, Suriname","Montevideo, Uruguay"]; - case 1200: return ["Antigua, Antigua and Barbuda","Anguilla, Anguilla","Aruba, Aruba","Barbados, Barbados","St Barthelemy, Saint Barthélemy","Bermuda, Bermuda","La Paz, Bolivia (Plurinational State of)","Kralendijk, Bonaire, Sint Eustatius and Saba","Campo Grande, Brazil","Cuiaba, Brazil","Porto Velho, Brazil","Boa Vista, Brazil","Manaus, Brazil","Halifax, Canada","Glace Bay, Canada","Moncton, Canada","Goose Bay, Canada","Blanc-Sablon, Canada","Curacao, Curaçao","Dominica, Dominica","Santo Domingo, Dominican Republic","Grenada, Grenada","Thule, Greenland","Guadeloupe, Guadeloupe","Guyana, Guyana","St Kitts, Saint Kitts and Nevis","St Lucia, Saint Lucia","Marigot, Saint Martin (French part)","Martinique, Martinique","Montserrat, Montserrat","Puerto Rico, Puerto Rico","Lower Princes, Sint Maarten (Dutch part)","Port of_Spain, Trinidad and Tobago","St Vincent, Saint Vincent and the Grenadines","Caracas, Venezuela (Bolivarian Republic of)","Tortola, Virgin Islands (British)","St Thomas, Virgin Islands (U.S.)"]; - case 1140: return ["Eirunepe, Brazil","Rio Branco, Brazil","Nassau, Bahamas","Toronto, Canada","Nipigon, Canada","Thunder Bay, Canada","Iqaluit, Canada","Pangnirtung, Canada","Atikokan, Canada","Easter, Chile","Bogota, Colombia","Havana, Cuba","Guayaquil, Ecuador","Port-au-Prince, Haiti","Jamaica, Jamaica","Cayman, Cayman Islands","Cancun, Mexico","Panama, Panama","Lima, Peru","Grand Turk, Turks and Caicos Islands","New York, United States of America","Detroit, United States of America","Louisville, Kentucky","Monticello, Kentucky","Indianapolis, Indiana","Vincennes, Indiana","Winamac, Indiana","Marengo, Indiana","Petersburg, Indiana","Vevay, Indiana"]; - case 1080: return ["Belize, Belize","Winnipeg, Canada","Rainy River, Canada","Resolute, Canada","Rankin Inlet, Canada","Regina, Canada","Swift Current, Canada","Costa Rica, Costa Rica","Galapagos, Ecuador","Guatemala, Guatemala","Tegucigalpa, Honduras","Mexico City, Mexico","Merida, Mexico","Monterrey, Mexico","Matamoros, Mexico","Bahia Banderas, Mexico","Managua, Nicaragua","El Salvador, El Salvador","Chicago, United States of America","Tell City, Indiana","Knox, Indiana","Menominee, United States of America","Center, North Dakota","New_Salem, North Dakota","Beulah, North Dakota"]; - case 1020: return ["Edmonton, Canada","Cambridge Bay, Canada","Yellowknife, Canada","Inuvik, Canada","Creston, Canada","Dawson Creek, Canada","Fort Nelson, Canada","Whitehorse, Canada","Dawson, Canada","Mazatlan, Mexico","Chihuahua, Mexico","Ojinaga, Mexico","Hermosillo, Mexico","Denver, United States of America","Boise, United States of America","Phoenix, United States of America"]; - case 960: return ["Vancouver, Canada","Tijuana, Mexico","Pitcairn, Pitcairn","Los Angeles, United States of America"]; - case 900: return ["Gambier, French Polynesia","Anchorage, United States of America","Juneau, United States of America","Sitka, United States of America","Metlakatla, United States of America","Yakutat, United States of America","Nome, United States of America"]; - case 840: return ["Rarotonga, Cook Islands","Kiritimati, Kiribati","Tahiti, French Polynesia","Adak, United States of America","Honolulu, United States of America"]; - case 780: return ["McMurdo, Antarctica","Pago Pago, American Samoa","Fiji, Fiji","Kanton, Kiribati","Niue, Niue","Auckland, New Zealand","Fakaofo, Tokelau","Tongatapu, Tonga","Midway, United States Minor Outlying Islands","Apia, Samoa"]; - case 720: return ["Tarawa, Kiribati","Majuro, Marshall Islands","Kwajalein, Marshall Islands","Norfolk, Norfolk Island","Nauru, Nauru","Kamchatka, Russian Federation","Anadyr, Russian Federation","Funafuti, Tuvalu","Wake, United States Minor Outlying Islands","Wallis, Wallis and Futuna"]; - case 660: return ["Casey, Antarctica","Lord Howe, Australia","Macquarie, Australia","Hobart, Australia","Melbourne, Australia","Sydney, Australia","Pohnpei, Micronesia (Federated States of)","Kosrae, Micronesia (Federated States of)","Noumea, New Caledonia","Bougainville, Papua New Guinea","Magadan, Russian Federation","Sakhalin, Russian Federation","Srednekolymsk, Russian Federation","Guadalcanal, Solomon Islands","Efate, Vanuatu"]; - case 600: return ["DumontDUrville, Antarctica","Brisbane, Australia","Lindeman, Australia","Chuuk, Micronesia (Federated States of)","Guam, Guam","Saipan, Northern Mariana Islands","Port Moresby, Papua New Guinea","Vladivostok, Russian Federation","Ust-Nera, Russian Federation"]; - case 540: return ["Jayapura, Indonesia","Tokyo, Japan","Pyongyang, Korea (Democratic People's Republic of)","Seoul, Korea, Republic of","Palau, Palau","Chita, Russian Federation","Yakutsk, Russian Federation","Khandyga, Russian Federation","Dili, Timor-Leste"]; + case 1380: return ["Cape Verde, Cape Verde"]; + case 1320: return ["Noronha, Brazil","Nuuk, Greenland","South Georgia, South Georgia and the South Sandwich Islands","Miquelon, Saint Pierre and Miquelon"]; + case 1260: return ["Palmer, Antarctica","Rothera, Antarctica","Buenos Aires, Argentina","Cordoba, Argentina","Salta, Argentina","Jujuy, Argentina","Tucuman, Argentina","Catamarca, Argentina","La Rioja, Argentina","San Juan, Argentina","Mendoza, Argentina","San Luis, Argentina","Rio Gallegos, Argentina","Ushuaia, Argentina","Bermuda, Bermuda","Belem, Brazil","Fortaleza, Brazil","Recife, Brazil","Araguaina, Brazil","Maceio, Brazil","Bahia, Brazil","Sao Paulo, Brazil","Santarem, Brazil","Halifax, Canada","Glace Bay, Canada","Moncton, Canada","Goose Bay, Canada","Santiago, Chile","Punta Arenas, Chile","Stanley, Falkland Islands (Malvinas)","Cayenne, French Guiana","Thule, Greenland","Paramaribo, Suriname","Montevideo, Uruguay"]; + case 1200: return ["Antigua, Antigua and Barbuda","Anguilla, Anguilla","Aruba, Aruba","Barbados, Barbados","St Barthelemy, Saint Barthélemy","La Paz, Bolivia, Plurinational State of","Kralendijk, Bonaire, Sint Eustatius and Saba","Campo Grande, Brazil","Cuiaba, Brazil","Porto Velho, Brazil","Boa Vista, Brazil","Manaus, Brazil","Nassau, Bahamas","Blanc-Sablon, Canada","Toronto, Canada","Nipigon, Canada","Thunder Bay, Canada","Iqaluit, Canada","Pangnirtung, Canada","Havana, Cuba","Curacao, Curaçao","Dominica, Dominica","Santo Domingo, Dominican Republic","Grenada, Grenada","Guadeloupe, Guadeloupe","Guyana, Guyana","Port-au-Prince, Haiti","St Kitts, Saint Kitts and Nevis","St Lucia, Saint Lucia","Marigot, Saint Martin (French part)","Martinique, Martinique","Montserrat, Montserrat","Puerto Rico, Puerto Rico","Asuncion, Paraguay","Lower Princes, Sint Maarten (Dutch part)","Grand Turk, Turks and Caicos Islands","Port of_Spain, Trinidad and Tobago","New York, United States","Detroit, United States","Louisville, Kentucky","Monticello, Kentucky","Indianapolis, Indiana","Vincennes, Indiana","Winamac, Indiana","Marengo, Indiana","Petersburg, Indiana","Vevay, Indiana","St Vincent, Saint Vincent and the Grenadines","Caracas, Venezuela, Bolivarian Republic of","Tortola, Virgin Islands, British","St Thomas, Virgin Islands, U.S."]; + case 1140: return ["Eirunepe, Brazil","Rio Branco, Brazil","Atikokan, Canada","Winnipeg, Canada","Rainy River, Canada","Resolute, Canada","Rankin Inlet, Canada","Easter, Chile","Bogota, Colombia","Guayaquil, Ecuador","Jamaica, Jamaica","Cayman, Cayman Islands","Cancun, Mexico","Matamoros, Mexico","Panama, Panama","Lima, Peru","Chicago, United States","Tell City, Indiana","Knox, Indiana","Menominee, United States","Center, North Dakota","New_Salem, North Dakota","Beulah, North Dakota"]; + case 1080: return ["Belize, Belize","Regina, Canada","Swift Current, Canada","Edmonton, Canada","Cambridge Bay, Canada","Yellowknife, Canada","Inuvik, Canada","Costa Rica, Costa Rica","Galapagos, Ecuador","Guatemala, Guatemala","Tegucigalpa, Honduras","Mexico City, Mexico","Merida, Mexico","Monterrey, Mexico","Ojinaga, Mexico","Bahia Banderas, Mexico","Managua, Nicaragua","El Salvador, El Salvador","Denver, United States","Boise, United States"]; + case 1020: return ["Creston, Canada","Dawson Creek, Canada","Fort Nelson, Canada","Whitehorse, Canada","Dawson, Canada","Vancouver, Canada","Mazatlan, Mexico","Chihuahua, Mexico","Hermosillo, Mexico","Tijuana, Mexico","Phoenix, United States","Los Angeles, United States"]; + case 960: return ["Pitcairn, Pitcairn","Anchorage, United States","Juneau, United States","Sitka, United States","Metlakatla, United States","Yakutat, United States","Nome, United States"]; + case 900: return ["Gambier, French Polynesia","Adak, United States"]; + case 840: return ["Rarotonga, Cook Islands","Kiritimati, Kiribati","Tahiti, French Polynesia","Honolulu, United States"]; + case 780: return ["McMurdo, Antarctica","Pago Pago, American Samoa","Kanton, Kiribati","Niue, Niue","Auckland, New Zealand","Fakaofo, Tokelau","Tongatapu, Tonga","Midway, United States Minor Outlying Islands","Apia, Samoa"]; + case 720: return ["Fiji, Fiji","Tarawa, Kiribati","Majuro, Marshall Islands","Kwajalein, Marshall Islands","Norfolk, Norfolk Island","Nauru, Nauru","Kamchatka, Russian Federation","Anadyr, Russian Federation","Funafuti, Tuvalu","Wake, United States Minor Outlying Islands","Wallis, Wallis and Futuna"]; + case 660: return ["Casey, Antarctica","Lord Howe, Australia","Macquarie, Australia","Hobart, Australia","Melbourne, Australia","Sydney, Australia","Pohnpei, Micronesia, Federated States of","Kosrae, Micronesia, Federated States of","Noumea, New Caledonia","Bougainville, Papua New Guinea","Magadan, Russian Federation","Sakhalin, Russian Federation","Srednekolymsk, Russian Federation","Guadalcanal, Solomon Islands","Efate, Vanuatu"]; + case 600: return ["DumontDUrville, Antarctica","Brisbane, Australia","Lindeman, Australia","Chuuk, Micronesia, Federated States of","Guam, Guam","Saipan, Northern Mariana Islands","Port Moresby, Papua New Guinea","Vladivostok, Russian Federation","Ust-Nera, Russian Federation"]; + case 540: return ["Jayapura, Indonesia","Tokyo, Japan","Pyongyang, Korea, Democratic People's Republic of","Seoul, Korea, Republic of","Palau, Palau","Chita, Russian Federation","Yakutsk, Russian Federation","Khandyga, Russian Federation","Dili, Timor-Leste"]; case 480: return ["Perth, Australia","Brunei, Brunei Darussalam","Shanghai, China","Hong Kong, Hong Kong","Makassar, Indonesia","Ulaanbaatar, Mongolia","Choibalsan, Mongolia","Macau, Macao","Kuala Lumpur, Malaysia","Kuching, Malaysia","Manila, Philippines","Irkutsk, Russian Federation","Singapore, Singapore","Taipei, Taiwan, Province of China"]; case 420: return ["Davis, Antarctica","Christmas, Christmas Island","Jakarta, Indonesia","Pontianak, Indonesia","Phnom Penh, Cambodia","Vientiane, Lao People's Democratic Republic","Hovd, Mongolia","Novosibirsk, Russian Federation","Barnaul, Russian Federation","Tomsk, Russian Federation","Novokuznetsk, Russian Federation","Krasnoyarsk, Russian Federation","Bangkok, Thailand","Ho Chi_Minh, Viet Nam"]; case 360: return ["Vostok, Antarctica","Dhaka, Bangladesh","Thimphu, Bhutan","Urumqi, China","Chagos, British Indian Ocean Territory","Bishkek, Kyrgyzstan","Almaty, Kazakhstan","Qostanay, Kazakhstan","Omsk, Russian Federation"]; case 300: return ["Mawson, Antarctica","Qyzylorda, Kazakhstan","Aqtobe, Kazakhstan","Aqtau, Kazakhstan","Atyrau, Kazakhstan","Oral, Kazakhstan","Maldives, Maldives","Karachi, Pakistan","Yekaterinburg, Russian Federation","Kerguelen, French Southern Territories","Dushanbe, Tajikistan","Ashgabat, Turkmenistan","Samarkand, Uzbekistan","Tashkent, Uzbekistan"]; case 240: return ["Dubai, United Arab Emirates","Yerevan, Armenia","Baku, Azerbaijan","Tbilisi, Georgia","Mauritius, Mauritius","Muscat, Oman","Reunion, Réunion","Astrakhan, Russian Federation","Saratov, Russian Federation","Ulyanovsk, Russian Federation","Samara, Russian Federation","Mahe, Seychelles"]; - case 180: return ["Syowa, Antarctica","Bahrain, Bahrain","Minsk, Belarus","Djibouti, Djibouti","Asmara, Eritrea","Addis Ababa, Ethiopia","Baghdad, Iraq","Nairobi, Kenya","Comoro, Comoros","Kuwait, Kuwait","Antananarivo, Madagascar","Qatar, Qatar","Moscow, Russian Federation","Simferopol, Ukraine","Kirov, Russian Federation","Volgograd, Russian Federation","Riyadh, Saudi Arabia","Mogadishu, Somalia","Istanbul, Turkey","Dar es_Salaam, Tanzania, United Republic of","Kampala, Uganda","Aden, Yemen","Mayotte, Mayotte"]; - case 120: return ["Mariehamn, Åland Islands","Sofia, Bulgaria","Bujumbura, Burundi","Gaborone, Botswana","Lubumbashi, Congo, Democratic Republic of the","Nicosia, Cyprus","Famagusta, Cyprus","Tallinn, Estonia","Cairo, Egypt","Helsinki, Finland","Athens, Greece","Jerusalem, Israel","Amman, Jordan","Beirut, Lebanon","Maseru, Lesotho","Vilnius, Lithuania","Riga, Latvia","Tripoli, Libya","Chisinau, Moldova, Republic of","Blantyre, Malawi","Maputo, Mozambique","Windhoek, Namibia","Gaza, Palestine, State of","Hebron, Palestine, State of","Bucharest, Romania","Kaliningrad, Russian Federation","Kigali, Rwanda","Khartoum, Sudan","Juba, South Sudan","Damascus, Syrian Arab Republic","Mbabane, Eswatini","Kiev, Ukraine","Uzhgorod, Ukraine","Zaporozhye, Ukraine","Johannesburg, South Africa","Lusaka, Zambia","Harare, Zimbabwe"]; - case 60: return ["Andorra, Andorra","Tirane, Albania","Luanda, Angola","Vienna, Austria","Sarajevo, Bosnia and Herzegovina","Brussels, Belgium","Porto-Novo, Benin","Kinshasa, Congo, Democratic Republic of the","Bangui, Central African Republic","Brazzaville, Congo","Zurich, Switzerland","Douala, Cameroon","Prague, Czechia","Berlin, Germany","Busingen, Germany","Copenhagen, Denmark","Algiers, Algeria","El Aaiun, Western Sahara","Madrid, Spain","Ceuta, Spain","Paris, France","Libreville, Gabon","Gibraltar, Gibraltar","Malabo, Equatorial Guinea","Zagreb, Croatia","Budapest, Hungary","Rome, Italy","Vaduz, Liechtenstein","Luxembourg, Luxembourg","Casablanca, Morocco","Monaco, Monaco","Podgorica, Montenegro","Skopje, North Macedonia","Malta, Malta","Niamey, Niger","Lagos, Nigeria","Amsterdam, Netherlands","Oslo, Norway","Warsaw, Poland","Belgrade, Serbia","Stockholm, Sweden","Ljubljana, Slovenia","Longyearbyen, Svalbard and Jan Mayen","Bratislava, Slovakia","San Marino, San Marino","Ndjamena, Chad","Tunis, Tunisia","Vatican, Holy See"]; - case 0: return ["Troll, Antarctica","Ouagadougou, Burkina Faso","Abidjan, Côte d'Ivoire","Canary, Spain","Faroe, Faroe Islands","London, United Kingdom of Great Britain and Northern Ireland","Guernsey, Guernsey","Accra, Ghana","Danmarkshavn, Greenland","Banjul, Gambia","Conakry, Guinea","Bissau, Guinea-Bissau","Dublin, Ireland","Isle of_Man, Isle of Man","Reykjavik, Iceland","Jersey, Jersey","Monrovia, Liberia","Bamako, Mali","Nouakchott, Mauritania","Lisbon, Portugal","Madeira, Portugal","St Helena, Saint Helena, Ascension and Tristan da Cunha","Freetown, Sierra Leone","Dakar, Senegal","Sao Tome, Sao Tome and Principe","Lome, Togo"]; + case 180: return ["Syowa, Antarctica","Mariehamn, Åland Islands","Sofia, Bulgaria","Bahrain, Bahrain","Minsk, Belarus","Nicosia, Cyprus","Famagusta, Cyprus","Djibouti, Djibouti","Tallinn, Estonia","Asmara, Eritrea","Addis Ababa, Ethiopia","Helsinki, Finland","Athens, Greece","Jerusalem, Israel","Baghdad, Iraq","Amman, Jordan","Nairobi, Kenya","Comoro, Comoros","Kuwait, Kuwait","Beirut, Lebanon","Vilnius, Lithuania","Riga, Latvia","Chisinau, Moldova, Republic of","Antananarivo, Madagascar","Gaza, Palestine, State of","Hebron, Palestine, State of","Qatar, Qatar","Bucharest, Romania","Moscow, Russian Federation","Simferopol, Ukraine","Kirov, Russian Federation","Volgograd, Russian Federation","Riyadh, Saudi Arabia","Mogadishu, Somalia","Damascus, Syrian Arab Republic","Istanbul, Turkey","Dar es_Salaam, Tanzania, United Republic of","Kiev, Ukraine","Uzhgorod, Ukraine","Zaporozhye, Ukraine","Kampala, Uganda","Aden, Yemen","Mayotte, Mayotte"]; + case 120: return ["Andorra, Andorra","Tirane, Albania","Troll, Antarctica","Vienna, Austria","Sarajevo, Bosnia and Herzegovina","Brussels, Belgium","Bujumbura, Burundi","Gaborone, Botswana","Lubumbashi, Congo, the Democratic Republic of the","Zurich, Switzerland","Prague, Czech Republic","Berlin, Germany","Busingen, Germany","Copenhagen, Denmark","Cairo, Egypt","Madrid, Spain","Ceuta, Spain","Paris, France","Gibraltar, Gibraltar","Zagreb, Croatia","Budapest, Hungary","Rome, Italy","Vaduz, Liechtenstein","Maseru, Lesotho","Luxembourg, Luxembourg","Tripoli, Libya","Monaco, Monaco","Podgorica, Montenegro","Skopje, Macedonia, the Former Yugoslav Republic of","Malta, Malta","Blantyre, Malawi","Maputo, Mozambique","Windhoek, Namibia","Amsterdam, Netherlands","Oslo, Norway","Warsaw, Poland","Belgrade, Serbia","Kaliningrad, Russian Federation","Kigali, Rwanda","Khartoum, Sudan","Stockholm, Sweden","Ljubljana, Slovenia","Longyearbyen, Svalbard and Jan Mayen","Bratislava, Slovakia","San Marino, San Marino","Juba, South Sudan","Mbabane, Swaziland","Vatican, Holy See (Vatican City State)","Johannesburg, South Africa","Lusaka, Zambia","Harare, Zimbabwe"]; + case 60: return ["Luanda, Angola","Porto-Novo, Benin","Kinshasa, Congo, the Democratic Republic of the","Bangui, Central African Republic","Brazzaville, Congo","Douala, Cameroon","Algiers, Algeria","Canary, Spain","Faroe, Faroe Islands","Libreville, Gabon","London, United Kingdom","Guernsey, Guernsey","Malabo, Equatorial Guinea","Dublin, Ireland","Isle of_Man, Isle of Man","Jersey, Jersey","Niamey, Niger","Lagos, Nigeria","Lisbon, Portugal","Madeira, Portugal","Ndjamena, Chad","Tunis, Tunisia"]; + case 0: return ["Ouagadougou, Burkina Faso","Abidjan, Côte d'Ivoire","El Aaiun, Western Sahara","Accra, Ghana","Danmarkshavn, Greenland","Scoresbysund, Greenland","Banjul, Gambia","Conakry, Guinea","Bissau, Guinea-Bissau","Reykjavik, Iceland","Monrovia, Liberia","Casablanca, Morocco","Bamako, Mali","Nouakchott, Mauritania","Azores, Portugal","St Helena, Saint Helena, Ascension and Tristan da Cunha","Freetown, Sierra Leone","Dakar, Senegal","Sao Tome, Sao Tome and Principe","Lome, Togo"]; default: return ["Houston, we have a bug."]; } }; diff --git a/apps/ftclock/metadata.json b/apps/ftclock/metadata.json index 3cbf5ce66..876feb1bb 100644 --- a/apps/ftclock/metadata.json +++ b/apps/ftclock/metadata.json @@ -1,7 +1,7 @@ { "id": "ftclock", "name": "Four Twenty Clock", - "version": "0.02", + "version": "0.03", "description": "A clock that tells when and where it's going to be 4:20 next", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}], diff --git a/apps/ftclock/mkFourTwentyTz.js b/apps/ftclock/mkFourTwentyTz.js index 4571c15f7..4e7829aa3 100644 --- a/apps/ftclock/mkFourTwentyTz.js +++ b/apps/ftclock/mkFourTwentyTz.js @@ -4,7 +4,7 @@ let csv = require('csv'); let countries = {}, zones = {}, offsdict = {}, - now = Date.now(); // we need this to find zone's current DST state + now = Date.now()/1000; // we need this to find zone's current DST state function handleWrite(err,bytes) { if (err) { @@ -19,10 +19,10 @@ fs.createReadStream(__dirname+'/country.csv') countries[r[0]] = r[1]; }) .on('end', () => { - fs.createReadStream(__dirname+'/zone.csv') + fs.createReadStream(__dirname+'/time_zone.csv') .pipe(csv.parse()) .on('data', (r) => { - let parts = r[2].replace('_',' ').split('/'); + let parts = r[0].replace('_',' ').split('/'); let city = parts[parts.length-1]; let country =''; if (parts.length>2) { // e.g. America/North_Dakota/New_Salem @@ -30,59 +30,51 @@ fs.createReadStream(__dirname+'/country.csv') } else { country = countries[r[1]]; // e.g. United States } - zones[parseInt(r[0])] = {"name": `${city}, ${country}`}; + zone = zones[r[0]] || { "name": `${city}, ${country}` }; + let starttime = parseInt(r[3] || "0"), // Bugger. They're feeding us blanks for UTC now + offs = parseInt(r[4]); + if (offs<0) { + offs += 60*60*24; + } + if (starttime { - fs.createReadStream(__dirname+'/timezone.csv') - .pipe(csv.parse()) - .on('data', (r) => { - code = parseInt(r[0]); - if (!(code in zones)) return; - starttime = parseInt(r[2] || "0"); // Bugger. They're feeding us blanks for UTC now - offs = parseInt(r[3]); - if (offs<0) { - offs += 60*60*24; - } - zone = zones[code]; - if (starttime { - for (z in zones) { - zone = zones[z]; - if (zone.offs%60) continue; // One a dem funky timezones. Ignore. - zonelist = offsdict[zone.offs] || []; - zonelist.push(zone.name); - offsdict[zone.offs] = zonelist; - } - offsets = []; - for (o in offsdict) { - offsets.unshift(parseInt(o)); - } - fs.open("fourTwentyTz.js","w", (err, fd) => { - if (err) { - console.log("Can't open output file"); - return; - } - fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite); - fs.write(fd, `// ${Date()}\n`, handleWrite); - fs.write(fd, "// Data source: https://timezonedb.com/files/timezonedb.csv.zip\n", handleWrite); - fs.write(fd, "exports.offsets = ", handleWrite); - fs.write(fd, JSON.stringify(offsets), handleWrite); - fs.write(fd, ";\n", handleWrite); - fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite); - fs.write(fd, " switch (offs) {\n", handleWrite); - for (i=0; i { + if (err) { + console.log("Can't open output file"); + return; + } + fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite); + fs.write(fd, `// ${Date()}\n`, handleWrite); + fs.write(fd, "// Data source: https://timezonedb.com/files/TimeZoneDB.csv.zip\n", handleWrite); + fs.write(fd, "exports.offsets = ", handleWrite); + fs.write(fd, JSON.stringify(offsets), handleWrite); + fs.write(fd, ";\n", handleWrite); + fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite); + fs.write(fd, " switch (offs) {\n", handleWrite); + for (i=0; i{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.setUI('clock'); +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/fuzzyw/fuzzyw.icon.js b/apps/fuzzyw/fuzzyw.icon.js new file mode 100644 index 000000000..acc7e2fcf --- /dev/null +++ b/apps/fuzzyw/fuzzyw.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/ABX8oYFD+AFE8AFE8IXE8YFKwFCj08h4FBocenEHCIPDjk4CoIFBhlwAoeMuIFEuBSBAoOI+AFD4HxGoQFB+AFD4P4uYFC8P4gYFD/w7BAFEfApfEj+B/Ecg/Ah8A+EMg/Dw0YseHj/Dw/8sfHAoPH/lhDoIFBwFwj4FB40AvkPAoU8v4dCAoIdDw04FIMP4EOgFwh47Bj8EvEfw/DJwgFXABY")) diff --git a/apps/fuzzyw/fuzzyw.png b/apps/fuzzyw/fuzzyw.png new file mode 100644 index 000000000..afd0b0f76 Binary files /dev/null and b/apps/fuzzyw/fuzzyw.png differ diff --git a/apps/fuzzyw/metadata.json b/apps/fuzzyw/metadata.json new file mode 100644 index 000000000..5d3045edb --- /dev/null +++ b/apps/fuzzyw/metadata.json @@ -0,0 +1,18 @@ +{ + "id":"fuzzyw", + "name":"Fuzzy Text Clock", + "shortName": "Fuzzy Text", + "version": "0.02", + "description": "An imprecise clock for when you're not in a rush", + "readme": "README.md", + "icon":"fuzzyw.png", + "screenshots": [{"url":"fuzzyw-light.png"},{"url":"fuzzyw-dark.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"fuzzyw.app.js","url":"fuzzyw.app.js"}, + {"name":"fuzzyw.img","url":"fuzzyw.icon.js","evaluate":true} + ] +} diff --git a/apps/game1024/ChangeLog b/apps/game1024/ChangeLog new file mode 100644 index 000000000..800fa6b9d --- /dev/null +++ b/apps/game1024/ChangeLog @@ -0,0 +1,10 @@ +0.01: Initial version +0.02: Temporary intermediate version +0.03: Basic colors +0.04: Bug fix score reset after Game Over, new icon +0.05: Chevron marker on the randomly added square +0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw +0.07: Optimized the mover algorithm for efficiency (work in progress) +0.08: Bug fix at end of the game with victorious splash and glorious orchestra +0.09: Added settings menu, removed symbol selection button (*), added highscore reset +0.10: fixed clockmode in settings diff --git a/apps/game1024/README.md b/apps/game1024/README.md new file mode 100644 index 000000000..a25683b68 --- /dev/null +++ b/apps/game1024/README.md @@ -0,0 +1,48 @@ + +# Play the game of 1024 + +Move the tiles by swiping left, right, up- or downward over the watchface. + +When two tiles with the same number are squashed together they will add up as exponentials: + +**1 + 1 = 2** or **A + A = D** which is a representation of **2^1 + 2^1 = 2^1 = 4** + +**2 + 2 = 3** or **B + B = C** which is a representation of **2^2 + 2^2 = 2^3 = 8** + +**3 + 3 = 4** or **C + C = D** which is a representation of **2^3 + 2^3 = 2^4 = 16** + +After each move a new tile will be added on a random empty square. The value can be 1 or 2, and will be marked with a chevron. + +So you can continue till you reach **1024** which equals **2^(10)**. So when you reach tile **10** you have won. + +The score is maintained by adding the outcome of the sum of all pairs of squashed tiles (4+16+4+8 etc.) + +Use the side **BTN** to exit the game, score and tile positions will be saved. + +## Buttons on the screen + + - Button **U**: Undo the last move. There are currently a maximum of 9 undo levels. The level is indicated with a small number in the lower righthand corner of the Undo button + - You can set the maximum undo level in the Apps settings menu. + + - Button **R**: Reset the game. The Highscore will be remembered. You will be prompted first. + - The highscore value can be reset in the Apps settings menu. + + Apps setting: ![Screenshot of the apps settings menu](./game1024_sc_dump_app_settings.png) + + - Stuff you can change in de 1024 Game settings: + - Symbols on the cells: numerical, alphabetical or Roman + - Undo levels [0-9] + - Exit: how to exit the game: long or short press + - Debug mode: on or off. This will log all kinds of stuff in the console of the Web IDE + - Reset Highsccore: Tired of looking at the old highscore? Now you can set it to 0 again. + +### Credits + +Game 1024 is based on Saming's 2048 and Misho M. Petkovic 1024game.org and conceptually similar to Threes by Asher Vollmer. + +In Dark theme with numbers: +![Screenshot from the Banglejs 2 watch with the game in dark theme](./game1024_sc_dump_dark_v0.09.png) + +In Light theme with characters: +![Screenshot from the Banglejs 2 watch with the game in light theme](./game1024_sc_dump_light.v0.09.png) + diff --git a/apps/game1024/app-icon.js b/apps/game1024/app-icon.js new file mode 100644 index 000000000..8e8b56d9f --- /dev/null +++ b/apps/game1024/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBkQAWkAyVgQXx5gAMCQOqAAeiC/4X/AAXdC6HP7gECn///oXH///+QXEn4XC4f/mf/AwQXEmczmQXD74QD7/8AQZHLFIPfC4QzC4ZICC5XPngXD/4CB5oXNIYQXG+YXSCYQXKkQXWU4oXbL5mjC5M/R5evC5PfniwBa5Gvd4gXE5/z7s/DQIXGl6PJ5v//5eCC46/F4YXCAgMzAoYXFkYXFABTvMC/4X0ACkCC/4XJu4AMCQOIAAeCC+0///zC6dz/8z/83C6V/CgN/+4XSn4DCF6ZcGC6Hyv53V+Z3WCgR3OkQAWA=")) \ No newline at end of file diff --git a/apps/game1024/app.js b/apps/game1024/app.js new file mode 100644 index 000000000..a82db4352 --- /dev/null +++ b/apps/game1024/app.js @@ -0,0 +1,708 @@ +let settings = Object.assign({ + // default values + maxUndoLevels: 4, + charIndex: 0, + clockMode: true, + debugMode: false, +}, require('Storage').readJSON("game1024.settings.json", true) || {}); + +const clockMode = settings.clockMode!==undefined ? settings.clockMode : true; +const debugMode = settings.debugMode!==undefined ? settings.debugMode : false; // #settings -- valid values are: true or false +const maxUndoLevels = settings.maxUndoLevels!==undefined ? settings.maxUndoLevels : 4; // #settings +const charIndex = settings.charIndex!==undefined ? settings.charIndex : 0; // #settings -- plain numbers on the grid + +delete settings; // remove unneeded settings from memory + +const middle = {x:Math.floor(g.getWidth()/2)-20, y: Math.floor(g.getHeight()/2)}; +const rows = 4, cols = 4; // #settings +const borderWidth = 6; +const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth; +const cellColors = [{bg:'#00FFFF', fg: '#000000'}, + {bg:'#FF00FF', fg: '#000000'}, {bg:'#808000', fg: '#FFFFFF'}, {bg:'#0000FF', fg: '#FFFFFF'}, {bg:'#008000', fg: '#FFFFFF'}, + {bg:'#800000', fg: '#FFFFFF'}, {bg:'#00FF00', fg: '#000000'}, {bg:'#000080', fg: '#FFFFFF'}, {bg:'#FFFF00', fg: '#000000'}, + {bg:'#800080', fg: '#FFFFFF'}, {bg:'#FF0000', fg: '#FFFFFF'}]; +const cellFonts = ["12x20", "12x20", "Vector:14"]; +const cellChars = [ + [0,1,2,3,4,5,6,7,8,9,10], + ['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], + ['0','I', 'II', 'III', 'IV', 'V', 'VI', 'VII','VIII', 'IX', 'X'] +]; + +const themeBg = g.theme.bg; + +const scores = { + currentScore: 0, + highScore: 0, + lastScores: [0], + add: function(val) { + this.currentScore = this.currentScore + Math.pow(2, val); + debug(() => console.log("new score=",this.currentScore)); + }, + addToUndo: function () { + this.lastScores.push(this.currentScore); + if (this.lastScores.length > maxUndoLevels) this.lastScores.shift(); + }, + undo: function () { + this.currentScore = this.lastScores.pop(); + debug(() => console.log("undo score =", this.currentScore, "rest:", this.lastScores)); + }, + reset: function () { + this.currentScore = 0; + this.lastScores = [0]; + }, + draw: function () { + g.setColor(btnAtribs.fg); + let ulCorner = {x: Bangle.appRect.x + 6, y: Bangle.appRect.y2 -22 }; + let lrCorner = {x: Bangle.appRect.x2, y: Bangle.appRect.y2 - 1}; + g.fillRect(ulCorner.x, ulCorner.y, lrCorner.x, lrCorner.y) + .setFont12x20(1) + .setFontAlign(0,0,0); + let scrX = Math.floor((ulCorner.x + lrCorner.x)/3); + let scrY = Math.floor((ulCorner.y + lrCorner.y)/2) + 1; + g.setColor('#000000') + .drawString(this.currentScore, scrX+1, scrY+1) + .setColor(btnAtribs.bg) + .drawString(this.currentScore, scrX, scrY); + scrX = Math.floor(4*(ulCorner.x + lrCorner.x)/5); + g.setFont("6x8:1x2") + .drawString(this.highScore, btnAtribs.x + Math.floor(btnAtribs.w/2), scrY); + }, + hsContents: function () { + return {"highScore": this.highScore, "lastScore": this.currentScore}; + }, + check: function () { + this.highScore = (this.currentScore > this.highScore) ? this.currentScore : this.highScore; + debug(() => console.log('highScore =', this.highScore)); + } +}; + +// snapshot interval is the number of moves after wich a snapshot is wriiten to file +const snInterval = 1; + +const snReadOnInit = true; +// a snapshot contains a json file dump of the last positions of the tiles on the board, including the scores +const snapshot = { + interval: snInterval, + snFileName: 'game1024.json', + counter: 0, + updCounter: function() { + this.counter = ++this.counter > this.interval ? 0 : this.counter; + }, + dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0}, + write: function() { + require("Storage").writeJSON(this.snFileName, this.dump); + }, + read: function () { + let sn = require("Storage").readJSON(this.snFileName, true); + if ((typeof sn == "undefined") || (sn.gridsize !== rows * cols)) { + require("Storage").writeJSON(this.snFileName, this.dump); + return false; + } else { + if ((typeof sn !== "undefined") && (sn.gridsize == rows * cols)){ + this.dump = sn; + return true; + } + } + }, + setDump: function () { + this.dump.expVals = []; + allSquares.forEach(sq => { + this.dump.expVals.push(sq.expVal); + }); + this.dump.score = scores.currentScore; + this.dump.highScore = scores.highScore; + }, + make: function () { + this.updCounter(); + if (this.counter == this.interval) { + this.setDump(); + this.write(); + debug(() => console.log("snapped the state of the game:", this.dump)); + } + }, + recover: function () { + if (this.read()) { + this.dump.expVals.forEach((val, idx) => { + allSquares[idx].setExpVal(val); + }); + scores.currentScore = this.dump.score ? this.dump.score : 0; + scores.highScore = this.dump.highScore ? this.dump.highScore : 0 ; + if (this.dump.hasOwnProperty('charIndex')) delete this.dump.charIndex; // depricated in v0.09 + } + }, + reset: function () { + this.dump.gridsize = rows * cols; + this.dump.expVals = []; + for (let i = 0; i< this.dump.gridsize; i++) { + this.dump.expVals[i] = 0; + } + this.dump.score = 0; + this.dump.highScore = scores.highScore; + this.write(); + debug(() => console.log("reset D U M P E D!", this.dump)); + } +}; +const btnAtribs = {x: 134, w: 42, h: 50, fg:'#C0C0C0', bg:'#800000'}; +const buttons = { + all: [], + draw: function () { + this.all.forEach(btn => { + btn.draw(); + }); + }, + add: function(btn) { + this.all.push(btn); + }, + isPopUpActive: false, + activatePopUp: function() { + this.isPopUpActive = true; + }, + deActivatePopUp: function() { + this.isPopUpActive = false; + } +}; +/** + * to the right = -1 + all tiles move to the left, begin with the outer righthand side tiles + moving 0 to max 3 places to the right + + find first tile beginning with bottom row, righthand side + */ + +const mover = { + gameWon: false, + direction: { + up: {name: 'up', step: 1, innerBegin: 0, innerEnd: rows-1, outerBegin: 0, outerEnd: cols-1, iter: rows -1, + sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i < rows -1 ? (i+1)*(cols) + o : -1;} + }, + down: {name: 'down', step:-1, innerBegin: rows-1, innerEnd: 0, outerBegin: cols-1, outerEnd: 0, iter: rows -1, + sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i > 0 ? (i-1)*(cols) + o : -1;} + }, + left: {name: 'left', step: 1, innerBegin: 0, innerEnd: cols-1, outerBegin: 0, outerEnd: rows-1, iter: cols -1, + sqIndex: function (i,o) {return o*(rows) + i;}, sqNextIndex: function (i,o) {return i < cols -1 ? o*(rows) + i +1 : -1;} + }, + right: {name: 'right', step:-1, innerBegin: cols-1, innerEnd: 0, outerBegin: rows-1, outerEnd: 0, iter: cols -1, + sqIndex: function (i,o) {return o*(rows) + i;}, sqNextIndex: function (i,o) {return i > 0 ? o*(rows) + i -1: -1;} + } + }, + anyLeft: function() { + let canContinue = false; + [this.direction.up,this.direction.left].forEach (dir => { + const step = dir.step; + // outer loop for all colums/rows + for (let n = dir.outerBegin; step*n <= step*dir.outerEnd; n=n+step) { + // lets move squares one position in a row or column, counting backwards starting from the and where the squares will end up + for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) { + const idx = dir.sqIndex(m,n); + const nextIdx = dir.sqNextIndex(m,n); + if (allSquares[idx].expVal == 0) { + canContinue = true; // there is an empty cell found + break; + } + if (nextIdx >= 0) { + if (allSquares[idx].expVal == allSquares[nextIdx].expVal) { + canContinue = true; // equal adjacent cells > 0 found + break; + } + if (allSquares[nextIdx].expVal == 0) { + canContinue = true; // there is an empty cell found + break; + } + } + if (canContinue) break; + } + if (canContinue) break; + } + }); + return canContinue; + }, + moveAndMerge: function (dir) { + const step = dir.step; + // outer loop for all colums/rows + for (let o = dir.outerBegin; step*o <= step*dir.outerEnd; o=o+step) { + + let allVals = allSquares.map(sq=>{return sq.getExpVal()}); + let allLineVals = []; + for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) { + allLineVals.push(allVals[dir.sqIndex(m,o)]); + } + + let sortedLineVals = allLineVals.filter((val)=>{return val>0;}); + let zeroLineVals = allLineVals.filter((val)=>{return val==0;}); + // merge the equally valued adjacent cells + let r=0; + while (r{return val>0;}); + sortedLineVals.filter((val)=>{return val==0;}).forEach((zero)=>{mergedLineVals.push(zero);}); + zeroLineVals.forEach((zero)=>{mergedLineVals.push(zero);}); + + let i = 0; + for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) { + let idx = dir.sqIndex(m,o); + allSquares[idx].setExpVal(mergedLineVals[i++]); + } + debug(()=>console.log("new allSquares values:", allSquares.map(sq=>{return sq.expVal}))); + } + } +}; +// Minimum number of pixels to interpret it as drag gesture +const dragThreshold = 10; + +// Maximum number of pixels to interpret a click from a drag event series +const clickThreshold = 3; + +let allSquares = []; + +class Button { + constructor(name, x0, y0, width, height, text, bg, fg, cb, enabled) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x0 + width; + this.y1 = y0 + height; + this.name = name; + this.cb = cb; + this.text = text; + this.bg = bg; + this.fg = fg; + this.font = "6x8:3"; + this.enabled = enabled; + } + disable() { + this.enabled = false; + } + enable() { + this.enabled = true; + } + draw() { + g.setColor(this.bg) + .fillRect(this.x0, this.y0, this.x1, this.y1) + .setFont(this.font) + .setFontAlign(0,0,0); + let strX = Math.floor((this.x0+this.x1)/2); + let strY = Math.floor((this.y0+this.y1)/2); + g.setColor("#000000") + .drawString(this.text, strX+2, strY+2) + .setColor(this.fg) + .drawString(this.text, strX, strY); + // buttons.push(this); + } + onClick() {if (typeof this.cb === 'function' && this.enabled) { + this.cb(this); + } + } +} + +class Cell { + constructor(x0, y0, width, idx) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x0 + width; + this.y1 = y0 + width; + this.expVal = 0; + this.previousExpVals=[]; + this.idx = idx; + // this.cb = cb; + this.isRndm = false; + this.ax = x0; + this.ay = Math.floor(0.2*width+y0); + this.bx = Math.floor(0.3*width+x0); + this.by = Math.floor(0.5*width+y0); + this.cx = x0; + this.cy = Math.floor(0.8*width+y0); + } + getColor(i) { + return cellColors[i >= cellColors.length ? cellColors.length -1 : i]; + } + drawBg() { + debug(()=>console.log("Drawbg!!")); + if (this.isRndm) { + debug(()=>console.log('Random: (ax)', this.ax)); + g.setColor(this.getColor(this.expVal).bg) + .fillRect(this.x0, this.y0, this.x1, this.y1) + .setColor(themeBg) + .fillPoly([this.cx,this.cy,this.bx,this.by,this.ax,this.ay]); + } else { + g.setColor(this.getColor(this.expVal).bg) + .fillRect(this.x0, this.y0, this.x1, this.y1); + } + } + drawNumber() { + if (this.expVal !== 0) { + g.setFont(cellFonts[charIndex]) + .setFontAlign(0,0,0); + let char = cellChars[charIndex][this.expVal]; + let strX = Math.floor((this.x0 + this.x1)/2); + let strY = Math.floor((this.y0 + this.y1)/2); + g.setColor(this.getColor(this.expVal).fg) + .drawString(char, strX, strY); + } + } + getExpVal() { + return this.expVal; + } + setExpVal(val) { + this.expVal = val; + } + getIdx() {return this.idx;} + pushToUndo() { + // remember this new step + this.previousExpVals.push(this.expVal); + // keep the undo list not longer than max undo levels + if (this.previousExpVals.length > maxUndoLevels) this.previousExpVals.shift(); + } + popFromUndo() { + // take one step back + if (this.previousExpVals.length > 0) { + this.expVal = this.previousExpVals.pop(); + } + } + removeUndo() { + this.previousExpVals=[0]; + } + setRndmFalse() { + this.isRndm = false; + } + setRndmTrue() { + this.isRndm = true; + } + drawRndmIndicator(){ + if (this.isRndm) { + debug(()=>console.log('Random: (ax)', this.ax)); + g.setColor(this.getColor(0).bg) + .fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy); + } + } +} + +function undoGame() { + + if (scores.lastScores.length) { + g.clear(); + allSquares.forEach(sq => { + sq.popFromUndo(); + sq.drawBg(); + sq.drawNumber(); + }); + scores.undo(); + scores.draw(); + buttons.draw(); + updUndoLvlIndex(); + snapshot.make(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + } +} +function addToUndo() { + allSquares.forEach(sq => { + sq.pushToUndo(); + }); + scores.addToUndo(); +} +function addToScore (val) { + scores.add(val); + if (val == 10) mover.gameWon = true; +} +function createGrid () { + let cn =0; + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + let x0 = borderWidth + c*(borderWidth + sqWidth) - (rows/2)*(2*borderWidth + sqWidth) + middle.x + Math.floor(sqWidth/3); + let y0 = borderWidth + r*(borderWidth + sqWidth) - (cols/2)*(2*borderWidth + sqWidth) + middle.y + Math.floor(sqWidth/3); + let cell = new Cell(x0, y0, sqWidth, c + r*cols); + allSquares.push(cell); + } + } +} +function messageGameOver () { + const c = (g.theme.dark) ? {"fg": "#FFFFFF", "bg": "#808080"} : {"fg": "#FF0000", "bg": "#000000"}; + g.setColor(c.bg) + .setFont12x20(2).setFontAlign(0,0,0) + .drawString("G A M E", middle.x+13, middle.y-24) + .drawString("O V E R !", middle.x+13, middle.y+24); + g.setColor(c.fg) + .drawString("G A M E", middle.x+12, middle.y-25) + .drawString("O V E R !", middle.x+12, middle.y+25); +} +function messageYouWin () { + const c = (g.theme.dark) ? {"fg": "#FFFFFF", "bg": "#808080"} : {"fg": "#FF0000", "bg": "#000000"}; + g.setColor(c.bg) + .setFont12x20(2) + .setFontAlign(0,0,0) + .drawString("YOU HAVE", middle.x+18, middle.y-24) + .drawString("W O N ! !", middle.x+18, middle.y+24); + g.setColor(c.fg) + .drawString("YOU HAVE", middle.x+17, middle.y-25) + .drawString("W O N ! !", middle.x+17, middle.y+25); + for (let r=0;r<4;r++){ + Bangle.buzz(200,0.2) + .then((result) => { + Bangle.buzz(200,0.5) + .then((result)=>{ + Bangle.buzz(200,0.8) + .then((result)=>{ + Bangle.buzz(200,1) + .then((result)=>{ + Bangle.buzz(500,0); + }) + }) + }) + }) + } +} +function makeRandomNumber () { + return Math.ceil(2*Math.random()); +} +function addRandomNumber() { + let emptySquaresIdxs = []; + allSquares.forEach(sq => { + if (sq.expVal == 0) emptySquaresIdxs.push(sq.getIdx()); + }); + if (emptySquaresIdxs.length > 0) { + let randomIdx = Math.floor( emptySquaresIdxs.length * Math.random() ); + allSquares[emptySquaresIdxs[randomIdx]].setExpVal(makeRandomNumber()); + allSquares[emptySquaresIdxs[randomIdx]].setRndmTrue(); + } +} +function drawGrid() { + allSquares.forEach(sq => { + sq.drawBg(); + // sq.drawRndmIndicator(); + sq.drawNumber(); + }); +} +function initGame() { + g.clear(); + // scores.read(); + createGrid(); + if (snReadOnInit) { + snapshot.recover(); + debug(() => console.log("R E C O V E R E D !", snapshot.dump)); + let sum = allSquares.reduce(function (tv, sq) {return (sq.expVal + tv) ;}, 0); + if (!sum) { + addRandomNumber(); + } + } else { + addRandomNumber(); + // addToUndo(); + } + addRandomNumber(); + drawGrid(); + scores.draw(); + buttons.draw(); + // #settings Clock mode allows short-press on button to exit + if(clockMode) Bangle.setUI("clock"); + // Load widgets + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} +function drawPopUp(message,cb) { + buttons.activatePopUp(); + g.setColor('#FFFFFF'); + let rDims = Bangle.appRect; + g.fillPoly([rDims.x+10, rDims.y+20, + rDims.x+20, rDims.y+10, + rDims.x2-30, rDims.y+10, + rDims.x2-20, rDims.y+20, + rDims.x2-20, rDims.y2-40, + rDims.x2-30, rDims.y2-30, + rDims.x+20, rDims.y2-30, + rDims.x+10, rDims.y2-40 + ]); + buttons.all.forEach(btn => {btn.disable();}); + const btnYes = new Button('yes', rDims.x+16, rDims.y2-88, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true); + const btnNo = new Button('no', rDims.x2-80, rDims.y2-88, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true); + btnYes.draw(); + btnNo.draw(); + g.setColor('#000000'); + g.setFont12x20(1); + g.setFontAlign(-1,-1,0); + g.drawString(message, rDims.x+20, rDims.y+20); + buttons.add(btnYes); + buttons.add(btnNo); + +} +function handlePopUpClicks(btn) { + const name = btn.name; + buttons.all.pop(); // remove the no button + buttons.all.pop(); // remove the yes button + buttons.all.forEach(b => {b.enable();}); // enable the remaining buttons again + debug(() => console.log("Button name =", name)); + buttons.deActivatePopUp(); + switch (name) { + case 'yes': + resetGame(); + break; + default: + g.clear(); + drawGrid(); + scores.draw(); + buttons.draw(); + updUndoLvlIndex(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + } +} +function resetGame() { + g.clear(); + scores.reset(); + mover.gameWon=false; + allSquares.forEach(sq => {sq.setExpVal(0);sq.removeUndo();sq.setRndmFalse();}); + addRandomNumber(); + addRandomNumber(); + drawGrid(); + scores.draw(); + buttons.draw(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} + +/** + * Function that can be used in test or development environment, or production. + * Depends on global constant debugMode + * @param {function} func function to call like console.log() + */ + const debug = (func) => { + if (debugMode) { + if (typeof func === 'function') func(); + } +}; + +// Handle a "click" event (only needed for menu button) +function handleclick(e) { + buttons.all.forEach(btn => { + if ((e.x >= btn.x0) && (e.x <= btn.x1) && (e.y >= btn.y0) && (e.y <= btn.y1)) { + btn.onClick(); + debug(() => console.log(btn.name)); + } + }); +} + +// Handle a drag event (moving the stones around) +function handledrag(e) { + // Stop moving things around when the popup message is active + // Bangleapps issue #1609 + if (!(buttons.isPopUpActive)) { + runGame((Math.abs(e.dx) > Math.abs(e.dy) ? + (e.dx > 0 ? mover.direction.right : mover.direction.left ) : + (e.dy > 0 ? mover.direction.down : mover.direction.up ))); + } +} +// Evaluate "drag" events from the UI and call handlers for drags or clicks +// The UI sends a drag as a series of events indicating partial movements +// of the finger. +// This class combines such parts to a long drag from start to end +// If the drag is short, it is interpreted as click, +// otherwise as drag. +// The approprate method is called with the data of the drag. +class Dragger { + + constructor(clickHandler, dragHandler, clickThreshold, dragThreshold) { + this.clickHandler = clickHandler; + this.dragHandler = dragHandler; + this.clickThreshold = (clickThreshold === undefined ? 3 : clickThreshold); + this.dragThreshold = (dragThreshold === undefined ? 10 : dragThreshold); + this.dx = 0; + this.dy = 0; + this.enabled = true; + } + + // Enable or disable the Dragger + setEnabled(b) { + this.enabled = b; + } + + // Handle a raw drag event from the UI + handleRawDrag(e) { + if (!this.enabled) + return; + this.dx += e.dx; // Always accumulate + this.dy += e.dy; + if (e.b === 0) { // Drag event ended: Evaluate full drag + if (Math.abs(this.dx) < this.clickThreshold && Math.abs(this.dy) < this.clickThreshold) + this.clickHandler({ + x: e.x - this.dx, + y: e.y - this.dy + }); // take x and y from the drag start + else if (Math.abs(this.dx) > this.dragThreshold || Math.abs(this.dy) > this.dragThreshold) + this.dragHandler({ + x: e.x - this.dx, + y: e.y - this.dy, + dx: this.dx, + dy: this.dy + }); + this.dx = 0; // Clear the drag accumulator + this.dy = 0; + } + } + + // Attach the drag evaluator to the UI + attach() { + Bangle.on("drag", e => this.handleRawDrag(e)); + } +} + +// Dragger is needed for interaction during the game +var dragger = new Dragger(handleclick, handledrag, clickThreshold, dragThreshold); + +// Disable dragger as board is not yet initialized +dragger.setEnabled(false); + +// Nevertheless attach it so that it is ready once the game starts +dragger.attach(); + +function runGame(dir){ + addToUndo(); + updUndoLvlIndex(); + mover.moveAndMerge(dir); + allSquares.forEach(sq => {sq.setRndmFalse();}); + addRandomNumber(); + drawGrid(); + scores.check(); + scores.draw(); + // scores.write(); + snapshot.make(); + dragger.setEnabled(true); + if (!(mover.anyLeft())) { + debug(() => console.log("G A M E O V E R !!")); + snapshot.reset(); + messageGameOver(); + } else { + if (mover.gameWon) { + debug(() => console.log("Y O U H A V E W O N !!")); + snapshot.reset(); + messageYouWin(); + } + } +} + +function updUndoLvlIndex() { + let x = 170; + let y = 60; + g.setColor(btnAtribs.fg) + .fillRect(x-6,y-6, 176, 67); + if (scores.lastScores.length > 0) { + g.setColor("#000000") + .setFont("4x6:2") + .drawString(scores.lastScores.length, x, y); + } +} + +buttons.add(new Button('undo', btnAtribs.x, 25, btnAtribs.w, btnAtribs.h, 'U', btnAtribs.fg, btnAtribs.bg, undoGame, true)); + +buttons.add(new Button('restart', btnAtribs.x, 106, btnAtribs.w, btnAtribs.h, 'R', btnAtribs.fg, btnAtribs.bg, function(){drawPopUp('Do you want\nto restart?',handlePopUpClicks);}, true)); + +initGame(); + +dragger.setEnabled(true); + +E.on('kill',function() { + this.write(); + debug(() => console.log("1024 game got killed!")); +}); \ No newline at end of file diff --git a/apps/game1024/game1024.app.info b/apps/game1024/game1024.app.info new file mode 100644 index 000000000..b1c9d84ce --- /dev/null +++ b/apps/game1024/game1024.app.info @@ -0,0 +1,6 @@ +require("Storage").write("timer.info",{ + "id":"game1024", + "name":"1024 Game", + "src":"game1024.app.js", + "icon":"game1024.img" +}); \ No newline at end of file diff --git a/apps/game1024/game1024.json b/apps/game1024/game1024.json new file mode 100644 index 000000000..3749649ee --- /dev/null +++ b/apps/game1024/game1024.json @@ -0,0 +1 @@ +{"gridsize": 16, "expVals": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "score": 0, "highScore": 0, "charIndex": 1} \ No newline at end of file diff --git a/apps/game1024/game1024.png b/apps/game1024/game1024.png new file mode 100644 index 000000000..c0f7eaf21 Binary files /dev/null and b/apps/game1024/game1024.png differ diff --git a/apps/game1024/game1024_sc_dump_app_settings.png b/apps/game1024/game1024_sc_dump_app_settings.png new file mode 100644 index 000000000..6c50898b3 Binary files /dev/null and b/apps/game1024/game1024_sc_dump_app_settings.png differ diff --git a/apps/game1024/game1024_sc_dump_dark_v0.09.png b/apps/game1024/game1024_sc_dump_dark_v0.09.png new file mode 100644 index 000000000..6c14f78e8 Binary files /dev/null and b/apps/game1024/game1024_sc_dump_dark_v0.09.png differ diff --git a/apps/game1024/game1024_sc_dump_light.v0.09.png b/apps/game1024/game1024_sc_dump_light.v0.09.png new file mode 100644 index 000000000..1218079e8 Binary files /dev/null and b/apps/game1024/game1024_sc_dump_light.v0.09.png differ diff --git a/apps/game1024/metadata.json b/apps/game1024/metadata.json new file mode 100644 index 000000000..728b5dc0e --- /dev/null +++ b/apps/game1024/metadata.json @@ -0,0 +1,18 @@ +{ "id": "game1024", + "name": "1024 Game", + "shortName" : "1024 Game", + "version": "0.10", + "icon": "game1024.png", + "screenshots": [ {"url":"screenshot.png" } ], + "readme":"README.md", + "description": "Swipe the squares up, down, to the left or right, join the numbers and get to the 10 (2^1024), J or X tile!", + "type": "app", + "tags": "game,puzzle", + "allow_emulator": true, + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"game1024.app.js","url":"app.js"}, + {"name":"game1024.settings.js","url":"settings.js"}, + {"name":"game1024.img","url":"app-icon.js","evaluate":true} + ] + } diff --git a/apps/game1024/screenshot.png b/apps/game1024/screenshot.png new file mode 100644 index 000000000..b8d204cd3 Binary files /dev/null and b/apps/game1024/screenshot.png differ diff --git a/apps/game1024/settings.js b/apps/game1024/settings.js new file mode 100644 index 000000000..24a972600 --- /dev/null +++ b/apps/game1024/settings.js @@ -0,0 +1,70 @@ +(function(back) { + var FILE = "game1024.settings.json"; + var scoreFile = "game1024.json"; + // Load settings + var settings = Object.assign({ + maxUndoLevels: 5, + charIndex: 0, + clockMode: true, + debugMode: false, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + var symbols = ["1 2 3 ...", "A B C ...", "I II III..."]; + var settingsMenu = { + "" : { "title" : "1024 Game" }, + "< Back" : () => back(), + "Symbols": { + value: 0|settings.charIndex, + min:0,max:symbols.length-1, + format: v=>symbols[v], + onchange: v=> { settings.charIndex=v; writeSettings();} + } + , + "Undo levels:": { + value: 0|settings.maxUndoLevels, // 0| converts undefined to 0 + min: 0, max: 9, + onchange: v => { + settings.maxUndoLevels = v; + writeSettings(); + } + }, + "Exit press:": { + value: !settings.clockMode, // ! converts undefined to true + format: v => v?"short":"long", + onchange: v => { + settings.clockMode = v; + writeSettings(); + }, + }, + "Debug mode:": { + value: !!settings.debugMode, // !! converts undefined to false + format: v => v?"On":"Off", + onchange: v => { + settings.debugMode = v; + writeSettings(); + } + }, + "Reset Highscore": () => { + E.showPrompt('Reset Highscore?').then((v) => { + let delay = 50; + if (v) { + delay = 500; + let sF = require("Storage").readJSON(scoreFile, true); + if (typeof sF !== "undefined") { + E.showMessage('Resetting'); + sF.highScore = 0; + require("Storage").writeJSON(scoreFile, sF); + } else { + E.showMessage('No highscore!'); + } + } + setTimeout(() => E.showMenu(settingsMenu), delay); + }); + } + } + // Show the menu + E.showMenu(settingsMenu); + }) diff --git a/apps/gbdebug/app-icon.js b/apps/gbdebug/app-icon.js index a701ef3a9..0cecad73b 100644 --- a/apps/gbdebug/app-icon.js +++ b/apps/gbdebug/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEw4cBzsE/4AClMywH680rlOW9N9kmSpICnyBBBgQRMkBUDgIRKoBoGGRYAFHBGARpARHT5MJKxQAFLgzELCIlIBQkSCIsEPRKBHCIYbGoIRFiQRJhJgFCISeEBwMQOQykCCIqlBpMEBIgRHOQYRIYQbPDhAbBNwgRJVwOCTIgRFMAJKDgQRGOQprBCIMSGogHBJwwbBkC2FCJNbUgMNwHYBYPJCIhODju0yFNCIUGCJGCoE2NwO24EAmw1FHgWCpMGgQOBBIMwCJGSpMmyAjDCI6eBCIWAhu2I4IRCUIYREk+Ah3brEB2CzFAAIRCl3b23btsNCJckjoRC1h2CyAREtoNC9oDC2isCCIgHBjdt5MtCJj2CowjD2uyCIOSCI83lu123tAQIRI4EB28/++39/0mwRCoARCgbfByU51/3rev+mWCIQwCPok0EYIRB/gRDpJ+EcYQRJkARQdgq/Bl5HE7IRDZAltwAREyXbCIbIFgEfCIXsBwQCDQAYRNLgvfCIXtCI44Dm3JCIUlYoYCGkrjBk9bxMkyy9CChICFA=")) +require("heatshrink").decompress(atob("mEw4cBzsE/4AClMywH680rlOW9N9kmSpICnyBBBgQRMkBUDgIRKoBoGGRYAFHBGARpARHT5MJKxQAFLgzELCIlIBQkSCIsEPRKBHCIYbGoIRFiQRJhJgFCISeEBwMQOQykCCIqlBpMEBIgRHOQYRIYQbPDhAbBNwgRJVwOCTIgRFMAJKDgQRGOQprBCIMSGogHBJwwbBkC2FCJNbUgMNwHYBYPJCIhODju0yFNCIUGCJGCoE2NwO24EAmw1FHgWCpMGgQOBBIMwCJGSpMmyAjDCI6eBCIWAhu2I4IRCUIYREk+Ah3brEB2CzFAAIRCl3b23btsNCJckjoRC1h2CyAREtoNC9oDC2isCCIgHBjdt5MtCJj2CowjD2uyCIOSCI83lu123tAQIRI4EB28/++39/0mwRCoARCgbfByU51/3rev+mWCIQwCPok0EYIRB/gRDpJ+EcYQRJkARQdgq/Bl5HE7IRDZAltwAREyXbCIbIFgEfCIXsBwQCDQAYRNLgvfCIXtCI44Dm3JCIUlYoYCGkrjBk9bxMkyy9CChICFA=")) \ No newline at end of file diff --git a/apps/gbmusic/app.js b/apps/gbmusic/app.js index 5252ac0ac..49f54dac9 100644 --- a/apps/gbmusic/app.js +++ b/apps/gbmusic/app.js @@ -86,7 +86,8 @@ function infoColor(name) { * @param l */ function rScroller(l) { - g.setFont("Vector", Math.round(g.getHeight()*l.fsz.slice(0, -1)/100)); + var size=l.font.split(":")[1].slice(0,-1); + g.setFont("Vector", Math.round(g.getHeight()*size/100)); const w = g.stringWidth(l.label)+40, y = l.y+l.h/2; l.offset = l.offset%w; diff --git a/apps/gbridge/metadata.json b/apps/gbridge/metadata.json index 0c46aa852..db7119758 100644 --- a/apps/gbridge/metadata.json +++ b/apps/gbridge/metadata.json @@ -2,7 +2,7 @@ "id": "gbridge", "name": "Gadgetbridge", "version": "0.26", - "description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android' Bangle.js app instead.", + "description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android Integration' Bangle.js app instead.", "icon": "app.png", "type": "widget", "tags": "tool,system,android,widget", diff --git a/apps/glbasic/ChangeLog b/apps/glbasic/ChangeLog new file mode 100644 index 000000000..89aee01f8 --- /dev/null +++ b/apps/glbasic/ChangeLog @@ -0,0 +1,2 @@ +0.20: New App! + diff --git a/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js b/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js new file mode 100644 index 000000000..d51d02a10 --- /dev/null +++ b/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js @@ -0,0 +1,114 @@ + +Graphics.prototype.setFontLECO1976Regular42 = function(scale) { + // Actual height 42 (41 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16)); +}; + +Graphics.prototype.setFontLECO1976Regular22 = function(scale) { + // Actual height 22 (21 - 0) +g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16)); +}; + + +require("Font7x11Numeric7Seg").add(Graphics); + + +var temperature = 13; + +//the following 2 sections are used from waveclk to schedule minutely updates +// timeout used to update every minute +var drawTimeout; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +function drawBackground() { + g.setBgColor(0,0,0); + g.setColor(1,1,1); + g.clear(); +} + +function digit(num){ + return String.fromCharCode(num + 48); +} +function timeString(h, m){ + return digit(h/10) + digit(h%10) + ":" + digit(m/10) + digit(m%10); +} +function dayString(w){ + return digit(w/10) + digit(w%10); +} + +function getSteps() { + if (WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom.getSteps(); + } + return '????'; +} + + + +function draw(){ + drawBackground(); + var date = new Date(); + var h = date.getHours(), m = date.getMinutes(); + var d = date.getDate(), w = date.getDay(); + + g.setBgColor(0,0,0); + g.setColor(1,1,1); + + + // g.setFont('Vector', 30); + // g.setFont("7x11Numeric7Seg", 5); + g.setFontLECO1976Regular42(); + g.setFontAlign(0, -1); + g.drawString(timeString(h, m), g.getWidth()/2,28); + g.drawString(dayString(w), g.getWidth()*3/4,88); + g.setColor(0,1,0); + g.fillRect(0,76,g.getWidth(), 80); + g.reset(); + + // Steps + g.setFontLECO1976Regular22(); + g.setFontAlign(-1, -1); + g.drawString(getSteps(), 8, 88); + + // g.drawString(temperature, 4, 108); + + + // widget redraw + Bangle.drawWidgets(); + queueDraw(); +} + + +//////////////////////////////////////////////////// +// Bangle.setBarometerPower(true); + +Bangle.loadWidgets(); +draw(); + + +// Bangle.on('pressure', function(e){ +// temperature = e.temperature; +// draw(); +// }); + +//the following section is also from waveclk +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.setUI("clock"); + +Bangle.drawWidgets(); \ No newline at end of file diff --git a/apps/glbasic/glbasic.app.js b/apps/glbasic/glbasic.app.js new file mode 100644 index 000000000..ff7837ada --- /dev/null +++ b/apps/glbasic/glbasic.app.js @@ -0,0 +1,202 @@ +Graphics.prototype.setFontLECO1976Regular42 = function (scale) { + // Actual height 42 (41 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60 + (scale << 8) + (1 << 16)); +}; + +Graphics.prototype.setFontLECO1976Regular22 = function (scale) { + // Actual height 22 (21 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22 + (scale << 8) + (1 << 16)); +}; + + +require("Font7x11Numeric7Seg").add(Graphics); + + +// the following 2 sections are used from waveclk to schedule minutely updates +// timeout used to update every minute +var drawTimeout; + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function () { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +function drawBackground() { + g.setBgColor(0, 0, 0); + g.setColor(1, 1, 1); + g.clear(); +} + +function digit(num) { + return String.fromCharCode(num + 48); +} + +function timeString(h, m) { + return digit(h / 10) + digit(h % 10) + ":" + digit(m / 10) + digit(m % 10); +} + +function dayString(w) { + return digit(w / 10) + digit(w % 10); +} + +function getSteps() { + if (WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom.getSteps(); + } + return '????'; +} + +/** + * draws calender week view (-1,0,1) for given date + */ +function drawCal() { + d = /*this.date ? this.date : */ new Date(); + + const DAY_NAME_FONT_SIZE = 10; + const CAL_Y = g.getHeight() - 44; // Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3; + const CAL_AREA_H = 44; // g.getHeight()-CAL_Y+24; //+24: top widgtes only + const CELL_W = g.getWidth() / 7; //cell width + const CELL_H = (CAL_AREA_H - DAY_NAME_FONT_SIZE) / 3; //cell heigth + const DAY_NUM_FONT_SIZE = Math.min(CELL_H + 3, 15); //size down, max 15 + + const wdStrt = 1; + + const ABR_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const IS_SUNDAY = [1, 0, 0, 0, 0, 1, 1]; // what days are sunday? + const nrgb = ["#000", "#FFF", "#F00", "#0F0", "#00F", "#FF0"]; //fg, r ,g , b + const suClr = 5; // sunday color fg + const tdyMrkClr = 3; // today bk + const tdyNumClr = 0; // today fg + + g.setFont("Vector", DAY_NAME_FONT_SIZE + 3); + g.setColor(nrgb[1]); + g.setFontAlign(-1, -1); + // g.clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H); + + //draw grid & Headline + const dNames = ABR_DAY.map((a) => a.length <= 2 ? a : a.substr(0, 2)); //force shrt 2 + for (var dNo = 0; dNo < dNames.length; dNo++) { + const dIdx = wdStrt >= 0 ? ((wdStrt + dNo) % 7) : ((dNo + d.getDay() + 4) % 7); + const dName = dNames[dIdx]; + // if(dNo>0) { g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1);} + + + var colTx = 0; + var colBk = 1; + if (IS_SUNDAY[dIdx]) { + colBk = suClr; + } + + g.setColor(nrgb[colBk]); + g.fillRect(dNo * CELL_W, CAL_Y, dNo * CELL_W + CELL_W, CAL_Y + DAY_NAME_FONT_SIZE - 1); + g.setColor(nrgb[colTx]); + g.drawString(dName, dNo * CELL_W + (CELL_W - g.stringWidth(dName)) / 2 + 2, CAL_Y - 1); + // g.setColor(nrgb[clTxt]); + } + g.setColor(nrgb[1]); + var nextY = CAL_Y + DAY_NAME_FONT_SIZE; + + // horizontal lines + // for(i=0; i<3; i++){ const y=nextY+i*CELL_H; g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); } + + g.setFont("Vector", DAY_NUM_FONT_SIZE); + + g.setFont("7x11Numeric7Seg", 1); + + //write days + const tdyDate = d.getDate(); + const days = wdStrt >= 0 ? 7 + ((7 + d.getDay() - wdStrt) % 7) : 10; //start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days + var rD = new Date(d.getTime()); + rD.setDate(rD.getDate() - days); + var rDate = rD.getDate(); + for (var y = 0; y < 3; y++) { + for (var x = 0; x < dNames.length; x++) { + if (rDate === tdyDate) { //today + g.setColor(nrgb[tdyMrkClr]); //today marker color or fg color + + // rectangle + g.fillRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1); + g.setColor(nrgb[tdyNumClr]); //today color or fg color + + // simulate "bold" + g.drawString(rDate, 1 + x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y)); + + } else if (IS_SUNDAY[rD.getDay()]) { //sundays + g.setColor(nrgb[suClr]); + } else { //default + g.setColor(nrgb[1]); + } + g.drawString(rDate, x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y)); + rD.setDate(rDate + 1); + rDate = rD.getDate(); + } + } +} + + +function draw() { + g.reset(); + drawBackground(); + var date = new Date(); + var h = date.getHours(), + m = date.getMinutes(); + var d = date.getDate(), + w = date.getDay(); // d=1..31; w=0..6 + + g.setBgColor(0, 0, 0); + g.setColor(1, 1, 1); + + + // g.setFont('Vector', 30); + // g.setFont("7x11Numeric7Seg", 5); + g.setFontLECO1976Regular42(); + g.setFontAlign(0, -1); + g.drawString(timeString(h, m), g.getWidth() / 2, 28); + g.drawString(dayString(d), g.getWidth() * 3 / 4, 88); + g.setColor(0, 1, 0); + g.fillRect(0, 76, g.getWidth(), 80); + g.reset(); + + // Steps + g.setFontLECO1976Regular22(); + g.setFontAlign(-1, -1); + g.drawString(getSteps(), 8, 88); + + drawCal(); + + + // widget redraw + Bangle.drawWidgets(); + queueDraw(); +} + + +//////////////////////////////////////////////////// +// Bangle.setBarometerPower(true); + +Bangle.loadWidgets(); +draw(); + + +// Bangle.on('pressure', function(e){ +// temperature = e.temperature; +// draw(); +// }); + +//the following section is also from waveclk +Bangle.on('lcdPower', on => { + if (on) { + draw(); // draw immediately, queue redraw + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.setUI("clock"); + +Bangle.drawWidgets(); diff --git a/apps/glbasic/glbasic.icon.js b/apps/glbasic/glbasic.icon.js new file mode 100644 index 000000000..0c3280635 --- /dev/null +++ b/apps/glbasic/glbasic.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///ssp4XthFCBwUBqoABqAaGBZcFBZdX1W1qgLHrwLKqv/6oLJAAILHioLJn5qBAAYLEBQoLeHQQABv4LjGAgLYq2qAAOlBbBHFBdPAKcQLdWcb7jAAoLcn4LKgEVHQVUBQsAgoLLq//6oLIr2q2oXJBZQvCqALGgILTA=")) diff --git a/apps/glbasic/glbasic_screenshot.png b/apps/glbasic/glbasic_screenshot.png new file mode 100644 index 000000000..151ee38b9 Binary files /dev/null and b/apps/glbasic/glbasic_screenshot.png differ diff --git a/apps/glbasic/icon48.png b/apps/glbasic/icon48.png new file mode 100644 index 000000000..368e08750 Binary files /dev/null and b/apps/glbasic/icon48.png differ diff --git a/apps/glbasic/metadata.json b/apps/glbasic/metadata.json new file mode 100644 index 000000000..7c79097da --- /dev/null +++ b/apps/glbasic/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "glbasic", + "name": "GLBasic Clock", + "shortName": "GLBasic", + "version": "0.20", + "description": "A clock with large numbers", + "dependencies": {"widpedom":"app"}, + "icon": "icon48.png", + "screenshots": [{"url":"glbasic_screenshot.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS", "BANGLEJS2"], + "storage": [ + {"name":"glbasic.app.js","url":"glbasic.app.js"}, + {"name":"glbasic.img","url":"glbasic.icon.js","evaluate":true} + ] +} diff --git a/apps/golfview/ChangeLog b/apps/golfview/ChangeLog new file mode 100644 index 000000000..909b8feca --- /dev/null +++ b/apps/golfview/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! Very limited course support. +0.02: Course search added to BangleApps page \ No newline at end of file diff --git a/apps/golfview/LICENSE b/apps/golfview/LICENSE new file mode 100644 index 000000000..18c8d5fc3 --- /dev/null +++ b/apps/golfview/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Jason Dekarske + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/apps/golfview/README.md b/apps/golfview/README.md new file mode 100644 index 000000000..41aae02a0 --- /dev/null +++ b/apps/golfview/README.md @@ -0,0 +1,33 @@ +# Golf View + +This app leverages open source map data to give you a birds eye view of your golf game! See a preview of any hole as well as your realtime distance to the green and position on the hole. + +![hole3](screenshot.png) + +## Usage + +Swipe left and right to select your hole. Use the GPS assist app to get a faster GPS fix. + +## Contributions + +The performance of this app depends on the accuracy and consistency of user-submitted maps. + +- See official mapping guidelines [here](https://wiki.openstreetmap.org/wiki/Tag:leisure%3Dgolf_course). +- All holes and features must be within the target course's area. +- Supported features are greens, fairways, tees, bunkers, water hazards and holes. +- All features for a given hole should have the "ref" tag with the hole number as value. Shared features should list ref values separated by ';'. [example](https://www.openstreetmap.org/way/36896320). +- here must be 18 holes and they must have the following tags: handicap, par, ref, dist. +- For any mapping assistance or issues, please file in the official repo. + +[Example Course](https://www.openstreetmap.org/way/25447898) +## Controls + +Swipe to change holes and tap to see a green closeup. + +## Requests/Creator + +[Jason Dekarske](https://github.com/jdekarske) + +## Attribution + +[© OpenStreetMap contributors](https://www.openstreetmap.org/copyright) diff --git a/apps/golfview/custom.html b/apps/golfview/custom.html new file mode 100644 index 000000000..f80e8dee5 --- /dev/null +++ b/apps/golfview/custom.html @@ -0,0 +1,235 @@ + + + + + + + + + + + +
+
+ +
+ + +
+
+
+ +
+

No course loaded, please search for a course then choose 'select'.

+ + +
+
+

A course needs a few things to be parsed correctly by this tool.

+
    +
  • See official mapping guidelines here.
  • +
  • All holes and features must be within the target course's area.
  • +
  • Supported features are greens, fairways, tees, bunkers, water hazards and holes.
  • +
  • All features for a given hole should have the "ref" tag with the hole number as value. Shared features + should + list ref values separated by ';'. example.
  • +
  • There must be 18 holes and they must have the following tags: handicap, par, ref, dist
  • +
  • For any mapping assistance or issues, please file in the official + repo
  • +
+ Example Course +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/apps/golfview/golfview-icon.js b/apps/golfview/golfview-icon.js new file mode 100644 index 000000000..71049197c --- /dev/null +++ b/apps/golfview/golfview-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("lEowcF23btoCRpMkyVKBxHayQOCpNbBw/UBwkrBw21BYQCCkoOIy/JCIetI4+X+QgEBxNIBxFqBxFWBxAsEBxHpBYPbsgOFqQOEy3btgOLDoIOrrYOKMoQOKOgYsKBx1KBwaGBWYgOBkVJBwKGBYQwOBiVJlIdCkmVBxdZfwwOGF4IONkoLCB2J3BBxkgQwQOKWYlJlYOUtQORtskzQOHtoOE7QOLAQdbBw21BydKBYgCD6gODBYwCNA")) diff --git a/apps/golfview/golfview.js b/apps/golfview/golfview.js new file mode 100644 index 000000000..537bd93dd --- /dev/null +++ b/apps/golfview/golfview.js @@ -0,0 +1,214 @@ +// maptools.js +const EARTHRADIUS = 6371000; //km + +function radians(a) { + return a * Math.PI / 180; +} + +function degrees(a) { + let d = a * 180 / Math.PI; + return (d + 360) % 360; +} + +function toXY(a, origin) { + let pt = { + x: 0, + y: 0 + }; + + pt.x = EARTHRADIUS * radians(a.lon - origin.lon) * Math.cos(radians((a.lat + origin.lat) / 2)); + pt.y = EARTHRADIUS * radians(origin.lat - a.lat); + return pt; +} + +function arraytoXY(array, origin) { + let out = []; + for (var j in array) { + let newpt = toXY(array[j], origin); + out.push(newpt); + } + return out; +} + +function angle(a, b) { + let x = b.x - a.x; + let y = b.y - a.y; + return Math.atan2(-y, x); +} + +function rotateVec(a, theta) { + let pt = { + x: 0, + y: 0 + }; + c = Math.cos(theta); + s = Math.sin(theta); + pt.x = c * a.x - s * a.y; + pt.y = s * a.x + c * a.y; + return pt; +} + +function distance(a, b) { + return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); +} + + +// golfview.js +let courselist = require("Storage").list(/^golf-\d+\.json$/); +let course = require("Storage").readJSON(courselist[0]).holes; // TODO use the first course for now + +let current_hole = 1; +let hole = course[current_hole.toString()]; +let user_position = { + fix: false, + lat: 0, + lon: 0, + x: 0, + y: 0, + to_hole: 0, + last_time: getTime(), + transform: {}, +}; + +function drawUser() { + if(!user_position.fix) return; + let new_pos = g.transformVertices([user_position.x,user_position.y],user_position.transform); + g.setColor(g.theme.fg); + g.drawCircle(new_pos[0],new_pos[1],8); +} + +function drawHole(l) { + + //console.log(l); + let hole_straight_distance = distance( + hole.nodesXY[0], + hole.nodesXY[hole.nodesXY.length - 1] + ); + + let scale = 0.9 * l.h / hole_straight_distance; + + let transform = { + x: l.x + l.w / 2, // center in the box + y: l.h * 0.95, // pad it just a bit TODO use the extent of the teeboxes/green + scale: scale, // scale factor (default 1) + rotate: hole.angle - Math.PI / 2.0, // angle in radians (default 0) + }; + + user_position.transform = transform; + + // draw the fairways first + hole.features.sort((a, b) => { + if (a.type === "fairway") { + return -1; + } + }); + + for (var feature of hole.features) { + //console.log(Object.keys(feature)); + if (feature.type === "fairway") { + g.setColor(1, 0, 1); // magenta + } else if (feature.type === "tee") { + g.setColor(1, 0, 0); // red + } else if (feature.type === "green") { + g.setColor(0, 1, 0); // green + } else if (feature.type === "bunker") { + g.setColor(1, 1, 0); // yellow + } else if (feature.type === "water_hazard") { + g.setColor(0, 0, 1); // blue + } else { + continue; + } + + var nodelist = []; + feature.nodesXY.forEach(function (node) { + nodelist.push(node.x); + nodelist.push(node.y); + }); + newnodelist = g.transformVertices(nodelist, transform); + + g.fillPoly(newnodelist, true); + //console.log(feature.type); + //console.log(newnodelist); + + drawUser(); + } + + var waynodelist = []; + hole.nodesXY.forEach(function (node) { + waynodelist.push(node.x); + waynodelist.push(node.y); + }); + + newnodelist = g.transformVertices(waynodelist, transform); + g.setColor(0, 1, 1); // cyan + g.drawPoly(newnodelist); +} + +function setHole(current_hole) { + layout.hole.label = "HOLE " + current_hole; + layout.par.label = "PAR " + course[current_hole.toString()].par; + layout.hcp.label = "HCP " + course[current_hole.toString()].handicap; + layout.postyardage.label = course[current_hole.toString()].tees[course[current_hole.toString()].tees.length - 1]; //TODO only use longest hole for now + + g.clear(); + layout.render(); +} + +function updateDistanceToHole() { + let xy = toXY({ "lat": user_position.lat, "lon": user_position.lon }, hole.way[0]); + user_position.x = xy.x; + user_position.y = xy.y; + user_position.last_time = getTime(); + let new_distance = Math.round(distance(xy, hole.nodesXY[hole.nodesXY.length - 1]) * 1.093613); //TODO meters later + //console.log(new_distance); + layout.measyardage.label = (new_distance < 999) ? new_distance : "---"; + + g.clear(); + layout.render(); +} + +Bangle.on('swipe', function (direction) { + if (direction > 0) { + current_hole--; + } else { + current_hole++; + } + + if (current_hole > 18) { current_hole = 1; } + if (current_hole < 1) { current_hole = 18; } + hole = course[current_hole.toString()]; + + setHole(current_hole); +}); + +Bangle.on('GPS', (fix) => { + if (isNaN(fix.lat)) return; + //console.log(fix.hdop * 5); //precision + user_position.fix = true; + user_position.lat = fix.lat; + user_position.lon = fix.lon; + updateDistanceToHole(); + drawUser(); +}); + +// The layout, referencing the custom renderer +var Layout = require("Layout"); +var layout = new Layout({ + type: "h", c: [ + { + type: "v", c: [ + { type: "txt", font: "10%", id: "hole", label: "HOLE 18" }, + { type: "txt", font: "10%", id: "par", label: "PAR 4" }, + { type: "txt", font: "10%", id: "hcp", label: "HCP 18" }, + { type: "txt", font: "35%", id: "postyardage", label: "---" }, + { type: "txt", font: "20%", id: "measyardage", label: "---" }, + ] + }, + { type: "custom", render: drawHole, id: "graph", bgCol: g.theme.bg, fillx: 1, filly: 1 } + ], + lazy: true +}); + +Bangle.setGPSPower(1); +setHole(current_hole); +//layout.debug(); diff --git a/apps/golfview/golfview.png b/apps/golfview/golfview.png new file mode 100644 index 000000000..dfadeb5fa Binary files /dev/null and b/apps/golfview/golfview.png differ diff --git a/apps/golfview/maptools.js b/apps/golfview/maptools.js new file mode 100644 index 000000000..e475c798b --- /dev/null +++ b/apps/golfview/maptools.js @@ -0,0 +1,63 @@ +const EARTHRADIUS = 6371000; //km + +function radians(a) { + return a * Math.PI / 180; +} + +function degrees(a) { + let d = a * 180 / Math.PI; + return (d + 360) % 360; +} + +function toXY(a, origin) { + let pt = { + x: 0, + y: 0 + }; + + pt.x = EARTHRADIUS * radians(a.lon - origin.lon) * Math.cos(radians((a.lat + origin.lat) / 2)); + pt.y = EARTHRADIUS * radians(origin.lat - a.lat); + return pt; +} + +function arraytoXY(array, origin) { + let out = []; + for (var j in array) { + let newpt = toXY(array[j], origin); + out.push(newpt); + } + return out; +} + +function angle(a, b) { + let x = b.x - a.x; + let y = b.y - a.y; + return Math.atan2(-y, x); +} + +function rotateVec(a, theta) { + let pt = { + x: 0, + y: 0 + }; + c = Math.cos(theta); + s = Math.sin(theta); + pt.x = c * a.x - s * a.y; + pt.y = s * a.x + c * a.y; + return pt; +} + +function distance(a,b) { + return Math.sqrt(Math.pow(a.x-b.x,2) + Math.pow(a.y-b.y,2)) +} + +// https://stackoverflow.com/questions/19721439/download-json-object-as-a-file-from-browser +function downloadObjectAsJSON(exportObj, exportName) { + var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(exportObj); // must be stringified!! + var downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", exportName + ".json"); + document.body.appendChild(downloadAnchorNode); // required for firefox + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +} \ No newline at end of file diff --git a/apps/golfview/metadata.json b/apps/golfview/metadata.json new file mode 100644 index 000000000..a36a9b6ab --- /dev/null +++ b/apps/golfview/metadata.json @@ -0,0 +1,15 @@ +{ "id": "golfview", + "name": "Golf View", + "version":"0.02", + "description": "This app will provide you with on course data to support your golf game!", + "icon": "golfview.png", + "tags": "outdoors, gps", + "allow_emulator": true, + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "custom": "custom.html", + "storage": [ + {"name":"golfview.app.js","url":"golfview.js"}, + {"name":"golfview.img","url":"golfview-icon.js","evaluate":true} + ] +} diff --git a/apps/golfview/screenshot.png b/apps/golfview/screenshot.png new file mode 100644 index 000000000..983b19ece Binary files /dev/null and b/apps/golfview/screenshot.png differ diff --git a/apps/gpsautotime/ChangeLog b/apps/gpsautotime/ChangeLog index 2827c9e5c..97b80ecdf 100644 --- a/apps/gpsautotime/ChangeLog +++ b/apps/gpsautotime/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Set Bangle.js 2 compatible +0.03: Add setting to hide the widget diff --git a/apps/gpsautotime/metadata.json b/apps/gpsautotime/metadata.json index 766961276..217a27931 100644 --- a/apps/gpsautotime/metadata.json +++ b/apps/gpsautotime/metadata.json @@ -2,13 +2,15 @@ "id": "gpsautotime", "name": "GPS auto time", "shortName": "GPS auto time", - "version": "0.02", + "version": "0.03", "description": "A widget that automatically updates the Bangle.js time to the GPS time whenever there is a valid GPS fix.", "icon": "widget.png", "type": "widget", "tags": "widget,gps", "supports": ["BANGLEJS","BANGLEJS2"], "storage": [ - {"name":"gpsautotime.wid.js","url":"widget.js"} - ] + {"name":"gpsautotime.wid.js","url":"widget.js"}, + {"name":"gpsautotime.settings.js","url":"settings.js"} + ], + "data": [{"name":"gpsautotime.json"}] } diff --git a/apps/gpsautotime/settings.js b/apps/gpsautotime/settings.js new file mode 100644 index 000000000..dbdd121d1 --- /dev/null +++ b/apps/gpsautotime/settings.js @@ -0,0 +1,25 @@ +(function(back) { + var FILE = "gpsautotime.json"; + // Load settings + var settings = Object.assign({ + show: true, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "GPS auto time" }, + "< Back" : () => back(), + 'Show widget?': { + value: !!settings.show, // !! converts undefined to false + format: v => v?"Show":"Hide", + onchange: v => { + settings.show = v; + writeSettings(); + } + }, + }); +}) diff --git a/apps/gpsautotime/widget.js b/apps/gpsautotime/widget.js index a1d1b2b08..a21c14619 100644 --- a/apps/gpsautotime/widget.js +++ b/apps/gpsautotime/widget.js @@ -1,6 +1,13 @@ (() => { var lastTimeSet = 0; + var settings = Object.assign({ + // default values + show: true, + }, require('Storage').readJSON("gpsautotime.json", true) || {}); + const show = settings.show; + delete settings; + Bangle.on('GPS',function(fix) { if (fix.fix) { var curTime = fix.time.getTime()/1000; @@ -14,8 +21,9 @@ // add your widget WIDGETS["gpsAutoTime"]={ area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right) - width: 28, // width of the widget + width: show ? 4*6+2 : 0, // width of the widget draw: function() { + if (!show) return; g.reset(); // reset the graphics context to defaults (color/font/etc) g.setFont("6x8"); if ((getTime() - lastTimeSet) <= 60) { @@ -27,7 +35,9 @@ } }; - setInterval(function() { - WIDGETS["gpsAutoTime"].draw(WIDGETS["gpsAutoTime"]); - }, 1*60000); // update every minute -})() + if (show) { + setInterval(function() { + WIDGETS["gpsAutoTime"].draw(WIDGETS["gpsAutoTime"]); + }, 1*60000); // update every minute + } +})(); diff --git a/apps/gpsinfo/gps-info.js b/apps/gpsinfo/gps-info.js index 0eca2ccf5..a6e21af0d 100644 --- a/apps/gpsinfo/gps-info.js +++ b/apps/gpsinfo/gps-info.js @@ -5,7 +5,7 @@ function satelliteImage() { var Layout = require("Layout"); var layout; //Bangle.setGPSPower(1, "app"); -E.showMessage("Loading..."); // avoid showing rubbish on screen +E.showMessage(/*LANG*/"Loading..."); // avoid showing rubbish on screen var lastFix = { fix: -1, diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog index 365405846..f923739f0 100644 --- a/apps/gpsrec/ChangeLog +++ b/apps/gpsrec/ChangeLog @@ -28,4 +28,5 @@ 0.24: Better support for Bangle.js 2, avoid widget area for Graphs, smooth graphs more 0.25: Fix issue where if Bangle.js 2 got a GPS fix but no reported time, errors could be caused by the widget (fix #935) 0.26: Multiple bugfixes -0.27: Map drawing with light theme (fix #1023) +0.27: Map drawing with light theme (fix #1023) +0.28: Show distance more accurately in conjunction with new locale app (fix #1523) diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js index 833a816ea..4595f616d 100644 --- a/apps/gpsrec/app.js +++ b/apps/gpsrec/app.js @@ -126,7 +126,7 @@ function asTime(v){ function viewTrack(n, info) { if (!info) { - E.showMessage("Loading...","GPS Track "+n); + E.showMessage(/*LANG*/"Loading...","GPS Track "+n); info = getTrackInfo(n); } const menu = { @@ -248,7 +248,7 @@ function plotTrack(info) { g.fillCircle(ox,oy,5); if (info.qOSTM) g.setColor(0, 0, 0); else g.setColor(g.theme.fg); - g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20); + g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20); g.setFont("6x8",2); g.setFontAlign(0,0,3); g.drawString("Back",g.getWidth() - 10, g.getHeight()/2); diff --git a/apps/gpsrec/metadata.json b/apps/gpsrec/metadata.json index 088b8c741..c870157df 100644 --- a/apps/gpsrec/metadata.json +++ b/apps/gpsrec/metadata.json @@ -1,8 +1,8 @@ { "id": "gpsrec", "name": "GPS Recorder", - "version": "0.27", - "description": "Application that allows you to record a GPS track. Can run in background", + "version": "0.28", + "description": "(NOT RECOMMENDED) - please use the more flexible 'Recorder' app instead. Application that allows you to record a GPS track. Can run in background", "icon": "app.png", "tags": "tool,outdoors,gps,widget", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/gpstime/gpstime.js b/apps/gpstime/gpstime.js index 8c80953fa..6a01821ba 100644 --- a/apps/gpstime/gpstime.js +++ b/apps/gpstime/gpstime.js @@ -10,7 +10,7 @@ var Layout = require("Layout"); Bangle.setGPSPower(1, "app"); Bangle.loadWidgets(); Bangle.drawWidgets(); -E.showMessage("Loading..."); // avoid showing rubbish on screen +E.showMessage(/*LANG*/"Loading..."); // avoid showing rubbish on screen function setGPSTime() { if (fix.time!==undefined) { diff --git a/apps/gsat/ChangeLog b/apps/gsat/ChangeLog new file mode 100644 index 000000000..48156d0d4 --- /dev/null +++ b/apps/gsat/ChangeLog @@ -0,0 +1 @@ +0.01: Added Source Code diff --git a/apps/gsat/README.md b/apps/gsat/README.md new file mode 100644 index 000000000..faf986947 --- /dev/null +++ b/apps/gsat/README.md @@ -0,0 +1,3 @@ +# Geek Squad Appointment Timer + +An app dedicated to setting a 20 minute timer for Geek Squad Appointments. diff --git a/apps/gsat/app-icon.js b/apps/gsat/app-icon.js new file mode 100644 index 000000000..06f93e2ef --- /dev/null +++ b/apps/gsat/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV")) diff --git a/apps/gsat/app.js b/apps/gsat/app.js new file mode 100644 index 000000000..3a7d443fe --- /dev/null +++ b/apps/gsat/app.js @@ -0,0 +1,38 @@ +// Clear screen +g.clear(); + +const secsinmin = 60; +const quickfixperiod = 900; +var seconds = 1200; + +function countSecs() { + if (seconds != 0) {seconds -=1;} + console.log(seconds); +} +function drawTime() { + g.clear(); + g.setFontAlign(0,0); + g.setFont('Vector', 12); + g.drawString('Geek Squad Appointment Timer', 125, 20); + if (seconds == 0) { + g.setFont('Vector', 35); + g.drawString('Appointment', 125, 100); + g.drawString('finished!', 125, 150); + Bangle.buzz(); + return; + } + min = seconds / secsinmin; + if (seconds < quickfixperiod) { + g.setFont('Vector', 20); + g.drawString('Quick Fix', 125, 50); + g.drawString('Period Passed!', 125, 75); + } + g.setFont('Vector', 50); + g.drawString(Math.ceil(min), 125, 125); + g.setFont('Vector', 25); + g.drawString('minutes', 125, 165); + g.drawString('remaining', 125, 195); +} +drawTime(); +setInterval(countSecs, 1000); +setInterval(drawTime, 60000); diff --git a/apps/gsat/app.png b/apps/gsat/app.png new file mode 100644 index 000000000..cf057046b Binary files /dev/null and b/apps/gsat/app.png differ diff --git a/apps/gsat/metadata.json b/apps/gsat/metadata.json new file mode 100644 index 000000000..878d213e4 --- /dev/null +++ b/apps/gsat/metadata.json @@ -0,0 +1,16 @@ +{ + "id": "gsat", + "name": "Geek Squad Appointment Timer", + "shortName": "gsat", + "version": "0.01", + "description": "Starts a 20 minute timer for appointments at Geek Squad.", + "icon": "app.png", + "tags": "tool", + "readme": "README.md", + "supports": ["BANGLEJS"], + "screenshots": [{"url":"screenshot.png"}], + "storage": [ + {"name":"gsat.app.js","url":"app.js"}, + {"name":"gsat.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/gsat/screenshot.png b/apps/gsat/screenshot.png new file mode 100644 index 000000000..032319bf6 Binary files /dev/null and b/apps/gsat/screenshot.png differ diff --git a/apps/hcclock/metadata.json b/apps/hcclock/metadata.json index e372a0a2c..0d4cbe0cd 100644 --- a/apps/hcclock/metadata.json +++ b/apps/hcclock/metadata.json @@ -8,6 +8,7 @@ "tags": "clock", "screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}], "supports": ["BANGLEJS"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"hcclock.app.js","url":"hcclock.app.js"}, diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 1e4864af8..74be2faec 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -10,3 +10,6 @@ 0.09: Fix file naming so months are 1-based (not 0) (fix #1119) 0.10: Adds additional 3 minute setting for HRM 0.11: Pre-minified boot&lib - folds constants and saves RAM +0.12: Add setting for Daily Step Goal +0.13: Add support for internationalization +0.14: Move settings diff --git a/apps/health/README.md b/apps/health/README.md index f44854e3e..c6b379c0a 100644 --- a/apps/health/README.md +++ b/apps/health/README.md @@ -24,6 +24,7 @@ Stores: * **Off** - Don't turn HRM on, but record heart rate if the HRM was turned on by another app/widget * **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found * **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours) +* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use ## Technical Info diff --git a/apps/health/app.js b/apps/health/app.js index 7a55eec27..a1fe1a4f7 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -1,37 +1,13 @@ -function getSettings() { - return require("Storage").readJSON("health.json",1)||{}; -} - -function setSettings(s) { - require("Storage").writeJSON("health.json",s); -} - function menuMain() { swipe_enabled = false; clearButton(); E.showMenu({ - "":{title:"Health Tracking"}, - "< Back":()=>load(), - "Step Counting":()=>menuStepCount(), - "Movement":()=>menuMovement(), - "Heart Rate":()=>menuHRM(), - "Settings":()=>menuSettings() - }); -} - -function menuSettings() { - swipe_enabled = false; - clearButton(); - var s=getSettings(); - E.showMenu({ - "":{title:"Health Tracking"}, - "< Back":()=>menuMain(), - "Heart Rt":{ - value : 0|s.hrm, - min : 0, max : 3, - format : v=>["Off","3 mins","10 mins","Always"][v], - onchange : v => { s.hrm=v;setSettings(s); } - } + "": { title: /*LANG*/"Health Tracking" }, + /*LANG*/"< Back": () => load(), + /*LANG*/"Step Counting": () => menuStepCount(), + /*LANG*/"Movement": () => menuMovement(), + /*LANG*/"Heart Rate": () => menuHRM(), + /*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>menuMain()) }); } @@ -39,10 +15,10 @@ function menuStepCount() { swipe_enabled = false; clearButton(); E.showMenu({ - "":{title:"Step Counting"}, - "< Back":()=>menuMain(), - "per hour":()=>stepsPerHour(), - "per day":()=>stepsPerDay() + "": { title:/*LANG*/"Steps" }, + /*LANG*/"< Back": () => menuMain(), + /*LANG*/"per hour": () => stepsPerHour(), + /*LANG*/"per day": () => stepsPerDay() }); } @@ -50,10 +26,10 @@ function menuMovement() { swipe_enabled = false; clearButton(); E.showMenu({ - "":{title:"Movement"}, - "< Back":()=>menuMain(), - "per hour":()=>movementPerHour(), - "per day":()=>movementPerDay(), + "": { title:/*LANG*/"Movement" }, + /*LANG*/"< Back": () => menuMain(), + /*LANG*/"per hour": () => movementPerHour(), + /*LANG*/"per day": () => movementPerDay(), }); } @@ -61,17 +37,16 @@ function menuHRM() { swipe_enabled = false; clearButton(); E.showMenu({ - "":{title:"Heart Rate"}, - "< Back":()=>menuMain(), - "per hour":()=>hrmPerHour(), - "per day":()=>hrmPerDay(), + "": { title:/*LANG*/"Heart Rate" }, + /*LANG*/"< Back": () => menuMain(), + /*LANG*/"per hour": () => hrmPerHour(), + /*LANG*/"per day": () => hrmPerDay(), }); } - function stepsPerHour() { - E.showMessage("Loading..."); - var data = new Uint16Array(24); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); g.clear(1); Bangle.drawWidgets(); @@ -81,8 +56,8 @@ function stepsPerHour() { } function stepsPerDay() { - E.showMessage("Loading..."); - var data = new Uint16Array(31); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); g.clear(1); Bangle.drawWidgets(); @@ -92,9 +67,9 @@ function stepsPerDay() { } function hrmPerHour() { - E.showMessage("Loading..."); - var data = new Uint16Array(24); - var cnt = new Uint8Array(23); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(24); + let cnt = new Uint8Array(23); require("health").readDay(new Date(), h=>{ data[h.hr]+=h.bpm; if (h.bpm) cnt[h.hr]++; @@ -108,9 +83,9 @@ function hrmPerHour() { } function hrmPerDay() { - E.showMessage("Loading..."); - var data = new Uint16Array(31); - var cnt = new Uint8Array(31); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(31); + let cnt = new Uint8Array(31); require("health").readDailySummaries(new Date(), h=>{ data[h.day]+=h.bpm; if (h.bpm) cnt[h.day]++; @@ -124,8 +99,8 @@ function hrmPerDay() { } function movementPerHour() { - E.showMessage("Loading..."); - var data = new Uint16Array(24); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); g.clear(1); Bangle.drawWidgets(); @@ -135,8 +110,8 @@ function movementPerHour() { } function movementPerDay() { - E.showMessage("Loading..."); - var data = new Uint16Array(31); + E.showMessage(/*LANG*/"Loading..."); + let data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement); g.clear(1); Bangle.drawWidgets(); @@ -158,7 +133,7 @@ var chart_data; var swipe_enabled = false; var btn; -// find the max value in the array, using a loop due to array size +// find the max value in the array, using a loop due to array size function max(arr) { var m = -Infinity; @@ -170,10 +145,10 @@ function max(arr) { // find the end of the data, the array might be for 31 days but only have 2 days of data in it function get_data_length(arr) { var nlen = arr.length; - + for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--) nlen--; - + return nlen; } @@ -192,14 +167,14 @@ function drawBarChart() { const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre var bar_top; var bar; - + g.setColor(g.theme.bg); g.fillRect(0,24,w,h); - + for (bar = 1; bar < 10; bar++) { if (bar == 5) { g.setFont('6x8', 2); - g.setFontAlign(0,-1) + g.setFontAlign(0,-1); g.setColor(g.theme.fg); g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150); g.setColor("#00f"); @@ -208,7 +183,7 @@ function drawBarChart() { } // draw a fake 0 height bar if chart_index is outside the bounds of the array - if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len) + if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len) bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum; else bar_top = bar_bot; @@ -238,7 +213,7 @@ Bangle.on('swipe', dir => { function setButton(fn) { // cancel callback, otherwise a slight up down movement will show the E.showMenu() Bangle.setUI("updown", undefined); - + if (process.env.HWVERSION == 1) btn = setWatch(fn, BTN2); else @@ -254,4 +229,5 @@ function clearButton() { Bangle.loadWidgets(); Bangle.drawWidgets(); + menuMain(); diff --git a/apps/health/metadata.json b/apps/health/metadata.json index 8bb986c57..58762c77a 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -1,7 +1,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.11", + "version": "0.14", "description": "Logs health data and provides an app to view it", "icon": "app.png", "tags": "tool,system,health", @@ -12,6 +12,8 @@ {"name":"health.app.js","url":"app.js"}, {"name":"health.img","url":"app-icon.js","evaluate":true}, {"name":"health.boot.js","url":"boot.min.js"}, - {"name":"health","url":"lib.min.js"} - ] + {"name":"health","url":"lib.min.js"}, + {"name":"health.settings.js","url":"settings.js"} + ], + "data": [{"name":"health.json"}] } diff --git a/apps/health/settings.js b/apps/health/settings.js new file mode 100644 index 000000000..f94efbb16 --- /dev/null +++ b/apps/health/settings.js @@ -0,0 +1,43 @@ +(function (back) { + var settings = Object.assign({ + hrm: 0, + stepGoal: 10000 + }, require("Storage").readJSON("health.json", true) || {}); + + E.showMenu({ + "": { title: /*LANG*/"Health Tracking" }, + + /*LANG*/"< Back": () => back(), + + /*LANG*/"HRM Interval": { + value: settings.hrm, + min: 0, + max: 3, + format: v => [ + /*LANG*/"Off", + /*LANG*/"3 min", + /*LANG*/"10 min", + /*LANG*/"Always" + ][v], + onchange: v => { + settings.hrm = v; + setSettings(settings); + } + }, + + /*LANG*/"Daily Step Goal": { + value: settings.stepGoal, + min: 0, + max: 20000, + step: 250, + onchange: v => { + settings.stepGoal = v; + setSettings(settings); + } + } + }); + + function setSettings(settings) { + require("Storage").writeJSON("health.json", settings); + } +}) diff --git a/apps/heartzone/ChangeLog b/apps/heartzone/ChangeLog new file mode 100644 index 000000000..2a37193a3 --- /dev/null +++ b/apps/heartzone/ChangeLog @@ -0,0 +1 @@ +0.01: Initial release. diff --git a/apps/heartzone/README.md b/apps/heartzone/README.md new file mode 100644 index 000000000..4de60355d --- /dev/null +++ b/apps/heartzone/README.md @@ -0,0 +1,35 @@ +# HeartZone + +HeartZone continuously monitors your heart rate. If your heart rate is outside of your configured limits, you get a configurable buzz. + +Inspired by [Workout HRM](https://github.com/espruino/BangleApps/tree/master/apps/wohrm), but I wanted the following features: + +* Larger text, more contrast, and color-coding for better readability while exercising. +* Configurable buzz interval, instead of at every heart rate reading (which was too distracting). +* Pause for a rest and resume afterwards without having to restart the heart rate sensor (which takes several seconds each time to stabilize). +* Configure the minimum heart rate confidence threshold (bad readings cause buzzes that have to be ignored). + +However, compared to Workout HRM, HeartZone doesn't support: + +* In-app configuration of the heart rate thresholds - you can only do it in the Settings app. +* Bangle.js 1 - this only supports Bangle.js 2. + +## Usage + +When you first start the app, it will begin displaying your heart rate after a few seconds. Until the heart rate confidence is above your configured minimum confidence, the background will be colored red: + +![Start screen](screenshots/start.png) + +After the heart rate confidence is at an acceptable level, the background will be colored white, and you will receive buzzes on your wrist while your heart rate is out of the configured range. By default, the BPM-too-low buzz is 200ms, while the BPM-too-high buzz is 1000ms: + +![Screen while we are monitoring](screenshots/running.png) + +If you're taking a break, swipe down to turn off the buzzes while continuing to measure and display your heart rate (swipe up again to end your break): + +![Screen while we are paused](screenshots/paused.png) + +When you're done, simply press the side button to exit the app. + +## Creator + +[Uberi](https://github.com/Uberi) diff --git a/apps/heartzone/app-icon.js b/apps/heartzone/app-icon.js new file mode 100644 index 000000000..2e3692455 --- /dev/null +++ b/apps/heartzone/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBiIA/AHqFCAxQWJ5gABCIQGGABEQB4QABgIGGC5MMCAnAAwwuOABAwIC64/FABBIIC68ADBnAVJEP+AXLBoJ2H/4XN/54GBAIXOGAouBBAMAABQXBGAoHCAB4wDFwQARGAYvWL7CPDbBXAR46/DiAXJgK/Id4URGBHABobwHEAIwIBQQuHAAcYGA3AwIUKC4eAC4sIC5+IGAnAxAXQkAXDgQXRkQwC4EiC6QwCgQXTl0M4HiC6nghwXV93uC9MRC44WOGAIXFFx4ABC4oWQiMSC4chC6MRC4YWSiMeC4PhC6cRC4IWUGAIuVAH4AVA=")) diff --git a/apps/heartzone/app.js b/apps/heartzone/app.js new file mode 100644 index 000000000..9ec3f005d --- /dev/null +++ b/apps/heartzone/app.js @@ -0,0 +1,87 @@ +// clear screen and draw widgets +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +var statusRect = {x: Bangle.appRect.x, y: Bangle.appRect.y, w: Bangle.appRect.w, h: 32}; +var settingsRect = {x: Bangle.appRect.x, y: Bangle.appRect.y2 - 16, w: Bangle.appRect.w, h: 16}; +var hrmRect = {x: Bangle.appRect.x, y: statusRect.y + statusRect.h, w: Bangle.appRect.w, h: Bangle.appRect.h - statusRect.h - settingsRect.h}; + +var isPaused = false; +var settings = Object.assign({ + minBpm: 120, + maxBpm: 160, + minConfidence: 60, + minBuzzIntervalSeconds: 5, + tooLowBuzzDurationMillis: 200, + tooHighBuzzDurationMillis: 1000, +}, require('Storage').readJSON("heartzone.settings.json", true) || {}); + +// draw current settings at the bottom +g.setFont6x15(1).setFontAlign(0, -1, 0); +g.drawString(settings.minBpm + "=" + settings.minConfidence + "% conf.", settingsRect.x + (settingsRect.w / 2), settingsRect.y + 4); + +function drawStatus(status) { // draw status bar at the top + g.setBgColor(g.theme.bg).setColor(g.theme.fg); + g.clearRect(statusRect); + + g.setFontVector(statusRect.h - 4).setFontAlign(0, -1, 0); + g.drawString(status, statusRect.x + (statusRect.w / 2), statusRect.y + 2); +} + +function drawHRM(hrmInfo) { // draw HRM info display + g.setBgColor(hrmInfo.confidence > settings.minConfidence ? '#fff' : '#f00').setColor(hrmInfo.confidence > settings.minConfidence ? '#000' : '#fff'); + g.setFontAlign(-1, -1, 0); + g.clearRect(hrmRect); + + var px = hrmRect.x + 10, py = hrmRect.y + 10; + g.setFontVector((hrmRect.h / 2) - 20); + g.drawString(hrmInfo.bpm, px, py); + g.setFontVector(16); + g.drawString('BPM', px + g.stringWidth(hrmInfo.bpm.toString()) + 32, py); + py += hrmRect.h / 2; + + g.setFontVector((hrmRect.h / 2) - 20); + g.drawString(hrmInfo.confidence, px, py); + g.setFontVector(16); + g.drawString('% conf.', px + g.stringWidth(hrmInfo.confidence.toString()) + 32, py); +} + +drawHRM({bpm: '?', confidence: '?'}); +drawStatus('RUNNING'); + +var lastBuzz = getTime(); +Bangle.on('HRM', function(hrmInfo) { + if (!isPaused) { + var currentTime; + if (hrmInfo.confidence > settings.minConfidence) { + if (hrmInfo.bpm < settings.minBpm) { + currentTime = getTime(); + if (currentTime - lastBuzz > settings.minBuzzIntervalSeconds) { + lastBuzz = currentTime; + Bangle.buzz(settings.tooLowBuzzDurationMillis); + } + } else if (hrmInfo.bpm > settings.maxBpm) { + currentTime = getTime(); + if (currentTime - lastBuzz > minBuzzIntervalSeconds) { + lastBuzz = currentTime; + Bangle.buzz(settings.tooHighBuzzDurationMillis); + } + } + } + } + drawHRM(hrmInfo); +}); + +Bangle.setUI('updown', function(action) { + if (action == -1) { // up + isPaused = false; + drawStatus("RUNNING"); + } else if (action == 1) { // down + isPaused = true; + drawStatus("PAUSED"); + } +}); +setWatch(function() { Bangle.setHRMPower(false, "heartzone"); load(); }, BTN1); + +Bangle.setHRMPower(true, "heartzone"); diff --git a/apps/heartzone/icon.png b/apps/heartzone/icon.png new file mode 100644 index 000000000..60ece4b32 Binary files /dev/null and b/apps/heartzone/icon.png differ diff --git a/apps/heartzone/metadata.json b/apps/heartzone/metadata.json new file mode 100644 index 000000000..7c385ce0d --- /dev/null +++ b/apps/heartzone/metadata.json @@ -0,0 +1,21 @@ +{ + "id": "heartzone", + "name": "HeartZone", + "version": "0.01", + "description": "Exercise app for keeping your heart rate in the aerobic zone. Buzzes the watch at configurable intervals when your heart rate is outside of configured limits.", + "readme":"README.md", + "screenshots": [ + {"url": "screenshots/start.png"}, + {"url": "screenshots/running.png"}, + {"url": "screenshots/paused.png"} + ], + "icon": "icon.png", + "tags": "health", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"heartzone.app.js","url":"app.js"}, + {"name":"heartzone.settings.js","url":"settings.js"}, + {"name":"heartzone.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name":"heartzone.settings.json"}] +} diff --git a/apps/heartzone/screenshots/paused.png b/apps/heartzone/screenshots/paused.png new file mode 100644 index 000000000..5f89ed076 Binary files /dev/null and b/apps/heartzone/screenshots/paused.png differ diff --git a/apps/heartzone/screenshots/running.png b/apps/heartzone/screenshots/running.png new file mode 100644 index 000000000..a514edff5 Binary files /dev/null and b/apps/heartzone/screenshots/running.png differ diff --git a/apps/heartzone/screenshots/start.png b/apps/heartzone/screenshots/start.png new file mode 100644 index 000000000..195957fa0 Binary files /dev/null and b/apps/heartzone/screenshots/start.png differ diff --git a/apps/heartzone/settings.js b/apps/heartzone/settings.js new file mode 100644 index 000000000..64030b0a3 --- /dev/null +++ b/apps/heartzone/settings.js @@ -0,0 +1,27 @@ +(function(back) { + var FILE = "heartzone.settings.json"; + var settings = Object.assign({ + minBpm: 120, + maxBpm: 160, + minConfidence: 60, + minBuzzIntervalSeconds: 5, + tooLowBuzzDurationMillis: 200, + tooHighBuzzDurationMillis: 1000, + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "HeartZone" }, + "< Save & Return" : () => { writeSettings(); back(); }, + 'Min BPM': {value: 0 | settings.minBpm, min: 80, max: 200, step: 10, onchange: v => { settings.minBpm = v; }}, + 'Max BPM': {value: 0 | settings.maxBpm, min: 80, max: 200, step: 10, onchange: v => { settings.maxBpm = v; }}, + 'Min % conf.': {value: 0 | settings.minConfidence, min: 30, max: 100, step: 5, onchange: v => { settings.minConfidence = v; }}, + 'Min buzz int. (sec)': {value: 0 | settings.minBuzzIntervalSeconds, min: 1, max: 30, onchange: v => { settings.minBuzzIntervalSeconds = v; }}, + 'BPM too low buzz (ms)': {value: 0 | settings.tooLowBuzzDurationMillis, min: 0, max: 3000, step: 100, onchange: v => { settings.tooLowBuzzDurationMillis = v; }}, + 'BPM too high buzz (ms)': {value: 0 | settings.tooHighBuzzDurationMillis, min: 0, max: 3000, step: 100, onchange: v => { settings.tooHighBuzzDurationMillis = v; }}, + }); +}) diff --git a/apps/hebrew_calendar/ChangeLog b/apps/hebrew_calendar/ChangeLog index fdd29db66..1fb04080d 100644 --- a/apps/hebrew_calendar/ChangeLog +++ b/apps/hebrew_calendar/ChangeLog @@ -1,4 +1,6 @@ 0.01: New App! 0.02: using TS and rollup to bundle 0.03: bug fixes and support bangle 1 -0.04: removing TS \ No newline at end of file +0.04: removing TS +0.05: major overhaul; now you customize your calendar based on your location for candle lighting times +0.06: bug fixes and improvements \ No newline at end of file diff --git a/apps/hebrew_calendar/HebrewCalendar-Screenshot.png b/apps/hebrew_calendar/HebrewCalendar-Screenshot.png new file mode 100644 index 000000000..f60314c99 Binary files /dev/null and b/apps/hebrew_calendar/HebrewCalendar-Screenshot.png differ diff --git a/apps/hebrew_calendar/README.md b/apps/hebrew_calendar/README.md index 7a96a97db..d4e6b837a 100644 --- a/apps/hebrew_calendar/README.md +++ b/apps/hebrew_calendar/README.md @@ -1,19 +1,22 @@ # Hebrew Calendar -Displays the current hebrew calendar date -Add screen shots (if possible) to the app folder and link then into this file with ![](.png) +Displays the current hebrew calendar date and upcoming holidays alongside a clock + +![](./HebrewCalendar-Screenshot.png) ## Usage -Open the app, and it shows a menu with the date components +Set it up as your clock in the settings ## Features -Shows the hebrew date, month, and year; alongside the gregorian date +- Shows the hebrew date, month, and year; alongside the gregorian date +- Shows when upcoming holidays start +- Shows the gregorian day of week, date, and current time ## Controls -Name the buttons and what they are used for +N/A ## Requests @@ -22,5 +25,5 @@ Michael Salaverry (github.com/barakplasma) ## Creator Michael Salaverry -with help from https://github.com/IonicaBizau/hebrew-date (MIT license) +with help from https://github.com/hebcal/hebcal-es6 (MIT license) which is used to calculate the calendar \ No newline at end of file diff --git a/apps/hebrew_calendar/app.js b/apps/hebrew_calendar/app.js index 399d124f3..3d098c60a 100644 --- a/apps/hebrew_calendar/app.js +++ b/apps/hebrew_calendar/app.js @@ -1,26 +1,181 @@ -g.clear(); +const dayInMS = 86400000; -let now = new Date(); +const DateProvider = { now: () => Date.now() }; -let today = require('hebrewDate').hebrewDate(now); +const Layout = require("Layout"); +const Locale = require("locale"); -var mainmenu = { - "": { - "title": "Hebrew Date" - }, - greg: { - // @ts-ignore - value: require('locale').date(now, 1), - }, - date: { - value: today.date, - }, - month: { - value: today.month_name, - }, - year: { - value: today.year, +let nextEndingEvent; + +function getCurrentEvents() { + const now = DateProvider.now(); + + const current = hebrewCalendar.filter( + (x) => x.startEvent <= now && x.endEvent >= now + ); + + nextEndingEvent = current.reduce((acc, ev) => { + return Math.min(acc, ev.endEvent); + }, Infinity); + + return current.map((event, i) => { + return { + type: "txt", + font: "12x20", + id: "currentEvents" + i, + label: event.desc, + pad: 2, + bgCol: g.theme.bg, + }; + }); +} + +function getUpcomingEvents() { + const now = DateProvider.now(); + + const futureEvents = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS + ); + + let warning; + let eventsLeft = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS * 14 + ).length; + + if (eventsLeft < 14) { + warning = { + startEvent: 0, + type: "txt", + font: "4x6", + id: "warning", + label: "only " + eventsLeft + " events left in calendar; update soon", + pad: 2, + bgCol: g.theme.bg, + }; } -}; -// @ts-ignore -E.showMenu(mainmenu); \ No newline at end of file + + return futureEvents + .slice(0, 2) + .map((event, i) => { + return { + startEvent: event.startEvent, + type: "txt", + font: "6x8", + id: "upcomingEvents" + 1, + label: event.desc + " at " + Locale.time(new Date(event.startEvent), 1), + pad: 2, + bgCol: g.theme.bg, + }; + }) + .concat(warning) + .sort(function (a, b) { + return a.startEvent - b.startEvent; + }); +} + +function dateTime() { + return ( + Locale.dow(new Date(), 1) + + " " + + Locale.date(new Date(), 1) + + " " + + Locale.time(new Date(), 1) + ); +} + +function makeLayout() { + return new Layout( + { + type: "v", + c: [ + { + type: "txt", + font: "6x8", + id: "title", + label: "-- Hebrew Calendar Events --", + pad: 2, + bgCol: g.theme.bg2, + }, + { + type: "txt", + font: "6x8", + id: "currently", + label: "Currently", + pad: 2, + bgCol: g.theme.bgH, + }, + ] + .concat(getCurrentEvents()) + .concat([ + { + type: "txt", + font: "6x8", + label: "Upcoming", + id: "upcoming", + pad: 2, + bgCol: g.theme.bgH, + }, + ]) + .concat(getUpcomingEvents()) + .concat([ + { + type: "txt", + font: "Vector14", + id: "time", + label: dateTime(), + pad: 2, + bgCol: undefined, + }, + ]), + }, + { lazy: true } + ); +} +let layout = makeLayout(); +// see also https://www.espruino.com/Bangle.js+Layout#updating-the-screen + +// timeout used to update every minute +let drawTimeout; + +function draw() { + layout.time.label = dateTime(); + layout.render(); + + // schedule a draw for the next minute + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function () { + drawTimeout = undefined; + draw(); + }, 60000 - (DateProvider.now() % 60000)); + console.log("updated time"); +} + +// update time and draw +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); + +function findNextEvent() { + return hebrewCalendar.find((ev) => { + return ev.startEvent > DateProvider.now(); + }); +} + +function updateCalendar() { + layout.clear(); + layout = makeLayout(); + layout.forgetLazyState(); + layout.render(); + + let nextChange = Math.min( + findNextEvent().startEvent - DateProvider.now() + 5000, + nextEndingEvent - DateProvider.now() + 5000 + ); + setTimeout(updateCalendar, nextChange); + console.log("updated events"); +} + +updateCalendar(); + +Bangle.setUI("clock"); \ No newline at end of file diff --git a/apps/hebrew_calendar/customizer.html b/apps/hebrew_calendar/customizer.html new file mode 100644 index 000000000..bea860e53 --- /dev/null +++ b/apps/hebrew_calendar/customizer.html @@ -0,0 +1,46 @@ + + + + + + + + Hebrew Calendar Customizer + + + + +
+
+
+
Hebrew Calendar Loader
+
+
+
Your location
+
+
+ + + + +
get your latitude and longitude from plus.codes or:
+ + + +
+
+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/apps/hebrew_calendar/customizer.mjs b/apps/hebrew_calendar/customizer.mjs new file mode 100644 index 000000000..06716a63b --- /dev/null +++ b/apps/hebrew_calendar/customizer.mjs @@ -0,0 +1,329 @@ +import { + HebrewCalendar, + HDate, + Location, + Zmanim, +} from "https://cdn.skypack.dev/@hebcal/core@^3?min"; + +function onload(event) { + event.preventDefault(); + const latLon = getLatLonFromForm(); + const events = generateHebCal(latLon); + const calendar = serializeEvents(events); + console.debug(calendar); + globalThis["cal"] = calendar; + loadWatch(calendar); +} + +function loadWatch(json) { + sendCustomizedApp({ + id: "hebrew_calendar", + + storage: [ + { + name: "hebrew_calendar.app.js", + url: "app.js", + // content below is same as app.js except for the first line which customizes the hebrewCalendar object used + content: ` +let hebrewCalendar = ${json}; + +const dayInMS = 86400000; + +const DateProvider = { now: () => Date.now() }; + +const Layout = require("Layout"); +const Locale = require("locale"); + +let nextEndingEvent; + +function getCurrentEvents() { + const now = DateProvider.now(); + + const current = hebrewCalendar.filter( + (x) => x.startEvent <= now && x.endEvent >= now + ); + + nextEndingEvent = current.reduce((acc, ev) => { + return Math.min(acc, ev.endEvent); + }, Infinity); + + return current.map((event, i) => { + return { + type: "txt", + font: "12x20", + id: "currentEvents" + i, + label: event.desc, + pad: 2, + bgCol: g.theme.bg, + }; + }); +} + +function getUpcomingEvents() { + const now = DateProvider.now(); + + const futureEvents = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS + ); + + let warning; + let eventsLeft = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS * 14 + ).length; + + if (eventsLeft < 14) { + warning = { + startEvent: 0, + type: "txt", + font: "4x6", + id: "warning", + label: "only " + eventsLeft + " events left in calendar; update soon", + pad: 2, + bgCol: g.theme.bg, + }; + } + + return futureEvents + .slice(0, 2) + .map((event, i) => { + return { + startEvent: event.startEvent, + type: "txt", + font: "6x8", + id: "upcomingEvents" + 1, + label: event.desc + " at " + Locale.time(new Date(event.startEvent), 1), + pad: 2, + bgCol: g.theme.bg, + }; + }) + .concat(warning) + .sort(function (a, b) { + return a.startEvent - b.startEvent; + }); +} + +function dateTime() { + return ( + Locale.dow(new Date(), 1) + + " " + + Locale.date(new Date(), 1) + + " " + + Locale.time(new Date(), 1) + ); +} + +function makeLayout() { + return new Layout( + { + type: "v", + c: [ + { + type: "txt", + font: "6x8", + id: "title", + label: "-- Hebrew Calendar Events --", + pad: 2, + bgCol: g.theme.bg2, + }, + { + type: "txt", + font: "6x8", + id: "currently", + label: "Currently", + pad: 2, + bgCol: g.theme.bgH, + }, + ] + .concat(getCurrentEvents()) + .concat([ + { + type: "txt", + font: "6x8", + label: "Upcoming", + id: "upcoming", + pad: 2, + bgCol: g.theme.bgH, + }, + ]) + .concat(getUpcomingEvents()) + .concat([ + { + type: "txt", + font: "Vector14", + id: "time", + label: dateTime(), + pad: 2, + bgCol: undefined, + }, + ]), + }, + { lazy: true } + ); +} +let layout = makeLayout(); +// see also https://www.espruino.com/Bangle.js+Layout#updating-the-screen + +// timeout used to update every minute +let drawTimeout; + +function draw() { + layout.time.label = dateTime(); + layout.render(); + + // schedule a draw for the next minute + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function () { + drawTimeout = undefined; + draw(); + }, 60000 - (DateProvider.now() % 60000)); + console.log("updated time"); +} + +// update time and draw +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); + +function findNextEvent() { + return hebrewCalendar.find((ev) => { + return ev.startEvent > DateProvider.now(); + }); +} + +function updateCalendar() { + layout.clear(); + layout = makeLayout(); + layout.forgetLazyState(); + layout.render(); + + let nextChange = Math.min( + findNextEvent().startEvent - DateProvider.now() + 5000, + nextEndingEvent - DateProvider.now() + 5000 + ); + setTimeout(updateCalendar, nextChange); + console.log("updated events"); +} + +updateCalendar(); + +Bangle.setUI("clock"); + `, + }, + ], + }); +} + +document + .querySelector("button[type=submit]") + .addEventListener("click", onload, false); + +document.querySelector("#geoloc")?.addEventListener("click", (event) => { + event.preventDefault(); + navigator.geolocation.getCurrentPosition( + (pos) => { + const { + coords: { latitude, longitude }, + } = pos; + locationElements[0].value = latitude; + locationElements[1].value = longitude; + console.debug(pos); + }, + (err) => { + if (err.PERMISSION_DENIED) { + alert("permission required to use geolocation api; enter manually"); + } + if (err.POSITION_UNAVAILABLE) { + alert("position unavailable; enter manually"); + } + }, + { enableHighAccuracy: false } + ); +}); + +document.querySelector( + "#hDate" +).innerText = `Today is ${new Date().toLocaleDateString()} & ${new HDate().toString()}`; + +const locationElements = [ + document.querySelector("#lat"), + document.querySelector("#lon"), +]; + +function getLatLonFromForm() { + const latLon = locationElements.map((el) => el.value); + if (locationElements.every((x) => x.checkValidity())) { + return latLon; + } else { + console.debug("lat lon invalid error"); + return [0, 0]; + } +} + +function groupBy(arr, fn) { + return arr + .map(typeof fn === "function" ? fn : (val) => val[fn]) + .reduce((acc, val, i) => { + acc[val] = (acc[val] || []).concat(arr[i]); + return acc; + }, {}); +} + +function generateHebCal(latLon) { + const location = new Location( + ...latLon, + document.querySelector("#inIL").checked + ); + + const now = new Date(); + + const options = { + year: now.getFullYear(), + isHebrewYear: false, + candlelighting: true, + location, + addHebrewDates: true, + addHebrewDatesForEvents: true, + sedrot: true, + start: now, + end: new Date(now.getFullYear(), now.getMonth() + 3), + }; + + const events = HebrewCalendar.calendar(options).map((ev) => { + const { desc, eventTime, startEvent, endEvent } = ev; + + const zman = new Zmanim(ev.date, ...latLon.map(Number)); + + let output = { + desc, + startEvent: startEvent?.eventTime?.getTime() || zman.gregEve().getTime(), + endEvent: endEvent?.eventTime?.getTime() || zman.shkiah().getTime(), + }; + + if (eventTime) { + delete output.startEvent; + delete output.endEvent; + output.startEvent = eventTime.getTime(); + output.endEvent = eventTime.getTime() + 60000 * 15; + } + + return output; + }); + + // console.table(events) + + return events.sort((a, b) => { + return a.startEvent - b.startEvent; + }); +} + +function enc(data) { + return btoa(heatshrink.compress(new TextEncoder().encode(data))); +} + +function serializeEvents(events) { + // const splitByGregorianMonth = groupBy(events, (evt) => { + // return new Date(evt.startEvent).getMonth(); + // }); + return JSON.stringify(events); +} diff --git a/apps/hebrew_calendar/hebrewDate.js b/apps/hebrew_calendar/hebrewDate.js deleted file mode 100644 index da0c9cf50..000000000 --- a/apps/hebrew_calendar/hebrewDate.js +++ /dev/null @@ -1,311 +0,0 @@ -/*! - * This script was taked from this page http://www.shamash.org/help/javadate.shtml and ported to Node.js by Ionică Bizău in https://github.com/IonicaBizau/hebrew-date - * - * This script was adapted from C sources written by - * Scott E. Lee, which contain the following copyright notice: - * - * Copyright 1993-1995, Scott E. Lee, all rights reserved. - * Permission granted to use, copy, modify, distribute and sell so long as - * the above copyright and this permission statement are retained in all - * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. - * - * Bill Hastings - * RBI Software Systems - * bhastings@rbi.com - */ -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; -var GREG_SDN_OFFSET = 32045, DAYS_PER_5_MONTHS = 153, DAYS_PER_4_YEARS = 1461, DAYS_PER_400_YEARS = 146097; -var HALAKIM_PER_HOUR = 1080, HALAKIM_PER_DAY = 25920, HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753, HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7); -var HEB_SDN_OFFSET = 347997, NEW_MOON_OF_CREATION = 31524, NOON = 18 * HALAKIM_PER_HOUR, AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204, AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589; -var SUN = 0, MON = 1, TUES = 2, WED = 3, THUR = 4, FRI = 5, SAT = 6; -function weekdayarr(d0, d1, d2, d3, d4, d5, d6) { - this[0] = d0; - this[1] = d1; - this[2] = d2; - this[3] = d3; - this[4] = d4; - this[5] = d5; - this[6] = d6; -} -function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; -} -function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; - this[12] = m12; - this[13] = m13; -} -function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; - this[12] = m12; - this[13] = m13; - this[14] = m14; - this[15] = m15; - this[16] = m16; - this[17] = m17; - this[18] = m18; -} -var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"), gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"), mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13); -/** - * hebrewDate - * Convert the Gregorian dates into Hebrew calendar dates. - * - * @name hebrewDate - * @function - * @param {Date|Number} inputDate The date object (representing the Gregorian date) or the year. - * @return {Object} An object containing: - * - * - `year`: The Hebrew year. - * - `month`: The Hebrew month. - * - `month_name`: The Hebrew month name. - * - `date`: The Hebrew date. - */ -function hebrewDate(inputDateOrYear) { - var inputMonth, inputDate; - var hebrewMonth = 0, hebrewDate = 0, hebrewYear = 0, metonicCycle = 0, metonicYear = 0, moladDay = 0, moladHalakim = 0; - function GregorianToSdn(inputYear, inputMonth, inputDay) { - var year = 0, month = 0, sdn = void 0; - // Make year a positive number - if (inputYear < 0) { - year = inputYear + 4801; - } - else { - year = inputYear + 4800; - } - // Adjust the start of the year - if (inputMonth > 2) { - month = inputMonth - 3; - } - else { - month = inputMonth + 9; - year--; - } - sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4); - sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4); - sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5); - sdn += inputDay - GREG_SDN_OFFSET; - return sdn; - } - function SdnToHebrew(sdn) { - var tishri1 = 0, tishri1After = 0, yearLength = 0, inputDay = sdn - HEB_SDN_OFFSET; - FindTishriMolad(inputDay); - tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); - if (inputDay >= tishri1) { - // It found Tishri 1 at the start of the year. - hebrewYear = metonicCycle * 19 + metonicYear + 1; - if (inputDay < tishri1 + 59) { - if (inputDay < tishri1 + 30) { - hebrewMonth = 1; - hebrewDate = inputDay - tishri1 + 1; - } - else { - hebrewMonth = 2; - hebrewDate = inputDay - tishri1 - 29; - } - return; - } - // We need the length of the year to figure this out,so find Tishri 1 of the next year. - moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim); - } - else { - // It found Tishri 1 at the end of the year. - hebrewYear = metonicCycle * 19 + metonicYear; - if (inputDay >= tishri1 - 177) { - // It is one of the last 6 months of the year. - if (inputDay > tishri1 - 30) { - hebrewMonth = 13; - hebrewDate = inputDay - tishri1 + 30; - } - else if (inputDay > tishri1 - 60) { - hebrewMonth = 12; - hebrewDate = inputDay - tishri1 + 60; - } - else if (inputDay > tishri1 - 89) { - hebrewMonth = 11; - hebrewDate = inputDay - tishri1 + 89; - } - else if (inputDay > tishri1 - 119) { - hebrewMonth = 10; - hebrewDate = inputDay - tishri1 + 119; - } - else if (inputDay > tishri1 - 148) { - hebrewMonth = 9; - hebrewDate = inputDay - tishri1 + 148; - } - else { - hebrewMonth = 8; - hebrewDate = inputDay - tishri1 + 178; - } - return; - } - else { - if (mpy[(hebrewYear - 1) % 19] == 13) { - hebrewMonth = 7; - hebrewDate = inputDay - tishri1 + 207; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - } - else { - hebrewMonth = 6; - hebrewDate = inputDay - tishri1 + 207; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - } - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 29; - if (hebrewDate > 0) - return; - // We need the length of the year to figure this out,so find Tishri 1 of this year. - tishri1After = tishri1; - FindTishriMolad(moladDay - 365); - tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); - } - } - yearLength = tishri1After - tishri1; - moladDay = inputDay - tishri1 - 29; - if (yearLength == 355 || yearLength == 385) { - // Heshvan has 30 days - if (moladDay <= 30) { - hebrewMonth = 2; - hebrewDate = moladDay; - return; - } - moladDay -= 30; - } - else { - // Heshvan has 29 days - if (moladDay <= 29) { - hebrewMonth = 2; - hebrewDate = moladDay; - return; - } - moladDay -= 29; - } - // It has to be Kislev. - hebrewMonth = 3; - hebrewDate = moladDay; - } - function FindTishriMolad(inputDay) { - // Estimate the metonic cycle number. Note that this may be an under - // estimate because there are 6939.6896 days in a metonic cycle not - // 6940,but it will never be an over estimate. The loop below will - // correct for any error in this estimate. - metonicCycle = Math.floor((inputDay + 310) / 6940); - // Calculate the time of the starting molad for this metonic cycle. - MoladOfMetonicCycle(); - // If the above was an under estimate,increment the cycle number until - // the correct one is found. For modern dates this loop is about 98.6% - // likely to not execute,even once,because the above estimate is - // really quite close. - while (moladDay < inputDay - 6940 + 310) { - metonicCycle++; - moladHalakim += HALAKIM_PER_METONIC_CYCLE; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - } - // Find the molad of Tishri closest to this date. - for (metonicYear = 0; metonicYear < 18; metonicYear++) { - if (moladDay > inputDay - 74) - break; - moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - } - } - function MoladOfMetonicCycle() { - var r1 = void 0, r2 = void 0, d1 = void 0, d2 = void 0; - // Start with the time of the first molad after creation. - r1 = NEW_MOON_OF_CREATION; - // Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 - // bits of the result will be in r2 and the lower 16 bits will be in r1. - r1 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE & 0xFFFF); - r2 = r1 >> 16; - r2 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE >> 16 & 0xFFFF); - // Calculate r2r1 / HALAKIM_PER_DAY. The remainder will be in r1,the - // upper 16 bits of the quotient will be in d2 and the lower 16 bits - // will be in d1. - d2 = Math.floor(r2 / HALAKIM_PER_DAY); - r2 -= d2 * HALAKIM_PER_DAY; - r1 = r2 << 16 | r1 & 0xFFFF; - d1 = Math.floor(r1 / HALAKIM_PER_DAY); - r1 -= d1 * HALAKIM_PER_DAY; - moladDay = d2 << 16 | d1; - moladHalakim = r1; - } - function Tishri1(metonicYear, moladDay, moladHalakim) { - var tishri1 = moladDay, dow = tishri1 % 7, leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18, lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0; - // Apply rules 2,3 and 4 - if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) { - tishri1++; - dow++; - if (dow == 7) - dow = 0; - } - // Apply rule 1 after the others because it can cause an additional delay of one day. - if (dow == WED || dow == FRI || dow == SUN) { - tishri1++; - } - return tishri1; - } - var inputYear = inputDateOrYear; - if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") { - inputMonth = inputDateOrYear.getMonth() + 1; - inputDate = inputDateOrYear.getDate(); - inputYear = inputDateOrYear.getFullYear(); - } - SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate)); - return { - year: hebrewYear, - month: hebrewMonth, - date: hebrewDate, - month_name: hMonth[hebrewMonth - 1] - }; -} - -exports.hebrewDate = hebrewDate; diff --git a/apps/hebrew_calendar/metadata.json b/apps/hebrew_calendar/metadata.json index a2b7932b6..dc10a99f2 100644 --- a/apps/hebrew_calendar/metadata.json +++ b/apps/hebrew_calendar/metadata.json @@ -2,25 +2,19 @@ "id": "hebrew_calendar", "name": "Hebrew Calendar", "shortName": "HebCal", - "version": "0.04", - "description": "lists the date according to the hebrew calendar", + "version": "0.06", + "description": "lists the date & holidays according to the hebrew calendar", "icon": "app.png", "allow_emulator": false, - "tags": "tool,locale", + "tags": "clocks,tools", + "custom": "customizer.html", "supports": [ "BANGLEJS", "BANGLEJS2" ], + "type": "clock", "readme": "README.md", "storage": [ - { - "name": "hebrew_calendar.app.js", - "url": "app.js" - }, - { - "name": "hebrewDate", - "url": "hebrewDate.js" - }, { "name": "hebrew_calendar.img", "url": "app-icon.js", diff --git a/apps/hidjoystick/ChangeLog b/apps/hidjoystick/ChangeLog index 5560f00bc..625daf4bb 100644 --- a/apps/hidjoystick/ChangeLog +++ b/apps/hidjoystick/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Make Bangle.js 2 compatible diff --git a/apps/hidjoystick/app.js b/apps/hidjoystick/app.js index 134814cee..69f56463d 100644 --- a/apps/hidjoystick/app.js +++ b/apps/hidjoystick/app.js @@ -1,7 +1,86 @@ -var storage = require('Storage'); +const storage = require('Storage'); +const Layout = require("Layout"); const settings = storage.readJSON('setting.json',1) || { HID: false }; +const BANGLEJS2 = process.env.HWVERSION == 2; +const sidebarWidth=18; +const buttonWidth = (Bangle.appRect.w-sidebarWidth)/2; +const buttonHeight = (Bangle.appRect.h-16)/2*0.85; // subtract text row and add a safety margin var sendInProgress = false; // Only send one message at a time, do not flood +var touchBtn2 = 0; +var touchBtn3 = 0; +var touchBtn4 = 0; +var touchBtn5 = 0; + +function renderBtnArrows(l) { + const d = g.getWidth() - l.width; + + function c(a) { + return { + width: 8, + height: a.length, + bpp: 1, + buffer: (new Uint8Array(a)).buffer + }; + } + + g.drawImage(c([0,8,12,14,255,14,12,8]),d,g.getHeight()/2); + if (!BANGLEJS2) { + g.drawImage(c([16,56,124,254,16,16,16,16]),d,40); + g.drawImage(c([16,16,16,16,254,124,56,16]),d,194); + } +} + +const layoutChilden = []; +if (BANGLEJS2) { // add virtual buttons in display + layoutChilden.push({type:"h", c:[ + {type:"btn", width:buttonWidth, height:buttonHeight, label:"BTN2", id:"touchBtn2" }, + {type:"btn", width:buttonWidth, height:buttonHeight, label:"BTN3", id:"touchBtn3" }, + ]}); +} +layoutChilden.push({type:"h", c:[ + {type:"txt", font:"6x8:2", label:"Joystick" }, +]}); +if (BANGLEJS2) { // add virtual buttons in display + layoutChilden.push({type:"h", c:[ + {type:"btn", width:buttonWidth, height:buttonHeight, label:"BTN4", id:"touchBtn4" }, + {type:"btn", width:buttonWidth, height:buttonHeight, label:"BTN5", id:"touchBtn5" }, + ]}); +} + +const layout = new Layout( + {type:"h", c:[ + {type:"v", width:Bangle.appRect.w-sidebarWidth, c: layoutChilden}, + {type:"custom", width:18, height: Bangle.appRect.h, render:renderBtnArrows } + ]} +); + +function isInBox(box, x, y) { + return x >= box.x && x < box.x+box.w && y >= box.y && y < box.y+box.h; +} + +if (BANGLEJS2) { + Bangle.on('drag', function(event) { + if (event.b == 0) { // release + touchBtn2 = touchBtn3 = touchBtn4 = touchBtn5 = 0; + } else if (isInBox(layout.touchBtn2, event.x, event.y)) { + touchBtn2 = 1; + touchBtn3 = touchBtn4 = touchBtn5 = 0; + } else if (isInBox(layout.touchBtn3, event.x, event.y)) { + touchBtn3 = 1; + touchBtn2 = touchBtn4 = touchBtn5 = 0; + } else if (isInBox(layout.touchBtn4, event.x, event.y)) { + touchBtn4 = 1; + touchBtn2 = touchBtn3 = touchBtn5 = 0; + } else if (isInBox(layout.touchBtn5, event.x, event.y)) { + touchBtn5 = 1; + touchBtn2 = touchBtn3 = touchBtn4 = 0; + } else { + // outside any buttons, release all + touchBtn2 = touchBtn3 = touchBtn4 = touchBtn5 = 0; + } + }); +} const sendHid = function (x, y, btn1, btn2, btn3, btn4, btn5, cb) { try { @@ -20,31 +99,17 @@ const sendHid = function (x, y, btn1, btn2, btn3, btn4, btn5, cb) { function drawApp() { g.clear(); - g.setFont("6x8",2); - g.setFontAlign(0,0); - g.drawString("Joystick", 120, 120); - const d = g.getWidth() - 18; - - function c(a) { - return { - width: 8, - height: a.length, - bpp: 1, - buffer: (new Uint8Array(a)).buffer - }; - } - - g.drawImage(c([16,56,124,254,16,16,16,16]),d,40); - g.drawImage(c([16,16,16,16,254,124,56,16]),d,194); - g.drawImage(c([0,8,12,14,255,14,12,8]),d,116); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + layout.render(); } function update() { - const btn1 = BTN1.read(); - const btn2 = BTN2.read(); - const btn3 = BTN3.read(); - const btn4 = BTN4.read(); - const btn5 = BTN5.read(); + const btn1 = BTN1 ? BTN1.read() : 0; + const btn2 = !BANGLEJS2 ? BTN2.read() : touchBtn2; + const btn3 = !BANGLEJS2 ? BTN3.read() : touchBtn3; + const btn4 = !BANGLEJS2 ? BTN4.read() : touchBtn4; + const btn5 = !BANGLEJS2 ? BTN5.read() : touchBtn5; const acc = Bangle.getAccel(); var x = acc.x*-127; var y = acc.y*-127; diff --git a/apps/hidjoystick/metadata.json b/apps/hidjoystick/metadata.json index e2b78a97b..c13ae2efa 100644 --- a/apps/hidjoystick/metadata.json +++ b/apps/hidjoystick/metadata.json @@ -2,11 +2,11 @@ "id": "hidjoystick", "name": "Bluetooth Joystick", "shortName": "Joystick", - "version": "0.01", - "description": "Emulates a 2 axis/5 button Joystick using the accelerometer as stick input and buttons 1-3, touch left as button 4 and touch right as button 5.", + "version": "0.02", + "description": "Emulates a 2 axis/5 button Joystick using the accelerometer as stick input and buttons 1-3, touch left as button 4 and touch right as button 5. On Bangle.js 2 buttons 2-5 are emulated with the touchscreen.", "icon": "app.png", "tags": "bluetooth", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], "storage": [ {"name":"hidjoystick.app.js","url":"app.js"}, {"name":"hidjoystick.img","url":"app-icon.js","evaluate":true} diff --git a/apps/homework/README.md b/apps/homework/README.md new file mode 100644 index 000000000..adad4ef39 --- /dev/null +++ b/apps/homework/README.md @@ -0,0 +1,4 @@ +# This is a simple homework app +Use the touchscreen to navigate. + +Requires the "textinput" library. (Tap keyboard) diff --git a/apps/homework/app-icon.js b/apps/homework/app-icon.js new file mode 100644 index 000000000..202b52b09 --- /dev/null +++ b/apps/homework/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQCyvd7oYTCwQYTCwgYRCwwYPIgpKQCA4YOBxIYMBhYLLHhgYEC5BsKDAYXHCwUBiUikAYIC4wtDC5IYCA4pEEC5QYBYRUCkQXJAA8K1Wq0AXHhGIxGAC5ZHHC8ZDDC4cM5qaBC8ZHHC68N6czmAXrL94X/C/4XHgUiCYIDDa54XXO/4XHAH4A/ABY=")) diff --git a/apps/homework/app.js b/apps/homework/app.js new file mode 100644 index 000000000..4ba786690 --- /dev/null +++ b/apps/homework/app.js @@ -0,0 +1,212 @@ +var Layout = require("Layout"); + +var homework = require("Storage").readJSON("homework.txt", "r"); +var mainCheckHomeworkMenu; + +var nhwmn = { // New homework Menu + "": { + "title": "New HW Subject:" + } + +}; + + + +function newHomeworkMenu() { + E.showMessage("Getting subjects..."); + var rawsubjects = require("Storage").read("subjects.txt"); // This code reads out the subjects list and removes the newline character at the end + var splitsubjects = rawsubjects.split(","); + var lastItem = splitsubjects[splitsubjects.length - 1]; + var thiscurrentsubject; + var command; + lastItem = lastItem.slice(0, -1); + splitsubjects[splitsubjects.length - 1] = lastItem; + for (let i = 0; i < splitsubjects.length; i++) { // loop through array and add to menu + thiscurrentsubject = splitsubjects[i]; + command = addNewHomework(thiscurrentsubject); + nhwmn[splitsubjects[i]] = addNewHomework.bind(null, thiscurrentsubject); + } + nhwmn["Back"] = function() {E.showMenu(mainMenu);}; + console.log(nhwmn); + E.showMenu(nhwmn); +} +var mode = "mainmenu"; +var statusmsg; +var mainMenu = { + "": { + title: "--Main Menu--" + }, + "New Homework": function() { + newHomeworkMenu(); + mode = "newhomework"; + }, + "Check Homework": function() { + checkUnfinishedHomeworkAssembler(); + }, + "Reset Homework": function() { + E.showPrompt("Are you sure you want to delete homework.txt?", { + buttons: { + "No": false, + "Yes": true + } + }).then(function(v) { + if (v) { + require("Storage").write("homework.txt", '{"homework":[]}'); + homework = require("Storage").readJSON("homework.txt", "r"); + E.showMenu(mainMenu); + + }else{ + E.showMenu(mainMenu); + } + }); + }, +}; + +function checkUnfinishedHomeworkAssembler() { + homework = require("Storage").readJSON("homework.txt", "r"); + var hwcount = Object.keys(homework.homework).length; + mainCheckHomeworkMenu = { + '': { + 'title': 'Unfinished HW:' + } + }; + // This code snippet gets the unfinished HW and puts it in mainCheckHomeworkMenu + // btw mainCheckHomeworkMenu displays all the homework, when tapping on it you get more details with checkPreciseHomework function + for (var i = 0; i < hwcount; ++i) { + if (homework.homework[i].done === false) { + var currentsubject = i; //attempting to pass i + mainCheckHomeworkMenu[homework.homework[i].subject] = checkPreciseHomework.bind(null, currentsubject); + } + + } + mainCheckHomeworkMenu["See Archived HW"] = function() { + checkFinishedHomeworkAssembler(); + }; + mainCheckHomeworkMenu["Back to Main Menu"] = function() { + mode = "mainmenu"; + E.showMenu(mainMenu); + }; + console.log(mainCheckHomeworkMenu); + E.showMenu(mainCheckHomeworkMenu); +} + +function checkFinishedHomeworkAssembler() { + homework = require("Storage").readJSON("homework.txt", "r"); + var hwcount = Object.keys(homework.homework).length; + mainCheckHomeworkMenu = { + '': { + 'title': 'Archived HW:' + } + }; + + // This code snippet gets the unfinished HW and puts it in mainCheckHomeworkMenu + // btw mainCheckHomeworkMenu displays all the homework, when tapping on it you get more details with checkPreciseHomework function (currently being written) + for (var i = 0; i < hwcount; ++i) { + if (homework.homework[i].done === true) { + var currentsubject = i; //attempting to pass i + mainCheckHomeworkMenu[homework.homework[i].subject] = checkPreciseHomework.bind(null, currentsubject); + } + + } + mainCheckHomeworkMenu["Back"] = function() { + mode = "mainmenu"; + E.showMenu(mainMenu); + }; + E.showMenu(mainCheckHomeworkMenu); +} + +function checkPreciseHomework(subjectnum) { // P A I N + homework = require("Storage").read("homework.txt", "r"); + homework = JSON.parse(homework); + var subject = homework.homework[subjectnum].subject; + var task = homework.homework[subjectnum].task; + var taskmsg = "Task: " + homework.homework[subjectnum].task; + if (homework.homework[subjectnum].done === false) { + statusmsg = "Status: Unfinished"; + } else { + statusmsg = "Status: Finished"; + } + var datetimerecieved = homework.homework[subjectnum].datetimerecievehw; + var datetimerecievedmsg = "Recieved: " + homework.homework[subjectnum].datetimerecievehw; + var checkPreciseHomeworkMenu = { + '': { + 'title': subject + }, + }; + checkPreciseHomeworkMenu[subject] = function() {}, + checkPreciseHomeworkMenu[taskmsg] = function() {}, + checkPreciseHomeworkMenu[statusmsg] = function() { + status = "Status: Finished"; + var d = new Date(); + var currenttime = require("locale").time(d, 1); + var currentdate = require("locale").date(d); + var datetime = (currenttime + " " + currentdate); + delete homework.homework[subjectnum]; + homework.homework.push({ + subject: subject, + task: task, + done: true, + datetimerecievehw: datetimerecieved, + datetimehwdone: datetime + }); + require("Storage").write("homework.txt", JSON.stringify(homework)); + checkUnfinishedHomeworkAssembler(); + }, + checkPreciseHomeworkMenu[datetimerecievedmsg] = function() {}, + checkPreciseHomeworkMenu["Back"] = function() { + checkUnfinishedHomeworkAssembler(); + }, + + E.showMenu(checkPreciseHomeworkMenu); + + +} + +function pushHomework(subject, status, datetimehwdone) { + homework = require("Storage").readJSON("homework.txt", "r"); + +} + +function addNewHomework(subject) { // Pass subject + console.log(subject); + require("textinput").input().then(result => { + if (result === "") { + mode = "newhomework"; + newHomeworkMenu(); + } else { + var d = new Date(); + // update time and date + var currenttime = require("locale").time(d, 1); + var currentdate = require("locale").date(d); + var datetime = (currenttime + " " + currentdate); + homework.homework.push({ + subject: subject, + task: result, + done: false, + datetimerecievehw: datetime + }); // TODO: when HW is done, add datetimeendhw !!! + console.log("subject is" + subject); + + //homework.homework[subject] = result; + require("Storage").write("homework.txt", JSON.stringify(homework)); + E.showMenu(mainMenu); + + } + }); + +} + +function main() { // why does this still exist + if (mode === "mainmenu") { + E.showMenu(mainMenu); + + } else if (mode === "newhomework") { + newHomeworkMenu() + + } +} +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +main(); +//loop = setInterval(main, 1); diff --git a/apps/homework/app.png b/apps/homework/app.png new file mode 100644 index 000000000..e6174d46b Binary files /dev/null and b/apps/homework/app.png differ diff --git a/apps/homework/metadata.json b/apps/homework/metadata.json new file mode 100644 index 000000000..2ba1e918f --- /dev/null +++ b/apps/homework/metadata.json @@ -0,0 +1,16 @@ + +{ "id": "homework", + "name": "Homework", + "shortName":"Homework", + "version":"0.1", + "description": "A simple app to manage homework", + "icon": "app.png", + "tags": "tool", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "custom": "subjects.html", + "storage": [ + {"name":"homework.app.js","url":"app.js"}, + {"name":"homework.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/homework/subjects.html b/apps/homework/subjects.html new file mode 100644 index 000000000..d3bf7a400 --- /dev/null +++ b/apps/homework/subjects.html @@ -0,0 +1,30 @@ + + + + + + +

Subjects:

+

Click

+ + + + + + diff --git a/apps/hrm/ChangeLog b/apps/hrm/ChangeLog index e559adfb6..f05a9dc13 100644 --- a/apps/hrm/ChangeLog +++ b/apps/hrm/ChangeLog @@ -6,3 +6,4 @@ 0.06: Add widgets 0.07: Update scaling for new firmware 0.08: Don't force backlight on/watch unlocked on Bangle 2 +0.09: Grey out BPM until confidence is over 50% diff --git a/apps/hrm/heartrate.js b/apps/hrm/heartrate.js index 305f0e1bc..703b60c01 100644 --- a/apps/hrm/heartrate.js +++ b/apps/hrm/heartrate.js @@ -35,9 +35,9 @@ function onHRM(h) { g.clearRect(0,24,g.getWidth(),80); g.setFont("6x8").drawString("Confidence "+hrmInfo.confidence+"%", px, 75); var str = hrmInfo.bpm; - g.setFontVector(40).drawString(str,px,45); + g.setFontVector(40).setColor(hrmInfo.confidence > 50 ? g.theme.fg : "#888").drawString(str,px,45); px += g.stringWidth(str)/2; - g.setFont("6x8"); + g.setFont("6x8").setColor(g.theme.fg); g.drawString("BPM",px+15,45); } Bangle.on('HRM', onHRM); @@ -101,4 +101,3 @@ function readHRM() { lastHrmPt = [hrmOffset, y]; } } - diff --git a/apps/hrm/metadata.json b/apps/hrm/metadata.json index 3e94c163c..10821d094 100644 --- a/apps/hrm/metadata.json +++ b/apps/hrm/metadata.json @@ -1,7 +1,7 @@ { "id": "hrm", "name": "Heart Rate Monitor", - "version": "0.08", + "version": "0.09", "description": "Measure your heart rate and see live sensor data", "icon": "heartrate.png", "tags": "health", diff --git a/apps/hrmaccevents/metadata.json b/apps/hrmaccevents/metadata.json index de59dceac..7207f685a 100644 --- a/apps/hrmaccevents/metadata.json +++ b/apps/hrmaccevents/metadata.json @@ -3,7 +3,7 @@ "name": "HRM Accelerometer event recorder", "shortName": "HRM ACC recorder", "version": "0.01", - "type": "ram", + "type": "RAM", "description": "Record HRM and accelerometer events in high resolution to CSV files in your browser", "icon": "app.png", "tags": "debug", diff --git a/apps/impwclock/metadata.json b/apps/impwclock/metadata.json index 120fbe795..733dbb957 100644 --- a/apps/impwclock/metadata.json +++ b/apps/impwclock/metadata.json @@ -7,6 +7,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}], "allow_emulator": true, "storage": [ diff --git a/apps/info/README.md b/apps/info/README.md index 007a9794e..32920cb75 100644 --- a/apps/info/README.md +++ b/apps/info/README.md @@ -7,7 +7,7 @@ screen. Very useful if combined with pattern launcher ;) ![](screenshot_1.png) ![](screenshot_2.png) -![](screenshot_2.png) +![](screenshot_3.png) ## Contributors diff --git a/apps/intclock/metadata.json b/apps/intclock/metadata.json index fff4c58b0..13f596392 100644 --- a/apps/intclock/metadata.json +++ b/apps/intclock/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"intclock.app.js","url":"app.js"}, diff --git a/apps/intervals/metadata.json b/apps/intervals/metadata.json index bc054a539..32c18ae70 100644 --- a/apps/intervals/metadata.json +++ b/apps/intervals/metadata.json @@ -7,6 +7,7 @@ "icon": "intervals.png", "tags": "", "supports": ["BANGLEJS"], + "readme": "README.md", "storage": [ {"name":"intervals.app.js","url":"intervals.app.js"}, {"name":"intervals.img","url":"intervals-icon.js","evaluate":true} diff --git a/apps/ios/boot.js b/apps/ios/boot.js index 50286c4a6..5ea7550eb 100644 --- a/apps/ios/boot.js +++ b/apps/ios/boot.js @@ -105,15 +105,33 @@ E.on('notify',msg=>{ "io.robbie.HomeAssistant": "Home Assistant", "net.weks.prowl": "Prowl", "net.whatsapp.WhatsApp": "WhatsApp", + "net.superblock.Pushover": "Pushover", "nl.ah.Appie": "Albert Heijn", "nl.postnl.TrackNTrace": "PostNL", + "org.whispersystems.signal": "Signal", "ph.telegra.Telegraph": "Telegram", "tv.twitch": "Twitch", // could also use NRF.ancsGetAppInfo(msg.appId) here }; var unicodeRemap = { - '2019':"'" + '2019':"'", + '260':"A", + '261':"a", + '262':"C", + '263':"c", + '280':"E", + '281':"e", + '321':"L", + '322':"l", + '323':"N", + '324':"n", + '346':"S", + '347':"s", + '377':"Z", + '378':"z", + '379':"Z", + '380':"z", }; var replacer = ""; //(n)=>print('Unknown unicode '+n.toString(16)); //if (appNames[msg.appId]) msg.a diff --git a/apps/ios/metadata.json b/apps/ios/metadata.json index 0083c66b0..eb75a6dbc 100644 --- a/apps/ios/metadata.json +++ b/apps/ios/metadata.json @@ -7,6 +7,7 @@ "tags": "tool,system,ios,apple,messages,notifications", "dependencies": {"messages":"app"}, "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "storage": [ {"name":"ios.app.js","url":"app.js"}, {"name":"ios.img","url":"app-icon.js","evaluate":true}, diff --git a/apps/kbmorse/ChangeLog b/apps/kbmorse/ChangeLog new file mode 100644 index 000000000..f62348ec8 --- /dev/null +++ b/apps/kbmorse/ChangeLog @@ -0,0 +1 @@ +0.01: New Keyboard! \ No newline at end of file diff --git a/apps/kbmorse/README.md b/apps/kbmorse/README.md new file mode 100644 index 000000000..2d5aa166f --- /dev/null +++ b/apps/kbmorse/README.md @@ -0,0 +1,25 @@ +# Morse Keyboard + +A library that provides the ability to input text by entering morse code. + +![demo](demo.gif) + +## Usage + + +* Press `BTN1` to input a dot, `BTN3` to input a dash, and `BTN2` to accept the +character for your current input. +* Long-press `BTN1` to toggle UPPERCASE for your next character. +* Long-press `BTN2` to finish editing. +* Tap the left side of the screen for backspace. +* Swipe left/right to move the cursor. +* Input three spaces in a row for a newline. + +The top/bottom of the screen show which characters start with your current input, +so basically you just look which side includes the letter you want to type, and +press that button to narrow your selection, until it appears next to `BTN2`. + + +## For Developers + +See the README for `kbswipe`/`kbtouch` for instructions on how to use this in your app. \ No newline at end of file diff --git a/apps/kbmorse/app.png b/apps/kbmorse/app.png new file mode 100644 index 000000000..0abc7e67d Binary files /dev/null and b/apps/kbmorse/app.png differ diff --git a/apps/kbmorse/demo.gif b/apps/kbmorse/demo.gif new file mode 100644 index 000000000..991c8c68d Binary files /dev/null and b/apps/kbmorse/demo.gif differ diff --git a/apps/kbmorse/lib.js b/apps/kbmorse/lib.js new file mode 100644 index 000000000..8bc177a46 --- /dev/null +++ b/apps/kbmorse/lib.js @@ -0,0 +1,247 @@ +exports.input = function(options) { + options = options || {}; + let text = options.text; + if ("string"!= typeof text) text = ""; + let code = "", + cur = text.length, // cursor position + uc = !text.length, // uppercase + spc = 0; // consecutive spaces entered + + const codes = { + // letters + "a": ".-", + "b": "-...", + "c": "-.-.", + "d": "-..", + "e": ".", + // no é + "f": "..-.", + "g": "--.", + "h": "....", + "i": "..", + "j": ".---", + "k": "-.-", + "l": ".-..", + "m": "--", + "n": "-.", + "o": "---", + "p": ".--.", + "q": "--.-", + "r": ".-.", + "s": "...", + "t": "-", + "u": "..-", + "v": "...-", + "w": ".--", + "x": "-..-", + "y": "-.--", + "z": "--..", + //digits + "1": ".----", + "2": "..---", + "3": "...--", + "4": "....-", + "5": ".....", + "6": "-....", + "7": "--...", + "8": "---..", + "9": "----.", + "0": "-----", + // punctuation + ".": ".-.-.-", + ",": "--..--", + ":": "---...", + "?": "..--..", + "!": "-.-.--", + "'": ".----.", + "-": "-....-", + "_": "..--.-", + "/": "-..-.", + "(": "-.--.", + ")": "-.--.-", + "\"": ".-..-.", + "=": "-...-", + "+": ".-.-.", + "*": "-..-", + "@": ".--.-.", + "$": "...-..-", + "&": ".-...", + }, chars = Object.keys(codes); + + function choices(start) { + return chars.filter(char => codes[char].startsWith(start)); + } + function char(code) { + if (code==="") return " "; + for(const char in codes) { + if (codes[char]===code) return char; + } + const c = choices(code); + if (c.length===1) return c[0]; // "-.-.-" is nothing, and only "-.-.--"(!) starts with it + return null; + } + + return new Promise((resolve, reject) => { + + function update() { + let dots = [], dashes = []; + layout.pick.label = (code==="" ? " " : ""); + choices(code).forEach(char => { + const c = codes[char]; + if (c===code) { + layout.pick.label = char; + } + const next = c.substring(code.length, code.length+1); + if (next===".") dots.push(char); + else if (next==="-") dashes.push(char); + }); + if (!code && spc>1) layout.pick.label = atob("ABIYAQAAAAAAAAAABwABwABwABwABwABwOBwOBwOBxwBxwBxwB/////////xwABwABwAAOAAOAAOAA=="); + g.setFont("6x8:2"); + const wrap = t => g.wrapString(t, Bangle.appRect.w-60).join("\n"); + layout.del.label = cur ? atob("AAwIAQ/hAiKkEiKhAg/gAA==") : " "; + layout.code.label = code; + layout.dots.label = wrap(dots.join(" ")); + layout.dashes.label = wrap(dashes.join(" ")); + if (uc) { + layout.pick.label = layout.pick.label.toUpperCase(); + layout.dots.label = layout.dots.label.toUpperCase(); + layout.dashes.label = layout.dashes.label.toUpperCase(); + } + let label = text.slice(0, cur)+"|"+text.slice(cur); + layout.text.label = g.wrapString(label, Bangle.appRect.w-80).join("\n") + .replace("|", atob("AAwQAfPPPAwAwAwAwAwAwAwAwAwAwAwAwPPPPA==")); + layout.update(); + layout.render(); + } + + function add(d) { + code += d; + const l = choices(code).length; + if (l===1) done(); + else if (l<1) { + Bangle.buzz(20); + code = code.slice(0, -1); + } else update(); + } + function del() { + if (code.length) code = code.slice(0, -1); // delete last dot/dash + else if (cur) { // delete char at cursor + text = text.slice(0, cur-1)+text.slice(cur); + cur--; + } else Bangle.buzz(20); // (already) at start of text + spc = 0; + uc = false; + update(); + } + + function done() { + let c = char(code); + if (c!==null) { + if (uc) c = c.toUpperCase(); + uc = false; + text = text.slice(0, cur)+c+text.slice(cur); + cur++; + code = ""; + if (c===" ") spc++; + else spc = 0; + if (spc>=3) { + text = text.slice(0, cur-3)+"\n"+text.slice(cur); + cur -= 2; + uc = true; + spc = 0; + } + update(); + } else { + console.log(`No char for ${code}!`); + Bangle.buzz(20); + } + } + + const Layout = require("Layout"); + let layout = new Layout({ + type: "h", c: [ + { + type: "v", width: Bangle.appRect.w-8, bgCol: g.theme.bg, c: [ + {id: "dots", type: "txt", font: "6x8:2", label: "", fillx: 1, bgCol: g.theme.bg}, + {filly: 1, bgCol: g.theme.bg}, + { + type: "h", fillx: 1, c: [ + {id: "del", type: "txt", font: "6x8", label: " + ({type: "txt", font: "6x8", height: Math.floor(Bangle.appRect.h/3), r: 1, label: l}) + ) + } + ] + }); + g.reset().clear(); + update(); + + if (Bangle.btnWatches) Bangle.btnWatches.forEach(clearWatch); + Bangle.btnWatches = []; + + // BTN1: press for dot, long-press to toggle uppercase + let ucTimeout; + const UC_TIME = 500; + Bangle.btnWatches.push(setWatch(e => { + if (ucTimeout) clearTimeout(ucTimeout); + ucTimeout = null; + if (e.state) { + // pressed: start UpperCase toggle timer + ucTimeout = setTimeout(() => { + ucTimeout = null; + uc = !uc; + update(); + }, UC_TIME); + } else if (e.time-e.lastTime { + if (enterTimeout) clearTimeout(enterTimeout); + enterTimeout = null; + if (e.state) { + // pressed: start UpperCase toggle timer + enterTimeout = setTimeout(() => { + enterTimeout = null; + resolve(text); + }, ENTER_TIME); + } else if (e.time-e.lastTime { + add("-"); + }, BTN3, {repeat: true, edge: "falling"})); + + // Left-hand side: backspace + if (Bangle.touchHandler) Bangle.removeListener("touch", Bangle.touchHandler); + Bangle.touchHandler = side => { + if (side===1) del(); + }; + Bangle.on("touch", Bangle.touchHandler); + + // swipe: move cursor + if (Bangle.swipeHandler) Bangle.removeListener("swipe", Bangle.swipeHandler); + Bangle.swipeHandler = dir => { + cur = Math.max(0, Math.min(text.length, cur+dir)); + update(); + }; + Bangle.on("swipe", Bangle.swipeHandler); + }); +}; \ No newline at end of file diff --git a/apps/kbmorse/metadata.json b/apps/kbmorse/metadata.json new file mode 100644 index 000000000..f9c5354f1 --- /dev/null +++ b/apps/kbmorse/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "kbmorse", + "name": "Morse keyboard", + "version": "0.01", + "description": "A library for text input as morse code", + "icon": "app.png", + "type": "textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS"], + "screenshots": [{"url":"screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"textinput","url":"lib.js"} + ] +} diff --git a/apps/kbmorse/screenshot.png b/apps/kbmorse/screenshot.png new file mode 100644 index 000000000..9050a45cd Binary files /dev/null and b/apps/kbmorse/screenshot.png differ diff --git a/apps/kbmulti/ChangeLog b/apps/kbmulti/ChangeLog new file mode 100644 index 000000000..709aa3203 --- /dev/null +++ b/apps/kbmulti/ChangeLog @@ -0,0 +1,2 @@ +0.01: New keyboard +0.02: Introduce setting "Show help button?". Make setting firstLaunch invisible by removing corresponding code from settings.js. Add marker that shows when character selection timeout has run out. Display opened text on launch when editing existing text string. Perfect horizontal alignment of buttons. Tweak help message letter casing. diff --git a/apps/kbmulti/README.md b/apps/kbmulti/README.md new file mode 100644 index 000000000..4c83d378e --- /dev/null +++ b/apps/kbmulti/README.md @@ -0,0 +1,17 @@ +# Multitap Keyboard + +A library that provides the ability to input text in a style familiar to anyone who had a mobile phone before they went all touchscreen. + +Swipe right for Space, left for Backspace, and up/down for Caps lock. Tap the '?' button in the app if you need a reminder! + +At time of writing, only the [Noteify app](http://microco.sm/out/Ffe9i) uses a keyboard. + +Uses the multitap keypad logic originally from here: http://www.espruino.com/Morse+Code+Texting + +![](screenshot_1.png) +![](screenshot_2.png) +![](screenshot_3.png) + +Written by: [Sir Indy](https://github.com/sir-indy) and [Thyttan](https://github.com/thyttan) + +For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/) diff --git a/apps/kbmulti/app.png b/apps/kbmulti/app.png new file mode 100644 index 000000000..5607a0553 Binary files /dev/null and b/apps/kbmulti/app.png differ diff --git a/apps/kbmulti/lib.js b/apps/kbmulti/lib.js new file mode 100644 index 000000000..5ccab4204 --- /dev/null +++ b/apps/kbmulti/lib.js @@ -0,0 +1,148 @@ +//Multitap logic originally from here: http://www.espruino.com/Morse+Code+Texting + +exports.input = function(options) { + options = options||{}; + var text = options.text; + if ("string"!=typeof text) text=""; + + var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {}; + if (settings.firstLaunch===undefined) { settings.firstLaunch = true; } + if (settings.charTimeout===undefined) { settings.charTimeout = 500; } + if (settings.showHelpBtn===undefined) { settings.showHelpBtn = true; } + + var fontSize = "6x15"; + var Layout = require("Layout"); + var letters = { + "1":".,!?1","2":"ABC2","3":"DEF3", + "4":"GHI4","5":"JKL5","6":"MNO6", + "7":"PQRS7","8":"TUV80","9":"WXYZ9", + }; + var helpMessage = 'Swipe:\nRight: Space\nLeft:Backspace\nUp/Down: Caps lock\n'; + + var charTimeout; // timeout after a key is pressed + var charCurrent; // current character (index in letters) + var charIndex; // index in letters[charCurrent] + var caps = true; + var layout; + var btnWidth = g.getWidth()/3 + + function displayText(hideMarker) { + layout.clear(layout.text); + layout.text.label = text.slice(settings.showHelpBtn ? -11 : -13) + (hideMarker ? " " : "_"); + layout.render(layout.text); + } + + function deactivateTimeout(charTimeout) { + if (charTimeout!==undefined) { + clearTimeout(charTimeout); + charTimeout = undefined; + } + } + + function backspace() { + deactivateTimeout(charTimeout); + text = text.slice(0, -1); + newCharacter(); + } + + function setCaps() { + caps = !caps; + for (var key in letters) { + layout[key].label = caps ? letters[key].toUpperCase() : letters[key].toLowerCase(); + } + layout.render(); + } + + function newCharacter(ch) { + displayText(); + charCurrent = ch; + charIndex = 0; + } + + function onKeyPad(key) { + deactivateTimeout(charTimeout); + // work out which char was pressed + if (key==charCurrent) { + charIndex = (charIndex+1) % letters[charCurrent].length; + text = text.slice(0, -1); + } else { + newCharacter(key); + } + var newLetter = letters[charCurrent][charIndex]; + text += (caps ? newLetter.toUpperCase() : newLetter.toLowerCase()); + // set a timeout + charTimeout = setTimeout(function() { + charTimeout = undefined; + newCharacter(); + }, settings.charTimeout); + displayText(charTimeout); + } + + function onSwipe(dirLeftRight, dirUpDown) { + if (dirUpDown) { + setCaps(); + } else if (dirLeftRight == 1) { + text += ' '; + newCharacter(); + } else if (dirLeftRight == -1) { + backspace(); + } + } + + function onHelp(resolve,reject) { + Bangle.removeListener("swipe", onSwipe); + E.showPrompt( + helpMessage, {title: "Help", buttons : {"Ok":true}} + ).then(function(v) { + Bangle.on('swipe', onSwipe); + generateLayout(resolve,reject); + layout.render(); + }); + } + + function generateLayout(resolve,reject) { + layout = new Layout( { + type:"v", c: [ + {type:"h", c: [ + {type:"txt", font:"12x20", label:text.slice(-12), id:"text", fillx:1}, + (settings.showHelpBtn ? {type:"btn", font:'6x8', label:'?', cb: l=>onHelp(resolve,reject), filly:1 } : {}), + ]}, + {type:"h", c: [ + {type:"btn", font:fontSize, label:letters[1], cb: l=>onKeyPad(1), id:'1', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[2], cb: l=>onKeyPad(2), id:'2', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[3], cb: l=>onKeyPad(3), id:'3', width:btnWidth, filly:1 }, + ]}, + {type:"h", filly:1, c: [ + {type:"btn", font:fontSize, label:letters[4], cb: l=>onKeyPad(4), id:'4', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[5], cb: l=>onKeyPad(5), id:'5', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[6], cb: l=>onKeyPad(6), id:'6', width:btnWidth, filly:1 }, + ]}, + {type:"h", filly:1, c: [ + {type:"btn", font:fontSize, label:letters[7], cb: l=>onKeyPad(7), id:'7', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[8], cb: l=>onKeyPad(8), id:'8', width:btnWidth, filly:1 }, + {type:"btn", font:fontSize, label:letters[9], cb: l=>onKeyPad(9), id:'9', width:btnWidth, filly:1 }, + ]}, + ] + },{back: ()=>{ + deactivateTimeout(charTimeout); + Bangle.setUI(); + Bangle.removeListener("swipe", onSwipe); + g.clearRect(Bangle.appRect); + resolve(text); + }}); + } + + return new Promise((resolve,reject) => { + g.clearRect(Bangle.appRect); + if (settings.firstLaunch) { + onHelp(resolve,reject); + settings.firstLaunch = false; + require('Storage').writeJSON("kbmulti.settings.json", settings); + } else { + generateLayout(resolve,reject); + displayText(false); + Bangle.on('swipe', onSwipe); + layout.render(); + } + }); +}; diff --git a/apps/kbmulti/metadata.json b/apps/kbmulti/metadata.json new file mode 100644 index 000000000..1efdb8847 --- /dev/null +++ b/apps/kbmulti/metadata.json @@ -0,0 +1,18 @@ +{ "id": "kbmulti", + "name": "Multitap keyboard", + "version":"0.02", + "description": "A library for text input via multitap/T9 style keypad", + "icon": "app.png", + "type":"textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS2"], + "screenshots": [{"url":"screenshot_1.png"},{"url":"screenshot_2.png"}], + "readme": "README.md", + "storage": [ + {"name":"textinput","url":"lib.js"}, + {"name":"kbmulti.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"kbmulti.settings.json"} + ] +} diff --git a/apps/kbmulti/screenshot_1.png b/apps/kbmulti/screenshot_1.png new file mode 100644 index 000000000..37e6e5da2 Binary files /dev/null and b/apps/kbmulti/screenshot_1.png differ diff --git a/apps/kbmulti/screenshot_2.png b/apps/kbmulti/screenshot_2.png new file mode 100644 index 000000000..d150d13bf Binary files /dev/null and b/apps/kbmulti/screenshot_2.png differ diff --git a/apps/kbmulti/screenshot_3.png b/apps/kbmulti/screenshot_3.png new file mode 100644 index 000000000..882ea7386 Binary files /dev/null and b/apps/kbmulti/screenshot_3.png differ diff --git a/apps/kbmulti/settings.js b/apps/kbmulti/settings.js new file mode 100644 index 000000000..8a66cd8f0 --- /dev/null +++ b/apps/kbmulti/settings.js @@ -0,0 +1,31 @@ +(function(back) { + function settings() { + var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {}; + if (settings.showHelpBtn===undefined) { settings.showHelpBtn = true; } + if (settings.charTimeout===undefined) { settings.charTimeout = 500; } + return settings; + } + + function updateSetting(setting, value) { + var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {}; + settings[setting] = value; + require('Storage').writeJSON("kbmulti.settings.json", settings); + } + + var mainmenu = { + "" : { "title" : /*LANG*/"Multitap keyboard" }, + "< Back" : back, + /*LANG*/'Character selection timeout [ms]': { + value: settings().charTimeout, + min: 200, max: 1500, step : 50, + format: v => v, + onchange: v => updateSetting("charTimeout", v), + }, + /*LANG*/'Show help button?': { + value: !!settings().showHelpBtn, + format: v => v?"Yes":"No", + onchange: v => updateSetting("showHelpBtn", v) + } + }; + E.showMenu(mainmenu); + }) diff --git a/apps/kbswipe/ChangeLog b/apps/kbswipe/ChangeLog new file mode 100644 index 000000000..f0dc54b69 --- /dev/null +++ b/apps/kbswipe/ChangeLog @@ -0,0 +1,4 @@ +0.01: New App! +0.02: Now keeps user input trace intact by changing how the screen is updated. +0.03: Positioning of marker now takes the height of the widget field into account. +0.04: Fix issue if going back without typing. diff --git a/apps/kbswipe/README.md b/apps/kbswipe/README.md new file mode 100644 index 000000000..3f5575777 --- /dev/null +++ b/apps/kbswipe/README.md @@ -0,0 +1,37 @@ +# Swipe Keyboard + +A library that provides the ability to input text by swiping PalmOS Graffiti-style characters onto the screen. + +To get a legend of available characters, just tap the screen. + +![](key.png) + +## Usage + +In your app's metadata, add: + +``` + "dependencies": {"textinput":"type"}, +``` + +From inside your app, call: + +``` +Bangle.loadWidgets(); +Bangle.drawWidgets(); +require("textinput").input({text:"Foo"}).then(result => { + console.log("Text input", E.toJS(result)); +}); +``` + +The first argument to `input` is an object containing the following: + +* `text` - initial text to edit + +(in the future, the ability to restrict usage of newline/etc may be added) + +## Make your own + +You can create your own keyboard input apps. Just ensure that they have +`"type":"textinput",` in their metadata and provide a library called `textinput` +that exports an `input` method. diff --git a/apps/kbswipe/app.png b/apps/kbswipe/app.png new file mode 100644 index 000000000..9564a4068 Binary files /dev/null and b/apps/kbswipe/app.png differ diff --git a/apps/kbswipe/key.png b/apps/kbswipe/key.png new file mode 100644 index 000000000..c5ed9bb9b Binary files /dev/null and b/apps/kbswipe/key.png differ diff --git a/apps/kbswipe/lib.js b/apps/kbswipe/lib.js new file mode 100644 index 000000000..417ac98d9 --- /dev/null +++ b/apps/kbswipe/lib.js @@ -0,0 +1,156 @@ +/* To make your own strokes, type: + +Bangle.on('stroke',print) + +on the left of the IDE, then do a stroke and copy out the Uint8Array line +*/ +exports.getStrokes = function(cb) { + cb("a", new Uint8Array([58, 159, 58, 155, 62, 144, 69, 127, 77, 106, 86, 90, 94, 77, 101, 68, 108, 62, 114, 59, 121, 59, 133, 61, 146, 70, 158, 88, 169, 107, 176, 124, 180, 135, 183, 144, 185, 152])); + cb("b", new Uint8Array([51, 47, 51, 77, 56, 123, 60, 151, 65, 163, 68, 164, 68, 144, 67, 108, 67, 76, 72, 43, 104, 51, 121, 74, 110, 87, 109, 95, 131, 117, 131, 140, 109, 152, 88, 157])); + cb("c", new Uint8Array([153, 62, 150, 62, 145, 62, 136, 62, 123, 62, 106, 65, 85, 70, 65, 75, 50, 82, 42, 93, 37, 106, 36, 119, 36, 130, 40, 140, 49, 147, 61, 153, 72, 156, 85, 157, 106, 158, 116, 158])); + cb("d", new Uint8Array([57, 178, 57, 176, 55, 171, 52, 163, 50, 154, 49, 146, 47, 135, 45, 121, 44, 108, 44, 97, 44, 85, 44, 75, 44, 66, 44, 58, 44, 48, 44, 38, 46, 31, 48, 26, 58, 21, 75, 20, 99, 26, 120, 35, 136, 51, 144, 70, 144, 88, 137, 110, 124, 131, 106, 145, 88, 153])); + cb("e", new Uint8Array([150, 72, 141, 69, 114, 68, 79, 69, 48, 77, 32, 81, 31, 85, 46, 91, 73, 95, 107, 100, 114, 103, 83, 117, 58, 134, 66, 143, 105, 148, 133, 148, 144, 148])); + cb("f", new Uint8Array([157, 52, 155, 52, 148, 52, 137, 52, 124, 52, 110, 52, 96, 52, 83, 52, 74, 52, 67, 52, 61, 52, 57, 52, 55, 52, 52, 52, 52, 54, 52, 58, 52, 64, 54, 75, 58, 97, 59, 117, 60, 130])); + cb("g", new Uint8Array([160, 66, 153, 62, 129, 58, 90, 56, 58, 57, 38, 65, 31, 86, 43, 125, 69, 152, 116, 166, 145, 154, 146, 134, 112, 116, 85, 108, 97, 106, 140, 106, 164, 106])); + cb("h", new Uint8Array([58, 50, 58, 55, 58, 64, 58, 80, 58, 102, 58, 122, 58, 139, 58, 153, 58, 164, 58, 171, 58, 177, 58, 179, 58, 181, 58, 180, 58, 173, 58, 163, 59, 154, 61, 138, 64, 114, 68, 95, 72, 84, 80, 79, 91, 79, 107, 82, 123, 93, 137, 111, 145, 130, 149, 147, 150, 154, 150, 159])); + cb("i", new Uint8Array([89, 48, 89, 49, 89, 51, 89, 55, 89, 60, 89, 68, 89, 78, 89, 91, 89, 103, 89, 114, 89, 124, 89, 132, 89, 138, 89, 144, 89, 148, 89, 151, 89, 154, 89, 156, 89, 157, 89, 158])); + cb("j", new Uint8Array([130, 57, 130, 61, 130, 73, 130, 91, 130, 113, 130, 133, 130, 147, 130, 156, 130, 161, 130, 164, 130, 166, 129, 168, 127, 168, 120, 168, 110, 168, 91, 167, 81, 167, 68, 167])); + cb("k", new Uint8Array([149, 63, 147, 68, 143, 76, 136, 89, 126, 106, 114, 123, 100, 136, 86, 147, 72, 153, 57, 155, 45, 152, 36, 145, 29, 131, 26, 117, 26, 104, 27, 93, 30, 86, 35, 80, 45, 77, 62, 80, 88, 96, 113, 116, 130, 131, 140, 142, 145, 149, 148, 153])); + cb("l", new Uint8Array([42, 55, 42, 59, 42, 69, 44, 87, 44, 107, 44, 128, 44, 143, 44, 156, 44, 163, 44, 167, 44, 169, 45, 170, 49, 170, 59, 169, 76, 167, 100, 164, 119, 162, 139, 160, 163, 159])); + cb("m", new Uint8Array([49, 165, 48, 162, 46, 156, 44, 148, 42, 138, 42, 126, 42, 113, 43, 101, 45, 91, 47, 82, 49, 75, 51, 71, 54, 70, 57, 70, 61, 74, 69, 81, 75, 91, 84, 104, 94, 121, 101, 132, 103, 137, 106, 130, 110, 114, 116, 92, 125, 75, 134, 65, 139, 62, 144, 66, 148, 83, 151, 108, 155, 132, 157, 149])); + cb("n", new Uint8Array([50, 165, 50, 160, 50, 153, 50, 140, 50, 122, 50, 103, 50, 83, 50, 65, 50, 52, 50, 45, 50, 43, 52, 52, 57, 67, 66, 90, 78, 112, 93, 131, 104, 143, 116, 152, 127, 159, 135, 160, 141, 150, 148, 125, 154, 96, 158, 71, 161, 56, 162, 49])); + cb("o", new Uint8Array([107, 58, 104, 58, 97, 61, 87, 68, 75, 77, 65, 88, 58, 103, 54, 116, 53, 126, 55, 135, 61, 143, 75, 149, 91, 150, 106, 148, 119, 141, 137, 125, 143, 115, 146, 104, 146, 89, 142, 78, 130, 70, 116, 65, 104, 62])); + cb("p", new Uint8Array([52, 59, 52, 64, 54, 73, 58, 88, 61, 104, 65, 119, 67, 130, 69, 138, 71, 145, 71, 147, 71, 148, 71, 143, 70, 133, 68, 120, 67, 108, 67, 97, 67, 89, 68, 79, 72, 67, 83, 60, 99, 58, 118, 58, 136, 63, 146, 70, 148, 77, 145, 84, 136, 91, 121, 95, 106, 97, 93, 97, 82, 97])); + cb("q", new Uint8Array([95, 59, 93, 59, 88, 59, 79, 59, 68, 61, 57, 67, 50, 77, 48, 89, 48, 103, 50, 117, 55, 130, 65, 140, 76, 145, 85, 146, 94, 144, 101, 140, 105, 136, 106, 127, 106, 113, 100, 98, 92, 86, 86, 79, 84, 75, 84, 72, 91, 69, 106, 67, 126, 67, 144, 67, 158, 67, 168, 67, 173, 67, 177, 67])); + cb("r", new Uint8Array([53, 49, 53, 62, 53, 91, 53, 127, 53, 146, 53, 147, 53, 128, 53, 94, 53, 69, 62, 44, 82, 42, 94, 50, 92, 68, 82, 85, 77, 93, 80, 102, 95, 119, 114, 134, 129, 145, 137, 150])); + cb("s", new Uint8Array([159, 72, 157, 70, 155, 68, 151, 66, 145, 63, 134, 60, 121, 58, 108, 56, 96, 55, 83, 55, 73, 55, 64, 56, 57, 60, 52, 65, 49, 71, 49, 76, 50, 81, 55, 87, 71, 94, 94, 100, 116, 104, 131, 108, 141, 114, 145, 124, 142, 135, 124, 146, 97, 153, 70, 157, 52, 158])); + cb("t", new Uint8Array([45, 55, 48, 55, 55, 55, 72, 55, 96, 55, 120, 55, 136, 55, 147, 55, 152, 55, 155, 55, 157, 55, 158, 56, 158, 60, 156, 70, 154, 86, 151, 102, 150, 114, 148, 125, 148, 138, 148, 146])); + cb("u", new Uint8Array([35, 52, 35, 59, 35, 73, 35, 90, 36, 114, 38, 133, 42, 146, 49, 153, 60, 157, 73, 158, 86, 156, 100, 152, 112, 144, 121, 131, 127, 114, 132, 97, 134, 85, 135, 73, 136, 61, 136, 56])); + cb("v", new Uint8Array([36, 55, 37, 59, 40, 68, 45, 83, 51, 100, 58, 118, 64, 132, 69, 142, 71, 149, 73, 156, 76, 158, 77, 160, 77, 159, 80, 151, 82, 137, 84, 122, 86, 111, 90, 91, 91, 78, 91, 68, 91, 63, 92, 61, 97, 61, 111, 61, 132, 61, 150, 61, 162, 61])); + cb("w", new Uint8Array([33, 58, 34, 81, 39, 127, 44, 151, 48, 161, 52, 162, 57, 154, 61, 136, 65, 115, 70, 95, 76, 95, 93, 121, 110, 146, 119, 151, 130, 129, 138, 84, 140, 56, 140, 45])); + cb("x", new Uint8Array([56, 63, 56, 67, 57, 74, 60, 89, 66, 109, 74, 129, 85, 145, 96, 158, 107, 164, 117, 167, 128, 164, 141, 155, 151, 140, 159, 122, 166, 105, 168, 89, 170, 81, 170, 73, 169, 66, 161, 63, 141, 68, 110, 83, 77, 110, 55, 134, 47, 145])); + cb("y", new Uint8Array([42, 56, 42, 70, 48, 97, 62, 109, 85, 106, 109, 90, 126, 65, 134, 47, 137, 45, 137, 75, 127, 125, 98, 141, 70, 133, 65, 126, 92, 137, 132, 156, 149, 166])); + cb("z", new Uint8Array([29, 62, 35, 62, 43, 62, 63, 62, 87, 62, 110, 62, 125, 62, 134, 62, 138, 62, 136, 63, 122, 68, 103, 77, 85, 91, 70, 107, 59, 120, 50, 132, 47, 138, 43, 143, 41, 148, 42, 151, 53, 155, 80, 157, 116, 158, 146, 158, 163, 158])); + cb("\b", new Uint8Array([183, 103, 182, 103, 180, 103, 176, 103, 169, 103, 159, 103, 147, 103, 133, 103, 116, 103, 101, 103, 85, 103, 73, 103, 61, 103, 52, 103, 38, 103, 34, 103, 29, 103, 27, 103, 26, 103, 25, 103, 24, 103])); + cb(" ", new Uint8Array([39, 118, 40, 118, 41, 118, 44, 118, 47, 118, 52, 118, 58, 118, 66, 118, 74, 118, 84, 118, 94, 118, 104, 117, 114, 116, 123, 116, 130, 116, 144, 116, 149, 116, 154, 116, 158, 116, 161, 116, 163, 116])); +}; + +exports.input = function(options) { + options = options||{}; + var text = options.text; + if ("string"!=typeof text) text=""; + +Bangle.strokes = {}; +exports.getStrokes( (id,s) => Bangle.strokes[id] = Unistroke.new(s) ); + + var flashToggle = false; + const R = Bangle.appRect; + var Rx1; + var Rx2; + var Ry1; + var Ry2; + + function findMarker(strArr) { + if (strArr.length == 0) { + Rx1 = 4; + Rx2 = 6*4; + Ry1 = 8*4 + R.y; + Ry2 = 8*4 + 3 + R.y; + } else if (strArr.length <= 4) { + Rx1 = (strArr[strArr.length-1].length)%7*6*4 + 4 ; + Rx2 = (strArr[strArr.length-1].length)%7*6*4 + 6*4; + Ry1 = (strArr.length)*(8*4) + Math.floor((strArr[strArr.length-1].length)/7)*(8*4) + R.y; + Ry2 = (strArr.length)*(8*4) + Math.floor((strArr[strArr.length-1].length)/7)*(8*4) + 3 + R.y; + } else { + Rx1 = (strArr[strArr.length-1].length)%7*6*4 + 4 ; + Rx2 = (strArr[strArr.length-1].length)%7*6*4 + 6*4; + Ry1 = (4)*(8*4) + Math.floor((strArr[strArr.length-1].length)/7)*(8*4) + R.y; + Ry2 = (4)*(8*4) + Math.floor((strArr[strArr.length-1].length)/7)*(8*4) + 3 + R.y; + } + //print(Rx1,Rx2,Ry1, Ry2); + return {x:Rx1,y:Ry1,x2:Rx2,y2:Ry2}; + } + + function draw(noclear) { + g.reset(); + var l = g.setFont("6x8:4").wrapString(text+' ', R.w-8); + if (!l) l = []; + //print(text+':'); + //print(l); + if (!noclear) (flashToggle?(g.fillRect(findMarker(l))):(g.clearRect(findMarker(l)))); + if (l.length>4) l=l.slice(-4); + g.drawString(l.join("\n"),R.x+4,R.y+4); + } + + /* + // This draws a big image to use in the README + (function() { + E.defrag(); + var b = Graphics.createArrayBuffer(500,420,1,{msb:true}); + var n=0; + exports.getStrokes((id,s) => { + var x = n%6; + var y = (n-x)/6; + s = b.transformVertices(s, {scale:0.55, x:x*85-20, y:y*85-20}); + b.fillCircle(s[0],s[1],3); + b.drawPoly(s); + n++; + }); + b.dump(); + })() + */ + + function show() { + g.reset(); + g.clearRect(R).setColor("#f00"); + var n=0; + exports.getStrokes((id,s) => { + var x = n%6; + var y = (n-x)/6; + s = g.transformVertices(s, {scale:0.16, x:R.x+x*30-4, y:R.y+y*30-4}); + g.fillRect(s[0]-1,s[1]-2,s[0]+1,s[1]+1); + g.drawPoly(s); + n++; + }); + } + + function strokeHandler(o) { + //print(o); + if (!flashInterval) + flashInterval = setInterval(() => { + flashToggle = !flashToggle; + draw(); + }, 1000); + if (o.stroke!==undefined) { + var ch = o.stroke; + if (ch=="\b") text = text.slice(0,-1); + else text += ch; + g.clearRect(R); + } + flashToggle = true; + draw(); + } + Bangle.on('stroke',strokeHandler); + g.reset().clearRect(R); + show(); + draw(false); + var flashInterval; + + return new Promise((resolve,reject) => { + var l;//last event + Bangle.setUI({mode:"custom", drag:e=>{ + if (l) g.reset().setColor("#f00").drawLine(l.x,l.y,e.x,e.y); + l = e.b ? e : 0; + },touch:() => { + if (flashInterval) clearInterval(flashInterval); + flashInterval = undefined; + show(); + }, back:()=>{ + Bangle.removeListener("stroke", strokeHandler); + if (flashInterval) clearInterval(flashInterval); + Bangle.setUI(); + g.clearRect(Bangle.appRect); + resolve(text); + }}); + }); +}; diff --git a/apps/kbswipe/metadata.json b/apps/kbswipe/metadata.json new file mode 100644 index 000000000..d4026c815 --- /dev/null +++ b/apps/kbswipe/metadata.json @@ -0,0 +1,14 @@ +{ "id": "kbswipe", + "name": "Swipe keyboard", + "version":"0.04", + "description": "A library for text input via PalmOS style swipe gestures (beta!)", + "icon": "app.png", + "type":"textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS2"], + "screenshots": [{"url":"screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"textinput","url":"lib.js"} + ] +} diff --git a/apps/kbswipe/screenshot.png b/apps/kbswipe/screenshot.png new file mode 100644 index 000000000..11788ad87 Binary files /dev/null and b/apps/kbswipe/screenshot.png differ diff --git a/apps/kbtouch/ChangeLog b/apps/kbtouch/ChangeLog new file mode 100644 index 000000000..17e824c00 --- /dev/null +++ b/apps/kbtouch/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Introduced settings to customize the layout and functionality of the keyboard. diff --git a/apps/kbtouch/README.md b/apps/kbtouch/README.md new file mode 100644 index 000000000..6bd0337a8 --- /dev/null +++ b/apps/kbtouch/README.md @@ -0,0 +1,48 @@ +# Touch Keyboard + +A library that provides an on-screen keyboard for text input. + +## Settings +Text size - small or big text font. Default=Big. Suggested=Small. + +Offset keyboard - display the keyboard on top, making it faster to see what character you have selected. Default=No. Suggested=Yes. + +Loop around - should the keyboard highlight loop around when going past the edges? Default=Yes. Suggested=No. + +One-to-one input and release to select - should the input correspond directly to discrete areas on the screen, instead of being handled by scaled relative changes in position on swipes? Default=No. Suggested=Yes. + +Speed scaling - how much should a swipe move the highligt on the keyboard? Higher number corresponds to slower movement. Not applicable if using one-to-one input. Default=24. Suggested=15. + +## Usage + +In your app's metadata, add: + +``` + "dependencies": {"textinput":"type"}, +``` + +From inside your app, call: + +``` +Bangle.loadWidgets(); +Bangle.drawWidgets(); +require("textinput").input({text:"Foo"}).then(result => { + console.log("Text input", E.toJS(result)); +}); +``` + +The first argument to `input` is an object containing the following: + +* `text` - initial text to edit + +(in the future, the ability to restrict usage of newline/etc may be added) + +## Make your own + +You can create your own keyboard input apps. Just ensure that they have +`"type":"textinput",` in their metadata and provide a library called `textinput` +that exports an `input` method. + +## To-do + +Make this Bangle.js 1 compatible (use left/right touch and up/down buttons) diff --git a/apps/kbtouch/app.png b/apps/kbtouch/app.png new file mode 100644 index 000000000..19aec3c05 Binary files /dev/null and b/apps/kbtouch/app.png differ diff --git a/apps/kbtouch/lib.js b/apps/kbtouch/lib.js new file mode 100644 index 000000000..db90440b9 --- /dev/null +++ b/apps/kbtouch/lib.js @@ -0,0 +1,168 @@ +exports.input = function(options) { + options = options||{}; + var text = options.text; + if ("string"!=typeof text) text=""; + + // Key Maps for Keyboard +var KEYMAPLOWER = [ + "`1234567890-=\b", + "\2qwertyuiop[]\n", + "\2asdfghjkl;'#\n", + " \\zxcvbnm,./ ", + ]; +var KEYMAPUPPER = [ + "¬!\"£$%^&*()_+\b", + "\2QWERTYUIOP{}\n", + "\2ASDFGHJKL:@~\n", + " |ZXCVBNM<>? ", + ]; +var KEYIMGL = Graphics.createImage(` + + + # + ### + ##### + # + # + # + # + # + # + # + # + # + # + # + # + # +`);KEYIMGL.transparent=0; +var KEYIMGR = Graphics.createImage(` + + + # + ## +##### + ## + # + + + +### + # + # + # + # + # +##### + ### + # + +#`);KEYIMGR.transparent=0; +/* If a char in the keymap is >=128, +subtract 128 and look in this array for +multi-character key codes*/ +var KEYEXTRA = [ + String.fromCharCode(27,91,68), // 0x80 left + String.fromCharCode(27,91,67), // 0x81 right + String.fromCharCode(27,91,65), // 0x82 up + String.fromCharCode(27,91,66), // 0x83 down + String.fromCharCode(27,91,53,126), // 0x84 page up + String.fromCharCode(27,91,54,126), // 0x85 page down +]; + +var settings = Object.assign({ + // default values + textSize: 1, + offsetKeyboard: 0, + loopAround: 1, + oneToOne: 0, + speedScaling: 24 +}, require('Storage').readJSON("kbtouch.settings.json", true) || {}); + +// state +const R = Bangle.appRect; +var kbx = 0, kby = 0, kbdx = 0, kbdy = 0, kbShift = false, flashToggle = false; +const PX=12, PY=16, DRAGSCALE=settings.speedScaling; +var xoff = 3, yoff = g.getHeight()-PY*(4+5*settings.offsetKeyboard); + +function draw() { + "ram"; + var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER; + //g.drawImage(KEYIMG,0,yoff); + g.reset().setFont("6x8:2"); + g.clearRect(R); + if (kbx>=0) + g.setColor(g.theme.bgH).fillRect(xoff+kbx*PX,yoff+kby*PY, xoff+(kbx+1)*PX-1,yoff+(kby+1)*PY-1).setColor(g.theme.fg); + g.drawImage(KEYIMGL,xoff,yoff+PY,{scale:2}); + g.drawImage(KEYIMGR,xoff+PX*13,yoff,{scale:2}); + g.drawString(map[0],xoff,yoff); + g.drawString(map[1],xoff,yoff+PY); + g.drawString(map[2],xoff,yoff+PY*2); + g.drawString(map[3],xoff,yoff+PY*3); + var l = g.setFont(settings.textSize ? "6x8:4":"6x8:2").wrapString(text+(flashToggle?"_":" "), R.w-8); + if (l.length>2+2*settings.textSize) l=l.slice(-(2+2*settings.textSize)); + g.drawString(l.join("\n"),R.x+4,R.y+4 +82*settings.offsetKeyboard); + + g.flip(); +} + g.reset().clearRect(R); + draw(); + var flashInterval = setInterval(() => { + flashToggle = !flashToggle; + draw(); + }, 1000); + + return new Promise((resolve,reject) => { + + Bangle.setUI({mode:"custom", drag:e=>{ + if (settings.oneToOne) { + kbx = Math.max(Math.min(Math.floor((e.x-16) / (6*2)) , 13) , 0); + kby = Math.max(Math.min(Math.floor((e.y-120) / (8*2)) , 3) , 0); + //print(e.y, kby, e.x, kbx); + } + + if (!settings.oneToOne) { + kbdx += e.dx; + kbdy += e.dy; + var dx = Math.round(kbdx/DRAGSCALE), dy = Math.round(kbdy/DRAGSCALE); + kbdx -= dx*DRAGSCALE; + kbdy -= dy*DRAGSCALE; + if (dx || dy) { + if (settings.loopAround) { + kbx = (kbx+dx+15)%15; + kby = (kby+dy+4)%4; + } else { + kbx = Math.max(Math.min((kbx+dx),13),0); + kby = Math.max(Math.min((kby+dy),3),0); + } + } + } + draw(); + + if (!e.b && e.y>Bangle.appRect.y && settings.oneToOne /*&& settings.releaseToSelect*/) { + var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER; + var ch = map[kby][kbx]; + if (ch=="\2") kbShift=!kbShift; + else if (ch=="\b") text = text.slice(0,-1); + else text += ch; + Bangle.buzz(20); + draw(); + } + },touch:()=>{ + if ( !settings.oneToOne /*|| !settings.releaseToSelect*/) { + var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER; + var ch = map[kby][kbx]; + if (ch=="\2") kbShift=!kbShift; + else if (ch=="\b") text = text.slice(0,-1); + else text += ch; + Bangle.buzz(20); + draw(); + } + },back:()=>{ + clearInterval(flashInterval); + Bangle.setUI(); + g.clearRect(Bangle.appRect); + resolve(text); + }}); + }); +}; diff --git a/apps/kbtouch/metadata.json b/apps/kbtouch/metadata.json new file mode 100644 index 000000000..f6d6d5228 --- /dev/null +++ b/apps/kbtouch/metadata.json @@ -0,0 +1,15 @@ +{ "id": "kbtouch", + "name": "Touch keyboard", + "version":"0.02", + "description": "A library for text input via onscreen keyboard", + "icon": "app.png", + "type":"textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS2"], + "screenshots": [{"url":"screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"textinput","url":"lib.js"}, + {"name":"kbtouch.settings.js","url":"settings.js"} + ] +} diff --git a/apps/kbtouch/screenshot.png b/apps/kbtouch/screenshot.png new file mode 100644 index 000000000..3aa772dda Binary files /dev/null and b/apps/kbtouch/screenshot.png differ diff --git a/apps/kbtouch/settings.js b/apps/kbtouch/settings.js new file mode 100644 index 000000000..871cc5d32 --- /dev/null +++ b/apps/kbtouch/settings.js @@ -0,0 +1,59 @@ +(function(back) { + function settings() { + let settings = require('Storage').readJSON("kbtouch.settings.json", true) || {}; + if (settings.textSize===undefined) settings.textSize=1; + if (settings.offsetKeyboard===undefined) settings.offsetKeyboard=0; + if (settings.loopAround===undefined) settings.loopAround=1; + if (settings.oneToOne===undefined) settings.oneToOne=0; + if (settings.speedScaling===undefined) settings.speedScaling=24; + return settings; + } + + function updateSetting(setting, value) { + let settings = require('Storage').readJSON("kbtouch.settings.json", true) || {}; + settings[setting] = value; + require('Storage').writeJSON("kbtouch.settings.json", settings); + } + + var mainmenu = { + "" : { "title" : /*LANG*/"Touch Keyboard" }, + "< Back" : back, + /*LANG*/'Text size': { + value: settings().textSize, + min: 0, max: 1, + format: v => [/*LANG*/"Small",/*LANG*/"Big"][v], + onchange: v => updateSetting("textSize", v) + }, + /*LANG*/'Offset keyboard': { + value: settings().offsetKeyboard, + min: 0, max: 1, + format: v => [/*LANG*/"No",/*LANG*/"Yes"][v], + onchange: v => updateSetting("offsetKeyboard", v) + }, + /*LANG*/'Loop around': { + value: settings().loopAround, + min: 0, max: 1, + format: v => [/*LANG*/"No",/*LANG*/"Yes"][v], + onchange: v => updateSetting("loopAround", v) + }, + /*LANG*/'One-to-one input and release to select': { + value: settings().oneToOne, + min: 0, max: 1, + format: v => [/*LANG*/"No",/*LANG*/"Yes"][v], + onchange: v => updateSetting("oneToOne", v) + }, + /*LANG*/'Speed scaling': { + value: settings().speedScaling, + min: 1, max: 24, step : 1, + format: v => v, + onchange: v => updateSetting("speedScaling", v) + } + ///*LANG*/'Release to select': { + // value: 1|settings().fontSize, + // min: 0, max: 1, + // format: v => [/*LANG*/"No",/*LANG*/"Yes"][v], + // onchange: v => updateSetting("releaseToSelect", v) + //} + }; + E.showMenu(mainmenu); +}) diff --git a/apps/launch/ChangeLog b/apps/launch/ChangeLog index b8c198d50..7248f69c3 100644 --- a/apps/launch/ChangeLog +++ b/apps/launch/ChangeLog @@ -12,3 +12,4 @@ 0.11: Merge Bangle.js 1 and 2 launchers, again 0.12: Add an option to hide clocks from the app list (fix #1015) Add /*LANG*/ tags for internationalisation +0.13: Add fullscreen mode diff --git a/apps/launch/README.md b/apps/launch/README.md new file mode 100644 index 000000000..4e6185dea --- /dev/null +++ b/apps/launch/README.md @@ -0,0 +1,14 @@ +Launcher +======== + +This is the default launcher but you can replace it with a customised launcher. + +The app is needed to display a menu with all the apps installed on your Bangle. You can launch an app by touching its name/icon. + +Settings +-------- + +- `Font` - The font used (`4x6`, `6x8`, `12x20`, `6x15` or `Vector`). Default `12x20`. +- `Vector Font Size` - The size of the font if `Font` is set to `Vector`. Default `10`. +- `Show Clocks` - If set to `No` then clocks won't appear in the app list. Default `Yes`. +- `Fullscreen` - If set to `Yes` then widgets won't be loaded. Default `No`. diff --git a/apps/launch/app.js b/apps/launch/app.js index 4ceabe751..556e61bfd 100644 --- a/apps/launch/app.js +++ b/apps/launch/app.js @@ -2,7 +2,10 @@ var s = require("Storage"); var scaleval = 1; var vectorval = 20; var font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2"; -let settings = Object.assign({ showClocks: true }, s.readJSON("launch.json", true) || {}); +let settings = Object.assign({ + showClocks: true, + fullscreen: false +}, s.readJSON("launch.json", true) || {}); if ("vectorsize" in settings) { vectorval = parseInt(settings.vectorsize); @@ -44,8 +47,11 @@ function drawApp(i, r) { } g.clear(); -Bangle.loadWidgets(); -Bangle.drawWidgets(); + +if (!settings.fullscreen) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} E.showScroller({ h : 64*scaleval, c : apps.length, diff --git a/apps/launch/metadata.json b/apps/launch/metadata.json index 96bbf104b..da76fc4bb 100644 --- a/apps/launch/metadata.json +++ b/apps/launch/metadata.json @@ -2,8 +2,9 @@ "id": "launch", "name": "Launcher", "shortName": "Launcher", - "version": "0.12", + "version": "0.13", "description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", + "readme": "README.md", "icon": "app.png", "type": "launch", "tags": "tool,system,launcher", diff --git a/apps/launch/settings.js b/apps/launch/settings.js index 60422e75c..5d37e1c1b 100644 --- a/apps/launch/settings.js +++ b/apps/launch/settings.js @@ -1,6 +1,9 @@ // make sure to enclose the function in parentheses (function(back) { - let settings = Object.assign({ showClocks: true }, require("Storage").readJSON("launch.json", true) || {}); + let settings = Object.assign({ + showClocks: true, + fullscreen: false + }, require("Storage").readJSON("launch.json", true) || {}); let fonts = g.getFonts(); function save(key, value) { @@ -8,7 +11,7 @@ require("Storage").write("launch.json",settings); } const appMenu = { - "": {"title": /*LANG*/"Launcher Settings"}, + "": { "title": /*LANG*/"Launcher" }, /*LANG*/"< Back": back, /*LANG*/"Font": { value: fonts.includes(settings.font)? fonts.indexOf(settings.font) : fonts.indexOf("12x20"), @@ -16,15 +19,20 @@ onchange: (m) => {save("font", fonts[m])}, format: v => fonts[v] }, - /*LANG*/"Vector font size": { + /*LANG*/"Vector Font Size": { value: settings.vectorsize || 10, min:10, max: 20,step:1,wrap:true, onchange: (m) => {save("vectorsize", m)} }, - /*LANG*/"Show clocks": { + /*LANG*/"Show Clocks": { value: settings.showClocks == true, format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", - onchange: (m) => {save("showClocks", m)} + onchange: (m) => { save("showClocks", m) } + }, + /*LANG*/"Fullscreen": { + value: settings.fullscreen == true, + format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", + onchange: (m) => { save("fullscreen", m) } } }; E.showMenu(appMenu); diff --git a/apps/lcars/ChangeLog b/apps/lcars/ChangeLog index 7d8fecb1e..e622feb1f 100644 --- a/apps/lcars/ChangeLog +++ b/apps/lcars/ChangeLog @@ -15,4 +15,8 @@ 0.15: Using wpedom to count steps. 0.16: Improved stability. Wind can now be shown. 0.17: Settings for mph/kph and other minor improvements. -0.18: Fullscreen mode can now be enabled or disabled in the settings. \ No newline at end of file +0.18: Fullscreen mode can now be enabled or disabled in the settings. +0.19: Alarms can not go bigger than 100. +0.20: Use alarm for alarm functionality instead of own implementation. +0.21: Add custom theming. +0.22: Fix alarm and add build in function for step counting. \ No newline at end of file diff --git a/apps/lcars/README.md b/apps/lcars/README.md index f979b2304..6d4b18b9a 100644 --- a/apps/lcars/README.md +++ b/apps/lcars/README.md @@ -1,9 +1,9 @@ # LCARS clock A simple LCARS inspired clock. -Note: To display the steps, the wpedom app is required. To show weather data -such as temperature, humidity or window you BangleJS must be connected -with Gadgetbride and the weather app must be installed. +To show weather data such as temperature, humidity or window you BangleJS must be connected +with Gadgetbride and the weather app must be installed. To use the timer +the "sched" app must be installed on your device. ## Control * Tap left / right to change between screens. @@ -15,9 +15,10 @@ with Gadgetbride and the weather app must be installed. * Tab on left/right to switch between different screens. * Cusomizable data that is shown on screen 1 (steps, weather etc.) * Shows random and real images of planets. - * Tap on top/bottom of screen 1 to activate an alarm. + * Tap on top/bottom of screen 1 to activate an alarm. Depends on widtmr. * The lower orange line indicates the battery level. * Display graphs (day or month) for steps + hrm on the second screen. + * Customizable theming colors in the settings menu of the app. ## Data that can be configured * Steps - Steps loaded via the wpedom app. @@ -36,8 +37,10 @@ Access different screens via tap on the left/ right side of the screen ![](screenshot_1.png) ![](screenshot_2.png) +## Creator +- [David Peer](https://github.com/peerdavid) ## Contributors -- [David Peer](https://github.com/peerdavid). -- [Adam Schmalhofer](https://github.com/adamschmalhofer). -- [Jon Warrington](https://github.com/BartokW). +- [Adam Schmalhofer](https://github.com/adamschmalhofer) +- [Jon Warrington](https://github.com/BartokW) +- [Ronin Stegner](https://github.com/Ronin0000) diff --git a/apps/lcars/bg_left.png b/apps/lcars/bg_left.png index 91c2bb6f7..3bae5e458 100644 Binary files a/apps/lcars/bg_left.png and b/apps/lcars/bg_left.png differ diff --git a/apps/lcars/bg_left_small.png b/apps/lcars/bg_left_small.png index bfdb110d9..8223a1898 100644 Binary files a/apps/lcars/bg_left_small.png and b/apps/lcars/bg_left_small.png differ diff --git a/apps/lcars/bg_right.png b/apps/lcars/bg_right.png index 6e23a5d6e..a87cf31d1 100644 Binary files a/apps/lcars/bg_right.png and b/apps/lcars/bg_right.png differ diff --git a/apps/lcars/bg_right_small.png b/apps/lcars/bg_right_small.png index df9d32b38..62d9ea651 100644 Binary files a/apps/lcars/bg_right_small.png and b/apps/lcars/bg_right_small.png differ diff --git a/apps/lcars/lcars.app.js b/apps/lcars/lcars.app.js index 7d5da2d8e..07ca51fd9 100644 --- a/apps/lcars/lcars.app.js +++ b/apps/lcars/lcars.app.js @@ -1,13 +1,17 @@ +const TIMER_IDX = "lcars"; const SETTINGS_FILE = "lcars.setting.json"; const locale = require('locale'); const storage = require('Storage') let settings = { alarm: -1, dataRow1: "Steps", - dataRow2: "Temp", + dataRow2: "HRM", dataRow3: "Battery", speed: "kph", fullscreen: false, + themeColor1BG: "#FF9900", + themeColor2BG: "#FF00DC", + themeColor3BG: "#0094FF", }; let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; for (const key in saved_settings) { @@ -17,9 +21,9 @@ for (const key in saved_settings) { /* * Colors to use */ -let cBlue = "#0094FF"; -let cOrange = "#FF9900"; -let cPurple = "#FF00DC"; +let color1 = settings.themeColor3BG; +let color2 = settings.themeColor1BG; +let color3 = settings.themeColor2BG; let cWhite = "#FFFFFF"; let cBlack = "#000000"; let cGrey = "#424242"; @@ -32,33 +36,77 @@ let lcarsViewPos = 0; var plotMonth = false; -/* - * Requirements and globals - */ +function convert24to16(input) +{ + let RGB888 = parseInt(input.replace(/^#/, ''), 16); + let r = (RGB888 & 0xFF0000) >> 16; + let g = (RGB888 & 0xFF00) >> 8; + let b = RGB888 & 0xFF; + r = (r * 249 + 1014) >> 11; + g = (g * 253 + 505) >> 10; + b = (b * 249 + 1014) >> 11; + let RGB565 = 0; + RGB565 = RGB565 | (r << 11); + RGB565 = RGB565 | (g << 5); + RGB565 = RGB565 | b; + + return "0x"+RGB565.toString(16); +} + +var color1C = convert24to16(color1); +var color2C = convert24to16(color2); +var color3C = convert24to16(color3); + +/* +* Requirements and globals +*/ + +var colorPalette = new Uint16Array([ + 0x0000, // not used + color2C, // second + color3C, // third + 0x0000, // not used + color1C, // first + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000, // not used + 0x0000 // not used +],0,1); var bgLeftFullscreen = { width : 27, height : 176, bpp : 3, transparent : 0, - buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAFCh/eX5Q/KAwdCAGVbtu27YCCoAJBkuWrNlAQRGCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A==")) + buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAoUP7y/KH5QGDoQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyDCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4"))), + palette: colorPalette }; var bgLeftNotFullscreen = { width : 27, height : 152, bpp : 3, transparent : 0, - buffer : require("heatshrink").decompress(atob("AAUM2XLlgCCwAJBBAuy4EAmQIF5cggAIGlmwgYIG2XIF42wF4ImGF4ImHJoQmGJoQdJhZNHNY47CgRNGBIJZHHgRiGBIRQ/KH5QCAGVbtu27YCCoAJBkuWrNlAQRkCiwRDAQPQBIMJCIYCBsAJBgomEtu0WoQmEy1YBIMBHYttIwQ7FyxQ/KHFlFAQ7F2weCHYplKChRTCCg5TCHw5TMAD0GzVp0wCCBBGaBIMaBAtpwECBA2mwEJBAugDgMmCIwJBF5EABAtoeQQvGCYQdPJoI7LMQzTCLJKAGzAJBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=")) + buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))), + palette: colorPalette }; var bgRightFullscreen = { width : 27, height : 176, bpp : 3, transparent : 0, - buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAnUP7y/KH4yGeVYAJrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgAA=")) + buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAnUP7y/KH4yGeVYAJ0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkp4CS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A=="))), + palette: colorPalette }; var bgRightNotFullscreen = { width : 27, height : 152, bpp : 3, transparent : 0, - buffer : require("heatshrink").decompress(atob("lmy5YCDBIUyBAmy5AJBhYUG2EAhgIFAQMAgQIGCgQABCg4ABEAwUNFI2AKZHAKZEgGRZTGOIUDQxJxGKH5Q/agwAxrdt23bAQVABIMly1ZsoCCMgUWCIYCB6AJBhIRDAQNgBIMFEwlt2i1CEwmWrAJBgI7FtpGCHYuWKH5QxEwpQDlo7F0A7IqBZBEwo7BCIwCBJo53CJoxiCJpIAdgOmzVpAQR/CgAIEAQJ2CBAoCBBIMmCg1oD4QLGFQUCCjQ+CKYw+CKY4JCKYwoCGRMaGREJDoroCgwdFzBlLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJox/CgA=")) + buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAx0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A="))), + palette: colorPalette }; var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen; @@ -124,11 +172,16 @@ Graphics.prototype.setFontAntonioLarge = function(scale) { */ var drawTimeout; function queueDraw() { + + // Faster updates during alarm to ensure that it is + // shown correctly... + var timeout = isAlarmEnabled() ? 10000 : 60000; + if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function() { drawTimeout = undefined; draw(); - }, 60000 - (Date.now() % 60000)); + }, timeout - (Date.now() % timeout)); } /** @@ -185,7 +238,7 @@ function _drawData(key, y, c){ value = E.getAnalogVRef().toFixed(2) + "V"; } else if(key == "HRM"){ - value = Math.round(Bangle.getHealthStatus("day").bpm); + value = Math.round(Bangle.getHealthStatus("last").bpm); } else if (key == "TEMP"){ var weather = getWeather(); @@ -238,8 +291,11 @@ function drawInfo(){ return; } + // Draw Infor is called from different sources so + // we have to ensure that the alignment is always the same. + g.setFontAlign(-1, -1, 0); g.setFontAntonioMedium(); - g.setColor(cOrange); + g.setColor(color2); g.clearRect(120, 10, g.getWidth(), 75); g.drawString("LCARS", 128, 13); @@ -249,7 +305,7 @@ function drawInfo(){ g.drawString("NOCON", 128, 33); } if(Bangle.isLocked()){ - g.setColor(cPurple); + g.setColor(color3); g.drawString("LOCK", 128, 53); } } @@ -280,7 +336,7 @@ function drawState(){ g.drawString("STATUS", 23+26, 108); } else { // Alarm within symbol - g.setColor(cOrange); + g.setColor(color2); g.drawString("ALARM", 23+26, 108); g.setColor(cWhite); g.setFontAntonioLarge(); @@ -295,19 +351,19 @@ function drawPosition0(){ // Draw background image var offset = settings.fullscreen ? 0 : 24; g.drawImage(bgLeft, 0, offset); - drawHorizontalBgLine(cBlue, 25, 120, offset, 4); - drawHorizontalBgLine(cBlue, 130, 176, offset, 4); - drawHorizontalBgLine(cPurple, 20, 70, 80, 4); - drawHorizontalBgLine(cPurple, 80, 176, 80, 4); - drawHorizontalBgLine(cOrange, 35, 110, 87, 4); - drawHorizontalBgLine(cOrange, 120, 176, 87, 4); + drawHorizontalBgLine(color1, 25, 120, offset, 4); + drawHorizontalBgLine(color1, 130, 176, offset, 4); + drawHorizontalBgLine(color3, 20, 70, 80, 4); + drawHorizontalBgLine(color3, 80, 176, 80, 4); + drawHorizontalBgLine(color2, 35, 110, 87, 4); + drawHorizontalBgLine(color2, 120, 176, 87, 4); // The last line is a battery indicator too var bat = E.getBattery() / 100.0; var batStart = 19; var batWidth = 172 - batStart; var batX2 = parseInt(batWidth * bat + batStart); - drawHorizontalBgLine(cOrange, batStart, batX2, 171, 5); + drawHorizontalBgLine(color2, batStart, batX2, 171, 5); drawHorizontalBgLine(cGrey, batX2, 172, 171, 5); for(var i=0; i+batStart<=172; i+=parseInt(batWidth/4)){ drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8) @@ -346,9 +402,9 @@ function drawPosition0(){ // Draw data g.setFontAlign(-1, -1, 0); g.setColor(cWhite); - drawData(settings.dataRow1, 97, cOrange); - drawData(settings.dataRow2, 122, cPurple); - drawData(settings.dataRow3, 147, cBlue); + drawData(settings.dataRow1, 97, color2); + drawData(settings.dataRow2, 122, color3); + drawData(settings.dataRow3, 147, color1); // Draw state drawState(); @@ -359,13 +415,13 @@ function drawPosition1(){ var offset = settings.fullscreen ? 0 : 24; g.drawImage(bgRight, 149, offset); if(settings.fullscreen){ - drawHorizontalBgLine(cBlue, 0, 140, offset, 4); + drawHorizontalBgLine(color1, 0, 140, offset, 4); } - drawHorizontalBgLine(cPurple, 0, 80, 80, 4); - drawHorizontalBgLine(cPurple, 90, 150, 80, 4); - drawHorizontalBgLine(cOrange, 0, 50, 87, 4); - drawHorizontalBgLine(cOrange, 60, 140, 87, 4); - drawHorizontalBgLine(cOrange, 0, 150, 171, 5); + drawHorizontalBgLine(color3, 0, 80, 80, 4); + drawHorizontalBgLine(color3, 90, 150, 80, 4); + drawHorizontalBgLine(color2, 0, 50, 87, 4); + drawHorizontalBgLine(color2, 60, 140, 87, 4); + drawHorizontalBgLine(color2, 0, 150, 171, 5); // Draw steps bars g.setColor(cWhite); @@ -480,9 +536,6 @@ function draw(){ // Queue draw first to ensure that its called in one minute again. queueDraw(); - // First handle alarm to show this correctly afterwards - handleAlarm(); - // Next draw the watch face g.reset(); g.clearRect(0, 0, g.getWidth(), g.getHeight()); @@ -507,17 +560,20 @@ function draw(){ * Step counter via widget */ function getSteps() { + var steps = 0; try{ if (WIDGETS.wpedom !== undefined) { - return WIDGETS.wpedom.getSteps(); + steps = WIDGETS.wpedom.getSteps(); } else if (WIDGETS.activepedom !== undefined) { - return WIDGETS.activepedom.getSteps(); + steps = WIDGETS.activepedom.getSteps(); + } else { + steps = Bangle.getHealthStatus("day").steps; } } catch(ex) { // In case we failed, we can only show 0 steps. } - return 0; + return steps; } @@ -526,78 +582,89 @@ function getWeather(){ try { weatherJson = storage.readJSON('weather.json'); + var weather = weatherJson.weather; + + // Temperature + weather.temp = locale.temp(weather.temp-273.15); + + // Humidity + weather.hum = weather.hum + "%"; + + // Wind + const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); + var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934; + weather.wind = Math.round(wind[1] * speedFactor); + + return weather + } catch(ex) { // Return default } - if(weatherJson === undefined){ - return { - temp: "-", - hum: "-", - txt: "-", - wind: "-", - wdir: "-", - wrose: "-" - }; - } - - var weather = weatherJson.weather; - - // Temperature - weather.temp = locale.temp(weather.temp-273.15); - - // Humidity - weather.hum = weather.hum + "%"; - - // Wind - const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); - var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934; - weather.wind = Math.round(wind[1] * speedFactor); - - return weather + return { + temp: " ? ", + hum: " ? ", + txt: " ? ", + wind: " ? ", + wdir: " ? ", + wrose: " ? " + }; } - /* * Handle alarm */ -function getCurrentTimeInMinutes(){ - return Math.floor(Date.now() / (1000*60)); -} - function isAlarmEnabled(){ - return settings.alarm >= 0; + try{ + var alarm = require('sched'); + var alarmObj = alarm.getAlarm(TIMER_IDX); + if(alarmObj===undefined || !alarmObj.on){ + return false; + } + + return true; + + } catch(ex){ } + return false; } function getAlarmMinutes(){ - var currentTime = getCurrentTimeInMinutes(); - return settings.alarm - currentTime; + if(!isAlarmEnabled()){ + return -1; + } + + var alarm = require('sched'); + var alarmObj = alarm.getAlarm(TIMER_IDX); + return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); } -function handleAlarm(){ - if(!isAlarmEnabled()){ - return; - } +function increaseAlarm(){ + try{ + var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0; + var alarm = require('sched') + alarm.setAlarm(TIMER_IDX, { + timer : (minutes+5)*60*1000, + }); + alarm.reload(); + } catch(ex){ } +} - if(getAlarmMinutes() > 0){ - return; - } +function decreaseAlarm(){ + try{ + var minutes = getAlarmMinutes(); + minutes -= 5; - // Alarm - var t = 300; - Bangle.buzz(t, 1) - .then(() => new Promise(resolve => setTimeout(resolve, t))) - .then(() => Bangle.buzz(t, 1)) - .then(() => new Promise(resolve => setTimeout(resolve, t))) - .then(() => Bangle.buzz(t, 1)) - .then(() => new Promise(resolve => setTimeout(resolve, t))) - .then(() => Bangle.buzz(t, 1)) - .then(() => new Promise(resolve => setTimeout(resolve, 5E3))) - .then(() => { - // Update alarm state to disabled - settings.alarm = -1; - storage.writeJSON(SETTINGS_FILE, settings); - }); + var alarm = require('sched') + alarm.setAlarm(TIMER_IDX, undefined); + + if(minutes > 0){ + alarm.setAlarm(TIMER_IDX, { + timer : minutes*60*1000, + }); + } + + alarm.reload(); + } catch(ex){ } } @@ -624,28 +691,6 @@ Bangle.on('charging',function(charging) { drawState(); }); - -function increaseAlarm(){ - if(isAlarmEnabled()){ - settings.alarm += 5; - } else { - settings.alarm = getCurrentTimeInMinutes() + 5; - } - - storage.writeJSON(SETTINGS_FILE, settings); -} - - -function decreaseAlarm(){ - if(isAlarmEnabled() && (settings.alarm-5 > getCurrentTimeInMinutes())){ - settings.alarm -= 5; - } else { - settings.alarm = -1; - } - - storage.writeJSON(SETTINGS_FILE, settings); -} - function feedback(){ Bangle.buzz(40, 0.3); } diff --git a/apps/lcars/lcars.settings.js b/apps/lcars/lcars.settings.js index 75add1ece..b64feb30e 100644 --- a/apps/lcars/lcars.settings.js +++ b/apps/lcars/lcars.settings.js @@ -5,11 +5,14 @@ const storage = require('Storage') let settings = { alarm: -1, - dataRow1: "Battery", - dataRow2: "Steps", - dataRow3: "Temp", + dataRow1: "Steps", + dataRow2: "HRM", + dataRow3: "Battery", speed: "kph", fullscreen: false, + themeColor1BG: "#FF9900", + themeColor2BG: "#FF00DC", + themeColor3BG: "#0094FF", }; let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; for (const key in saved_settings) { @@ -20,8 +23,11 @@ storage.write(SETTINGS_FILE, settings) } + var dataOptions = ["Steps", "Battery", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"]; var speedOptions = ["kph", "mph"]; + var color_options = ['Green','Orange','Cyan','Purple','Red','Blue','Yellow','White']; + var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF']; E.showMenu({ '': { 'title': 'LCARS Clock' }, @@ -69,6 +75,33 @@ settings.speed = speedOptions[v]; save(); }, + }, + 'Theme Color 1': { + value: 0 | bg_code.indexOf(settings.themeColor1BG), + min: 0, max: 7, + format: v => color_options[v], + onchange: v => { + settings.themeColor1BG = bg_code[v]; + save(); + }, + }, + 'Theme Color 2': { + value: 0 | bg_code.indexOf(settings.themeColor2BG), + min: 0, max: 7, + format: v => color_options[v], + onchange: v => { + settings.themeColor2BG = bg_code[v]; + save(); + }, + }, + 'Theme Color 3': { + value: 0 | bg_code.indexOf(settings.themeColor3BG), + min: 0, max: 7, + format: v => color_options[v], + onchange: v => { + settings.themeColor3BG = bg_code[v]; + save(); + }, } }); }) diff --git a/apps/lcars/metadata.json b/apps/lcars/metadata.json index e6ca10f79..40da1b37f 100644 --- a/apps/lcars/metadata.json +++ b/apps/lcars/metadata.json @@ -3,7 +3,7 @@ "name": "LCARS Clock", "shortName":"LCARS", "icon": "lcars.png", - "version":"0.18", + "version":"0.22", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "Library Computer Access Retrieval System (LCARS) clock.", diff --git a/apps/lightswitch/ChangeLog b/apps/lightswitch/ChangeLog index 7a7ecd027..2c6d2b5db 100644 --- a/apps/lightswitch/ChangeLog +++ b/apps/lightswitch/ChangeLog @@ -1,2 +1,4 @@ 0.01: New App! 0.02: Add the option to enable touching the widget only on clock and settings. +0.03: Settings page now uses built-in min/max/wrap (fix #1607) +0.04: Add masking widget input to other apps (using espruino/Espruino#2151), add a oversize option to increase the touch area. diff --git a/apps/lightswitch/README.md b/apps/lightswitch/README.md index d58de7ca4..67d070f5c 100644 --- a/apps/lightswitch/README.md +++ b/apps/lightswitch/README.md @@ -1,8 +1,11 @@ # Light Switch Widget -Whis this widget I wanted to create a solution to quickly en-/disable the LCD backlight and even change the brightness. +With this widget I wanted to create a solution to quickly en-/disable the LCD backlight and even change the brightness. In addition it shows the lock status with the option to personalize the lock icon with a tiny image. +All touch and drag inputs related to this widget are cached/masked to prevent actions in the active app. +(See [espruino/Espruino#2151](https://github.com/espruino/Espruino/issues/2151) for more information.) + --- ### Control --- @@ -39,6 +42,9 @@ In addition it shows the lock status with the option to personalize the lock ico * _clk+launch_ -> on all apps of the types _clock_ and _launch_ * _except apps_ -> on all apps of the types _clock_ and _launch_ and in the settings * _always on_ -> always enabled when the widget is displayed +* __Oversize__ + _0px_ / _1px_ / _..._ / __20px__ / _..._ / _50px_ + To make it easier to hit the widget, this value extends the touch area of the widget in all directions. * __Drag Delay__ _off_ / _50ms_ / _100ms_ / _..._ / __500ms__ / _..._ / _1000ms_ Change the maximum delay between first touch and re-touch/drag to change the brightness or disable changing the brightness completely. @@ -85,8 +91,6 @@ This images are stored in a seperate file _(lightswitch.images.json)_. ### Worth Mentioning --- #### To do list -* Catch the touch and draw input related to this widget to prevent actions in the active app. - _(For now I have no idea how to achieve this, help is appreciated)_ * Manage images for the lock icon through a _Customize and Upload App_ page. #### Requests, Bugs and Feedback diff --git a/apps/lightswitch/metadata.json b/apps/lightswitch/metadata.json index 902b1536b..54dc8389f 100644 --- a/apps/lightswitch/metadata.json +++ b/apps/lightswitch/metadata.json @@ -2,7 +2,7 @@ "id": "lightswitch", "name": "Light Switch Widget", "shortName": "Light Switch", - "version": "0.02", + "version": "0.04", "description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.", "icon": "images/app.png", "screenshots": [ diff --git a/apps/lightswitch/settings.js b/apps/lightswitch/settings.js index aac159148..5ac70bc28 100644 --- a/apps/lightswitch/settings.js +++ b/apps/lightswitch/settings.js @@ -6,7 +6,8 @@ var settings = Object.assign({ colors: "011", image: "default", - touchOn: "clock,launch", + touchOn: "always", + oversize: 20, dragDelay: 500, minValue: 0.1, unlockSide: "", @@ -44,9 +45,11 @@ // return entry for string value return { value: entry.value.indexOf(settings[key]), + min : 0, + max : entry.value.length - 1, + wrap : true, format: v => entry.title ? entry.title[v] : entry.value[v], onchange: function(v) { - this.value = v = v >= entry.value.length ? 0 : v < 0 ? entry.value.length - 1 : v; writeSetting(key, entry.value[v], entry.drawWidgets); if (entry.exec) entry.exec(entry.value[v]); } @@ -55,10 +58,12 @@ // return entry for numerical value return { value: settings[key] * entry.factor, + min : entry.min, + max : entry.max, step: entry.step, + wrap : true, format: v => v > 0 ? v + entry.unit : "off", onchange: function(v) { - this.value = v = v > entry.max ? entry.min : v < entry.min ? entry.max : v; writeSetting(key, v / entry.factor, entry.drawWidgets); }, }; @@ -92,6 +97,14 @@ value: ["", "clock", "clock,setting.app.js", "clock,launch", "clock,setting.app.js,launch", "always"], drawWidgets: true }, + oversize: { + factor: 1, + unit: "px", + min: 0, + max: 50, + step: 1, + drawWidgets: true + }, dragDelay: { factor: 1, unit: "ms", @@ -133,16 +146,17 @@ title: "Light Switch" }, "< Back": () => back(), - "-- Widget --------": 0, + "-- Widget": 0, "Bulb col": getEntry("colors"), "Image": getEntry("image"), - "-- Control -------": 0, + "-- Control": 0, "Touch": getEntry("touchOn"), + "Oversize": getEntry("oversize"), "Drag Delay": getEntry("dragDelay"), "Min Value": getEntry("minValue"), - "-- Unlock --------": 0, + "-- Unlock": 0, "TapSide": getEntry("unlockSide"), - "-- Flash ---------": 0, + "-- Flash": 0, "TapSide ": getEntry("tapSide"), "Tap": getEntry("tapOn"), "Timeout": getEntry("tOut"), diff --git a/apps/lightswitch/settings.json b/apps/lightswitch/settings.json index 3d88e2282..176c3cea0 100644 --- a/apps/lightswitch/settings.json +++ b/apps/lightswitch/settings.json @@ -10,8 +10,8 @@ "101" -> magenta * image: string // - "default" -> - "random" -> + "default" -> image nearest to the default lock + "random" -> a random image from all available * touchOn: string // select when widget touch is active "" -> only on default clock @@ -19,6 +19,9 @@ "clock,launch" -> on all clocks and lanchers (default) "always" -> always + * oversize: int // extends the touch area of the widget in px in all directions + 0 to 50, 20 as default + * dragDelay: int // drag listener reset time in ms // time until a drag is needed to activate backlight changing mode 0 -> disabled @@ -59,6 +62,7 @@ "colors": "011", "image": "default", "touchOn": "clock,launch", + "oversize": 20, "dragDelay": 500, "minValue": 0.1, "unlockSide": "", diff --git a/apps/lightswitch/widget.js b/apps/lightswitch/widget.js index 119a114fe..829f75102 100644 --- a/apps/lightswitch/widget.js +++ b/apps/lightswitch/widget.js @@ -3,7 +3,8 @@ var settings = Object.assign({ colors: "011", image: "default", - touchOn: "clock,launch", + touchOn: "always", + oversize: 20, dragDelay: 500, minValue: 0.1, unlockSide: "", @@ -162,6 +163,11 @@ // change brigthness value, skip write to storage while still touching w.changeValue(value, event.b); + // masks this drag event by messing up the event handler + // see https://github.com/espruino/Espruino/issues/2151 + Bangle.removeListener("drag", w.dragListener); + Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]); + // on touch release remove drag listener and reset drag status to indicate stopped drag action if (!event.b) { Bangle.removeListener("drag", w.dragListener); @@ -184,14 +190,14 @@ if (w.dragStatus === "off") { // check if inside widget area - if (!(!w || cursor.x < w.x || cursor.x > w.x + w.width || - cursor.y < w.y || cursor.y > w.y + 23)) { + if (!(!w || cursor.x < w.x - w.oversize || cursor.x > w.x + w.width + w.oversize || + cursor.y < w.y - w.oversize || cursor.y > w.y + 23 + w.oversize)) { // first touch feedback Bangle.buzz(25); // check if drag is disabled if (w.dragDelay) { - // add drag listener - Bangle.on("drag", w.dragListener); + // add drag listener at first position + Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]); // set drag timeout w.dragStatus = setTimeout((w) => { // remove drag listener @@ -204,6 +210,10 @@ } // switch backlight w.changeValue(); + // masks this touch event by messing up the event handler + // see https://github.com/espruino/Espruino/issues/2151 + Bangle.removeListener("touch", w.touchListener); + Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]); } } @@ -236,11 +246,11 @@ // add lock listener Bangle.on("lock", w.draw); - // add touch listener to control the light depending on settings + // add touch listener to control the light depending on settings at first position if (w.touchOn === "always" || !global.__FILE__ || w.touchOn.includes(__FILE__) || w.touchOn.includes(require("Storage").readJSON(__FILE__.replace("app.js", "info")).type)) - Bangle.on("touch", w.touchListener); + Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]); // add tap listener to unlock and/or flash backlight if (w.unlockSide || w.tapSide) Bangle.on("tap", require("lightswitch.js").tapListener); diff --git a/apps/locale/ChangeLog b/apps/locale/ChangeLog index 39b825e02..d21cb1a56 100644 --- a/apps/locale/ChangeLog +++ b/apps/locale/ChangeLog @@ -7,7 +7,7 @@ 0.06: Remove translations if not required Ensure 'on' is always supplied for translations 0.07: Improve handling of non-ASCII characters (fix #469) -0.08: Added Mavigation units and en_NAV +0.08: Added Navigation units and en_NAV 0.09: Added New Zealand en_NZ 0.10: Apply 12hour setting to time 0.11: Added translations for nl_NL and changes one formatting @@ -15,4 +15,7 @@ 0.13: Now use shorter de_DE date format to more closely match other languages for size 0.14: Added some first translations for Messages in nl_NL 0.15: Fixed sv_SE formatting, long date does not work well for Bangle.js2 - Added Swedish localisation with English text \ No newline at end of file + Added Swedish localisation with English text +0.16: Remove global variables that used RAM + Add second 'dp' argument for decimal places in distance/speed/temp (fix #1523) +0.17: Fix regression where long month names were 'undefined' (fix #1641) diff --git a/apps/locale/README.md b/apps/locale/README.md index fd43c2825..99fba7945 100644 --- a/apps/locale/README.md +++ b/apps/locale/README.md @@ -1,25 +1,34 @@ -# Languages (locale) +Languages (locale) +================== Country-specific app internationalisation. This is not an app, but instead it is a library that can be used by -other applications or widgets to display messages. +other applications or widgets to provide locale-friendly -## Usage +- Dates +- Time (12h/24h) +- Days of the Week +- Months +- Currency values +- Distances/Lengths/Speed (metric/imperial) +- Temperature (°C/°F) -Some menus that pop up are translated automatically, but if you're -writing an application you can use the `locale` library to +Usage +----- + +If you're writing an application you can use the `locale` library to do all the translation for you. See https://www.espruino.com/Bangle.js+Locale for full examples. ```JS // Date to date string (long) ->require('locale').date(new Date()) +>require("locale").date(new Date()) ="Donnerstag, 02. April 2020" // Date to date string (short) ->require('locale').date(new Date(),1) +>require("locale").date(new Date(), 1) ="02.04.2020" ``` diff --git a/apps/locale/locale.html b/apps/locale/locale.html index b23225d5f..6eb0d94ea 100644 --- a/apps/locale/locale.html +++ b/apps/locale/locale.html @@ -16,6 +16,7 @@

Then click

+ + + + diff --git a/apps/noteify/menu.png b/apps/noteify/menu.png new file mode 100644 index 000000000..66988d1f6 Binary files /dev/null and b/apps/noteify/menu.png differ diff --git a/apps/noteify/metadata.json b/apps/noteify/metadata.json new file mode 100644 index 000000000..7e897d1f0 --- /dev/null +++ b/apps/noteify/metadata.json @@ -0,0 +1,23 @@ +{ + "id": "noteify", + "name": "Noteify", + "version": "0.01", + "description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.", + "icon": "app.png", + "tags": "tool,alarm", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"noteify.app.js","url":"app.js"}, + {"name":"noteify.img","url":"app-icon.js","evaluate":true}, + {"name":"noteify.wid.js","url":"widget.js"} + ], + "data": [{"name":"noteify.json"}], + "dependencies": {"scheduler":"type","textinput":"type"}, + "interface": "interface.html", + "screenshots": [ + {"url": "menu.png"}, + {"url": "note.png"}, + {"url": "timer-alert.png"} + ] +} diff --git a/apps/noteify/note.png b/apps/noteify/note.png new file mode 100644 index 000000000..ab8f172e4 Binary files /dev/null and b/apps/noteify/note.png differ diff --git a/apps/noteify/timer-alert.png b/apps/noteify/timer-alert.png new file mode 100644 index 000000000..01b4b8f17 Binary files /dev/null and b/apps/noteify/timer-alert.png differ diff --git a/apps/noteify/widget.js b/apps/noteify/widget.js new file mode 100644 index 000000000..052ac9ebd --- /dev/null +++ b/apps/noteify/widget.js @@ -0,0 +1,8 @@ +WIDGETS["alarm"]={area:"tl",width:0,draw:function() { + if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); + },reload:function() { + // don't include library here as we're trying to use as little RAM as possible + WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0; + } +}; +WIDGETS["alarm"].reload(); diff --git a/apps/numerals/metadata.json b/apps/numerals/metadata.json index dcb86da9a..6ba850d86 100644 --- a/apps/numerals/metadata.json +++ b/apps/numerals/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "numerals,clock", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "screenshots": [{"url":"bangle1-numerals-screenshot.png"}], "storage": [ diff --git a/apps/openwind/ChangeLog b/apps/openwind/ChangeLog new file mode 100644 index 000000000..1e5f791b2 --- /dev/null +++ b/apps/openwind/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Fix true wind computation, add swipe gesture to pause GPS diff --git a/apps/openwind/README.md b/apps/openwind/README.md new file mode 100644 index 000000000..c03ec1401 --- /dev/null +++ b/apps/openwind/README.md @@ -0,0 +1,24 @@ +# OpenWind + +Receive and display data from a wireless [OpenWind](https://www.openwind.de/) sailing wind instrument on the Bangle. + +## Usage + +Upon startup, the app will attempt to automatically connect to the wind instrument. This typically only takes a few seconds. + +## Features + +The app displays the apparent wind direction (via a green dot) and speed (green numbers, in knots) relative to the mounting direction of the wind vane. +If "True wind" is enabled in settings and a GPS fix is available, the true wind speed and direction (relative to the mounting direction of the vane) is +additionally displayed in red. In this mode, the speed over ground in knots is also shown at the bottom left of the screen. + +## Controls + +In the main app, when true wind mode is enabled (see below), swiping left on the screen will temporarily disable GPS (to preserve battery); a small +red satellite symbol will appear on the bottom right. Swiping right will turn GPS back on. +The settings app provides the following two settings: + + * True wind: enables or disables true wind calculations; enabling this will turn on GPS inside the app + * Mounting angle: mounting relative to the boat of the wind instrument (in degrees) + +![](openwind_screenshot.png) diff --git a/apps/openwind/app-icon.js b/apps/openwind/app-icon.js new file mode 100644 index 000000000..b86738955 --- /dev/null +++ b/apps/openwind/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4A/AH4A/AH4AzhMJF94wtF+QwsF/4APnAACF54wZFoYxNF7guHGBQv0GCwuJGBIvFACov/AD4vvd6Yv/GCoumGIwtpAH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AHoA==")) diff --git a/apps/openwind/app.js b/apps/openwind/app.js new file mode 100644 index 000000000..db67804f3 --- /dev/null +++ b/apps/openwind/app.js @@ -0,0 +1,137 @@ +OW_CHAR_UUID = '0000cc91-0000-1000-8000-00805f9b34fb'; +require("Font7x11Numeric7Seg").add(Graphics); +var gatt = {}; +var cx = g.getWidth()/2; +var cy = 24+(g.getHeight()-24)/2; +var w = (g.getWidth()-24)/2; +var y1 = 24; +var y2 = g.getHeight()-1; +var gps_course = { spd: 0 }; +var course_marker_len = g.getWidth()/4; + +var settings = require("Storage").readJSON('openwindsettings.json', 1) || {}; + +var pause_gps = false; + +var i = 0; +var hullpoly = []; +for (y=-1; y<=1; y+=0.1) { + hullpoly[i++] = cx - (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3; + hullpoly[i++] = cy - y*w*0.7; +} +for (y=1; y>=-1; y-=0.1) { + hullpoly[i++] = cx + (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3; + hullpoly[i++] = cy - y*w*0.7; +} + +function wind_updated(ev) { + if (ev.target.uuid == "0xcc91") { + awa = settings.mount_angle+ev.target.value.getInt16(1, true)*0.1; + if (awa<0) awa += 360; + aws = ev.target.value.getInt16(3, true)*0.01; + //console.log(awa, aws); + if (gps_course.spd > 0) { + wv = { // wind vector (in "earth" reference frame) + vlon: Math.sin(Math.PI*(gps_course.course+(awa+180))/180)*aws, + vlat: Math.cos(Math.PI*(gps_course.course+(awa+180))/180)*aws + }; + twv = { vlon: wv.vlon+gps_course.vlon, vlat: wv.vlat+gps_course.vlat }; + tws = Math.sqrt(Math.pow(twv.vlon,2)+Math.pow(twv.vlat, 2)); + twa = 180+Math.atan2(twv.vlon, twv.vlat)*180/Math.PI-gps_course.course; + if (twa<0) twa += 360; + if (twa>360) twa -=360; + } + else { + tws = -1; + twa = 0; + } + draw_compass(awa,aws,twa,tws); + } +} + +function draw_compass(awa, aws, twa, tws) { + g.clearRect(0, 24, g.getWidth()-1, g.getHeight()-1); + fh = w*0.15; + g.setColor(0, 0, 1).fillPoly(hullpoly); + g.setFontVector(fh).setColor(g.theme.fg); + g.setFontAlign(0, 0, 0).drawString("0", cx, 24+fh/2); + g.setFontAlign(0, 0, 1).drawString("90", g.getWidth()-12-fh, cy); + g.setFontAlign(0, 0, 2).drawString("180", cx, g.getHeight()-fh/2); + g.setFontAlign(0, 0, 3).drawString("270", 12+fh/2, cy); + for (i=0; i<4; ++i) { + a = i*Math.PI/2+Math.PI/4; + g.drawLineAA(cx+Math.cos(a)*w*0.85, cy+Math.sin(a)*w*0.85, cx+Math.cos(a)*w*0.99, cy+Math.sin(a)*w*0.99); + } + g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy-Math.cos(Math.PI*awa/180)*w*0.9, w*0.1); + if (tws>0) g.setColor(1, 0, 0).fillCircle(cx+Math.sin(Math.PI*twa/180)*w*0.9, cy+Math.cos(Math.PI*twa/180)*w*0.9, w*0.1); + g.setColor(0, 1, 0).setFont("7x11Numeric7Seg",w*0.06); + g.setFontAlign(0, 0, 0).drawString(aws.toFixed(1), cx, cy-0.32*w); + if (!pause_gps) { + if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w); + if (settings.truewind && gps_course.spd!=-1) { + spd = gps_course.spd/1.852; + g.setColor(g.theme.fg).setFont("7x11Numeric7Seg", w*0.03).setFontAlign(-1, 1, 0).drawString(spd.toFixed(1), 1, g.getHeight()-1); + } + } + if (pause_gps) g.setColor("#f00").drawImage(atob("DAwBEAKARAKQE4DwHkPqPRGKAEAA"),g.getWidth()-15, g.getHeight()-15); +} + +function parseDevice(d) { + device = d; + console.log("Found device"); + device.gatt.connect().then(function(ga) { + console.log("Connected"); + gatt = ga; + return ga.getPrimaryService("cc90"); + }).then(function(s) { + return s.getCharacteristic("cc91"); + }).then(function(c) { + c.on('characteristicvaluechanged', (event)=>wind_updated(event)); + return c.startNotifications(); + }).then(function() { + console.log("Done!"); + }).catch(function(e) { + console.log("ERROR"+e); + });} + +function connection_setup() { + NRF.setScan(); + NRF.setScan(parseDevice, { filters: [{services:["cc90"]}], timeout: 2000}); + console.log("Scanning for OW sensor"); +} + +if (settings.truewind) { + Bangle.on('GPS',function(fix) { + if (fix.fix && fix.satellites>3 && fix.speed>2) { // only uses fixes w/ more than 3 sats and speed > 2kph + gps_course = + { vlon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852, + vlat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852, + lat: fix.lat, + lon: fix.lon, + spd: fix.speed, + course: fix.course + }; + } + else gps_course.spd = -1; + }); + Bangle.setGPSPower(1, "app"); +} + +if (settings.truewind) { + Bangle.on("swipe", (d)=>{ + if (d==-1 && !pause_gps) { + pause_gps = true; + Bangle.setGPSPower(0); + draw_compass(0, 0, 0, 0); + } + else if (d==1 && pause_gps) { + pause_gps = false; + Bangle.setGPSPower(1, "app"); + draw_compass(0, 0, 0, 0); + } + }); +} +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw_compass(0, 0, 0, 0); +connection_setup(); diff --git a/apps/openwind/app.png b/apps/openwind/app.png new file mode 100644 index 000000000..9fd64efba Binary files /dev/null and b/apps/openwind/app.png differ diff --git a/apps/openwind/metadata.json b/apps/openwind/metadata.json new file mode 100644 index 000000000..43961cc44 --- /dev/null +++ b/apps/openwind/metadata.json @@ -0,0 +1,15 @@ +{ "id": "openwind", + "name": "OpenWind", + "shortName":"OpenWind", + "version":"0.02", + "description": "OpenWind", + "icon": "openwind.png", + "readme": "README.md", + "tags": "ble,outdoors,gps,sailing", + "supports" : ["BANGLEJS", "BANGLEJS2"], + "storage": [ + {"name":"openwind.app.js","url":"app.js"}, + {"name":"openwind.img","url":"app-icon.js","evaluate":true}, + {"name":"openwind.settings.js", "url":"settings.js"} + ] +} diff --git a/apps/openwind/openwind.png b/apps/openwind/openwind.png new file mode 100644 index 000000000..9fd64efba Binary files /dev/null and b/apps/openwind/openwind.png differ diff --git a/apps/openwind/openwind_screenshot.png b/apps/openwind/openwind_screenshot.png new file mode 100644 index 000000000..05143e8a4 Binary files /dev/null and b/apps/openwind/openwind_screenshot.png differ diff --git a/apps/openwind/settings.js b/apps/openwind/settings.js new file mode 100644 index 000000000..a7e3a1abe --- /dev/null +++ b/apps/openwind/settings.js @@ -0,0 +1,44 @@ +// This file should contain exactly one function, which shows the app's settings +/** + * @param {function} back Use back() to return to settings menu + */ +const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off"; +(function(back) { + const SETTINGS_FILE = 'openwindsettings.json' + // initialize with default settings... + let settings = { + 'truewind': false, + 'mount_angle': 0 + } + // ...and overwrite them with any saved values + // This way saved values are preserved if a new version adds more settings + const storage = require('Storage') + const saved = storage.readJSON(SETTINGS_FILE, 1) || {} + for (const key in saved) { + settings[key] = saved[key]; + } + // creates a function to safe a specific setting, e.g. save('color')(1) + function save(key) { + return function (value) { + settings[key] = value; + storage.write(SETTINGS_FILE, settings); + } + } + const menu = { + '': { 'title': 'OpenWind' }, + '< Back': back, + 'True wind': { + value: settings.truewind, + format: boolFormat, + onchange: save('truewind'), + }, + 'Mounting angle': { + value: settings.mount_angle, + min: 0, + max: 355, + step: 5, + onchange: save('mount_angle'), + } + } + E.showMenu(menu); +}) diff --git a/apps/pebble/ChangeLog b/apps/pebble/ChangeLog index 0cba5a2b2..274c34a34 100644 --- a/apps/pebble/ChangeLog +++ b/apps/pebble/ChangeLog @@ -1,7 +1,10 @@ -0.01: first release -0.02: included deployment of pebble.settings.js in apps.json +0.01: First release +0.02: Included deployment of pebble.settings.js in apps.json 0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot 0.04: Fix widget hiding code (fix #1046) 0.05: Fix typo in settings - Purple -0.06: Added dependancy on Pedometer Widget -0.07: Fixed icon and ong file to 48x48 +0.06: Add dependancy on Pedometer Widget +0.07: Fix icon and ong file to 48x48 +0.08: Add theme options and optional lock symbol +0.09: Add support for internationalization (LANG placeholders + "locale" module) + Get steps from built-in step counter (widpedom no more needed, fix #1697) diff --git a/apps/pebble/README.md b/apps/pebble/README.md index 4b0233781..051f46e37 100644 --- a/apps/pebble/README.md +++ b/apps/pebble/README.md @@ -1,14 +1,13 @@ # Pebble - *a Pebble style clock with configurable background color, to keep the revolution going* +*A Pebble style clock with configurable background color, to keep the revolution going* * Designed specifically for Bangle 2 -* A choice of 6 different background colous through its setting menu. Goto Settings, App/Widget settings, Pebble. -* Supports the Light and Dark themes -* Uses pedometer widget to get latest step count -* Dependant apps are installed when Pebble installs +* A choice of 6 different background colous through its settings menu. Goto *Settings* → *Apps* → *Pebble*. +* Supports the Light and Dark themes (or set theme independently) * Uses the whole screen, widgets are made invisible but still run in the background * When battery is less than 30% main screen goes Red +* Optionally show a lock symbol when screen is locked (default off, enable in Settings) ![](pebble_screenshot.png) ![](pebble_screenshot2.png) diff --git a/apps/pebble/metadata.json b/apps/pebble/metadata.json index 4295d7507..f3c1fcc12 100644 --- a/apps/pebble/metadata.json +++ b/apps/pebble/metadata.json @@ -2,15 +2,15 @@ "id": "pebble", "name": "Pebble Clock", "shortName": "Pebble", - "version": "0.07", + "version": "0.09", "description": "A pebble style clock to keep the rebellion going", - "dependencies": {"widpedom":"app"}, "readme": "README.md", "icon": "pebble.png", "screenshots": [{"url":"pebble_screenshot.png"}], "type": "clock", "tags": "clock", "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, "storage": [ {"name":"pebble.app.js","url":"pebble.app.js"}, {"name":"pebble.settings.js","url":"pebble.settings.js"}, diff --git a/apps/pebble/pebble.app.js b/apps/pebble/pebble.app.js index 106e09b82..774b24c3f 100644 --- a/apps/pebble/pebble.app.js +++ b/apps/pebble/pebble.app.js @@ -10,9 +10,10 @@ Graphics.prototype.setFontLECO1976Regular22 = function(scale) { const SETTINGS_FILE = "pebble.json"; let settings; +let theme; function loadSettings() { - settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green'}; + settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false}; } var img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AE8AAAoeXoAfeDQUBmcyD7A+Dh///8QD649CiAfaHwUvD4sEHy0DDYIfEICg+Cn4fHICY+DD4nxcgojOHwgfEIAYfRCIQaDD4ZAFD5r7DH4//kAfRCIZ/GAAnwD5p9DX44fTHgYSBf4ofVDAQEBl4fFUAgfOXoQzBgIfFBAIfPP4RAEAoYAB+cRiK/SG4h/WIBAfXIA7CBAAswD55AHn6fUIBMCD65AHl4gCmcziAfQQJqfQQJpiDgk0IDXxQLRAEECaBM+QgRYRYgUIA0CD4ggSQJiDCiAKBICszAAswD55AHABKBVD7BAFABIqBD5pAFABPxD55AOD6BADiIAJQAyxLABwf/gaAPAH4A/AH4ARA==")); @@ -26,50 +27,49 @@ const h3 = 7*h/8; let batteryWarning = false; function draw() { + let locale = require("locale"); let date = new Date(); - let da = date.toString().split(" "); - let timeStr = da[4].substr(0,5); + let dayOfWeek = locale.dow(date, 1).toUpperCase(); + let dayOfMonth = date.getDate(); + let time = locale.time(date, 1); + let steps = Bangle.getHealthStatus("day").steps; const t = 6; - // turn the warning on once we have dipped below 30% - if (E.getBattery() < 30) + if (E.getBattery() < 30) { + // turn the warning on once we have dipped below 30% batteryWarning = true; - - // turn the warning off once we have dipped above 40% - if (E.getBattery() > 40) + } else if (E.getBattery() > 40) { + // turn the warning off once we have dipped above 40% batteryWarning = false; + } g.reset(); g.setColor(settings.bg); g.fillRect(0, 0, w, h2 - t); // contrast bar - g.setColor(g.theme.fg); + g.setColor(theme.fg); g.fillRect(0, h2 - t, w, h2); // day and steps - if (settings.color == 'Blue' || settings.color == 'Red') - g.setColor('#fff'); // white on blue or red best contrast - else - g.setColor('#000'); // otherwise black regardless of theme - + g.setColor(theme.day); g.setFontLECO1976Regular22(); g.setFontAlign(0, -1); - g.drawString(da[0].toUpperCase(), w/4, ha); // day of week - g.drawString(getSteps(), 3*w/4, ha); + g.drawString(dayOfWeek, w/4, ha); + g.drawString(steps, 3*w/4, ha); // time // white on red for battery warning - g.setColor(!batteryWarning ? g.theme.bg : '#f00'); + g.setColor(!batteryWarning ? theme.bg : '#f00'); g.fillRect(0, h2, w, h3); g.setFontLECO1976Regular42(); g.setFontAlign(0, -1); - g.setColor(!batteryWarning ? g.theme.fg : '#fff'); - g.drawString(timeStr, w/2, h2 + 8); + g.setColor(!batteryWarning ? theme.fg : '#fff'); + g.drawString(time, w/2, h2 + 8); // contrast bar - g.setColor(g.theme.fg); + g.setColor(theme.fg); g.fillRect(0, h3, w, h3 + t); // the bottom @@ -78,16 +78,18 @@ function draw() { g.setColor(settings.bg); g.drawImage(img, w/2 + ((w/2) - 64)/2, 1, { scale: 1 }); - drawCalendar(((w/2) - 42)/2, 14, 42, 4, da[2]); + drawCalendar(((w/2) - 42)/2, 14, 42, 4, dayOfMonth); + + drawLock(); } // at x,y width:wi thicknes:th function drawCalendar(x,y,wi,th,str) { - g.setColor(g.theme.fg); + g.setColor(theme.fg); g.fillRect(x, y, x + wi, y + wi); - g.setColor(g.theme.bg); + g.setColor(theme.bg); g.fillRect(x + th, y + th, x + wi - th, y + wi - th); - g.setColor(g.theme.fg); + g.setColor(theme.fg); let hook_t = 6; // first calendar hook, one third in @@ -100,22 +102,51 @@ function drawCalendar(x,y,wi,th,str) { g.drawString(str, x + wi/2, y + wi/2 + th); } -function getSteps() { - if (WIDGETS.wpedom !== undefined) { - return WIDGETS.wpedom.getSteps(); +function loadThemeColors() { + theme = {fg: g.theme.fg, bg: g.theme.bg, day: g.toColor(0,0,0)}; + if (settings.theme === "Dark") { + theme.fg = g.toColor(1,1,1); + theme.bg = g.toColor(0,0,0); + } else if (settings.theme === "Light") { + theme.fg = g.toColor(0,0,0); + theme.bg = g.toColor(1,1,1); } - return '????'; + // day and steps + if (settings.color == 'Blue' || settings.color == 'Red') + theme.day = g.toColor(1,1,1); // white on blue or red best contrast } +function drawLock(){ + if (settings.showlock) { + if (Bangle.isLocked()){ + g.setColor(theme.day); + g.setBgColor(settings.bg); + g.drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), 1, 4); + } else { + g.setColor(settings.bg); + g.fillRect(0, 0, 20, 20); + } + } +} + +Bangle.on('lock', function(on) { + drawLock(); +}); + g.clear(); Bangle.loadWidgets(); -/* - * we are not drawing the widgets as we are taking over the whole screen - * so we will blank out the draw() functions of each widget and change the - * area to the top bar doesn't get cleared. - */ -for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} + +// We are not drawing the widgets as we are taking over the whole screen +// so we will blank out the draw() functions of each widget and change the +// area to the top bar doesn't get cleared. +for (let wd of WIDGETS) { + wd.draw=()=>{}; + wd.area=""; +} + loadSettings(); +loadThemeColors(); setInterval(draw, 15000); // refresh every 15s draw(); + Bangle.setUI("clock"); diff --git a/apps/pebble/pebble.settings.js b/apps/pebble/pebble.settings.js index ff408907d..f1c065db4 100644 --- a/apps/pebble/pebble.settings.js +++ b/apps/pebble/pebble.settings.js @@ -1,30 +1,33 @@ (function(back) { const SETTINGS_FILE = "pebble.json"; - // initialize with default settings... - let s = {'bg': '#0f0', 'color': 'Green'} + // TODO Only the color/theme indices should be written in the settings file so the labels can be translated + + // Initialize with default settings... + let s = {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false} // ...and overwrite them with any saved values // This way saved values are preserved if a new version adds more settings - const storage = require('Storage') + const storage = require('Storage'); let settings = storage.readJSON(SETTINGS_FILE, 1) || s; - const saved = settings || {} + const saved = settings || {}; for (const key in saved) { s[key] = saved[key] } function save() { - settings = s - storage.write(SETTINGS_FILE, settings) + settings = s; + storage.write(SETTINGS_FILE, settings); } var color_options = ['Green','Orange','Cyan','Purple','Red','Blue']; var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f']; + var theme_options = ['System', 'Light', 'Dark']; E.showMenu({ '': { 'title': 'Pebble Clock' }, - '< Back': back, - 'Colour': { + /*LANG*/'< Back': back, + /*LANG*/'Colour': { value: 0 | color_options.indexOf(s.color), min: 0, max: 5, format: v => color_options[v], @@ -32,7 +35,24 @@ s.color = color_options[v]; s.bg = bg_code[v]; save(); - }, - } + } + }, + /*LANG*/'Theme': { + value: 0 | theme_options.indexOf(s.theme), + min: 0, max: theme_options.length - 1, + format: v => theme_options[v], + onchange: v => { + s.theme = theme_options[v]; + save(); + } + }, + /*LANG*/'Show Lock': { + value: settings.showlock, + format: () => (settings.showlock ? /*LANG*/'Yes' : /*LANG*/'No'), + onchange: () => { + settings.showlock = !settings.showlock; + save(); + } + }, }); -}) +}); diff --git a/apps/pie/app.js b/apps/pie/app.js index 69b67d3bd..74f4b4575 100644 --- a/apps/pie/app.js +++ b/apps/pie/app.js @@ -11,7 +11,7 @@ function scrollX(){ gfx.clearRect(0,gfx.getHeight()*(1/4),gfx.getWidth(),0); gfx.scroll(0,gfx.getHeight()/4); score++; - if(typeof(m) != undefined && score>0){ + if(typeof m !== 'undefined' && score>0){ clearInterval(m); m = setInterval(scrollY,Math.abs(100/score+15-0.1*score));} gfx.setColor(1,1,1); diff --git a/apps/pipboy/metadata.json b/apps/pipboy/metadata.json index 34a6cb0ac..3dfb94c4a 100644 --- a/apps/pipboy/metadata.json +++ b/apps/pipboy/metadata.json @@ -7,6 +7,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS"], + "readme": "README.md", "allow_emulator": true, "screenshots": [{"url":"bangle1-pipboy-themed-clock-screenshot.png"}], "storage": [ diff --git a/apps/powermanager/ChangeLog b/apps/powermanager/ChangeLog new file mode 100644 index 000000000..8ccf678de --- /dev/null +++ b/apps/powermanager/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Allow forcing monotonic battery voltage/percentage diff --git a/apps/powermanager/README.md b/apps/powermanager/README.md new file mode 100644 index 000000000..434ec814e --- /dev/null +++ b/apps/powermanager/README.md @@ -0,0 +1,19 @@ +# Power manager + +Manages settings for charging. +Features: +* Warning threshold to be able to disconnect the charger at a given percentage +* Set the battery calibration offset. +* Force monotonic battery percentage or voltage + +## Internals + +Battery calibration offset is set by writing `batFullVoltage` in setting.json + +## TODO + +* Optionally keep battery history and show as graph + +## Creator + +[halemmerich](https://github.com/halemmerich) diff --git a/apps/powermanager/app.png b/apps/powermanager/app.png new file mode 100644 index 000000000..dc51aa01b Binary files /dev/null and b/apps/powermanager/app.png differ diff --git a/apps/powermanager/boot.js b/apps/powermanager/boot.js new file mode 100644 index 000000000..077e24413 --- /dev/null +++ b/apps/powermanager/boot.js @@ -0,0 +1,51 @@ +(function() { + var settings = Object.assign( + require('Storage').readJSON("powermanager.default.json", true) || {}, + require('Storage').readJSON("powermanager.json", true) || {} + ); + + if (settings.warnEnabled){ + print("Charge warning enabled"); + var chargingInterval; + + function handleCharging(charging){ + if (charging){ + if (chargingInterval) clearInterval(chargingInterval); + chargingInterval = setInterval(()=>{ + if (E.getBattery() > settings.warn){ + Bangle.buzz(1000); + } + }, 10000); + } + if (chargingInterval && !charging){ + clearInterval(chargingInterval); + chargingInterval = undefined; + } + } + + Bangle.on("charging",handleCharging); + handleCharging(Bangle.isCharging()); + } + + if (settings.forceMonoPercentage){ + var p = (E.getBattery()+E.getBattery()+E.getBattery()+E.getBattery())/4; + var op = E.getBattery; + E.getBattery = function() { + var current = Math.round((op()+op()+op()+op())/4); + if (Bangle.isCharging() && current > p) p = current; + if (!Bangle.isCharging() && current < p) p = current; + return p; + }; + } + + if (settings.forceMonoVoltage){ + var v = (NRF.getBattery()+NRF.getBattery()+NRF.getBattery()+NRF.getBattery())/4; + var ov = NRF.getBattery; + NRF.getBattery = function() { + var current = (ov()+ov()+ov()+ov())/4; + if (Bangle.isCharging() && current > v) v = current; + if (!Bangle.isCharging() && current < v) v = current; + return v; + }; + } +})(); diff --git a/apps/powermanager/default.json b/apps/powermanager/default.json new file mode 100644 index 000000000..6c929dc38 --- /dev/null +++ b/apps/powermanager/default.json @@ -0,0 +1,6 @@ +{ + "warnEnabled": false, + "warn": 96, + "forceMonoVoltage": false, + "forceMonoPercentage": false +} diff --git a/apps/powermanager/metadata.json b/apps/powermanager/metadata.json new file mode 100644 index 000000000..2bb531099 --- /dev/null +++ b/apps/powermanager/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "powermanager", + "name": "Power Manager", + "shortName": "Power Manager", + "version": "0.02", + "description": "Allow configuration of warnings and thresholds for battery charging and display.", + "icon": "app.png", + "type": "bootloader", + "tags": "tool", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"powermanager.boot.js","url":"boot.js"}, + {"name":"powermanager.settings.js","url":"settings.js"}, + {"name":"powermanager.default.json","url":"default.json"} + ] +} diff --git a/apps/powermanager/settings.js b/apps/powermanager/settings.js new file mode 100644 index 000000000..8af873e5f --- /dev/null +++ b/apps/powermanager/settings.js @@ -0,0 +1,139 @@ +(function(back) { + var systemsettings = require("Storage").readJSON("setting.json") || {}; + + function writeSettings(key, value) { + var s = require('Storage').readJSON(FILE, true) || {}; + s[key] = value; + require('Storage').writeJSON(FILE, s); + readSettings(); + } + + function readSettings() { + settings = Object.assign( + require('Storage').readJSON("powermanager.default.json", true) || {}, + require('Storage').readJSON(FILE, true) || {} + ); + } + + var FILE = "powermanager.json"; + var settings; + readSettings(); + + var mainmenu = { + '': { + 'title': 'Power Manager' + }, + '< Back': back, + 'Monotonic percentage': { + value: !!settings.forceMonoPercentage, + format: v => settings.forceMonoPercentage ? "On" : "Off", + onchange: v => { + writeSettings("forceMonoPercentage", v); + } + }, + 'Monotonic voltage': { + value: !!settings.forceMonoVoltage, + format: v => settings.forceMonoVoltage ? "On" : "Off", + onchange: v => { + writeSettings("forceMonoVoltage", v); + } + }, + 'Charge warning': function() { + E.showMenu(submenu_chargewarn); + }, + 'Calibrate': function() { + E.showMenu(submenu_calibrate); + } + }; + + + function roundToDigits(number, stepsize) { + return Math.round(number / stepsize) * stepsize; + } + + function getCurrentVoltageDirect() { + return (analogRead(D3) + analogRead(D3) + analogRead(D3) + analogRead(D3)) / 4; + } + + var stepsize = 0.0002; + var full = 0.32; + + function getInitialCalibrationOffset() { + return roundToDigits(systemsettings.batFullVoltage - full, stepsize) || 0; + } + + + var submenu_calibrate = { + '': { + title: "Calibrate" + }, + '< Back': function() { + E.showMenu(mainmenu); + }, + 'Offset': { + min: -0.05, + max: 0.05, + step: stepsize, + value: getInitialCalibrationOffset(), + format: v => roundToDigits(v, stepsize).toFixed((""+stepsize).length - 2), + onchange: v => { + print(typeof v); + systemsettings.batFullVoltage = v + full; + require("Storage").writeJSON("setting.json", systemsettings); + } + }, + 'Auto': function() { + var newVoltage = getCurrentVoltageDirect(); + E.showAlert("Please charge fully before auto setting").then(() => { + E.showPrompt("Set current charge as full").then((r) => { + if (r) { + systemsettings.batFullVoltage = newVoltage; + require("Storage").writeJSON("setting.json", systemsettings); + //reset value shown in menu to the newly set one + submenu_calibrate.Offset.value = getInitialCalibrationOffset(); + E.showMenu(mainmenu); + } + }); + }); + }, + 'Clear': function() { + E.showPrompt("Clear charging offset?").then((r) => { + if (r) { + delete systemsettings.batFullVoltage; + require("Storage").writeJSON("setting.json", systemsettings); + //reset value shown in menu to the newly set one + submenu_calibrate.Offset.value = getInitialCalibrationOffset(); + E.showMenu(mainmenu); + } + }); + } + }; + + var submenu_chargewarn = { + '': { + title: "Charge warning" + }, + '< Back': function() { + E.showMenu(mainmenu); + }, + 'Enabled': { + value: !!settings.warnEnabled, + format: v => settings.warnEnabled ? "On" : "Off", + onchange: v => { + writeSettings("warnEnabled", v); + } + }, + 'Percentage': { + min: 80, + max: 100, + step: 2, + value: settings.warn, + format: v => v + "%", + onchange: v => { + writeSettings("warn", v); + } + } + }; + + E.showMenu(mainmenu); +}) diff --git a/apps/promenu/metadata.json b/apps/promenu/metadata.json index d70f36b0a..e0124467a 100644 --- a/apps/promenu/metadata.json +++ b/apps/promenu/metadata.json @@ -4,9 +4,10 @@ "version": "0.02", "description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.", "icon": "icon.png", - "type": "boot", + "type": "bootloader", "tags": "system", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "screenshots": [{"url":"pro-menu-screenshot.png"}], "storage": [ {"name":"promenu.boot.js","url":"boot.js","supports": ["BANGLEJS"]}, diff --git a/apps/qalarm/metadata.json b/apps/qalarm/metadata.json index 326ba33a7..2039af4bf 100644 --- a/apps/qalarm/metadata.json +++ b/apps/qalarm/metadata.json @@ -4,7 +4,7 @@ "shortName": "Q Alarm", "icon": "app.png", "version": "0.04", - "description": "Alarm and timer app with days of week and 'hard' option.", + "description": "[Not recommended - use 'Alarm & Timer' app] Alarm and timer app with days of week and 'hard' option.", "tags": "tool,alarm,widget", "supports": ["BANGLEJS", "BANGLEJS2"], "storage": [ diff --git a/apps/qmsched/ChangeLog b/apps/qmsched/ChangeLog index c868b6668..94fcffe1a 100644 --- a/apps/qmsched/ChangeLog +++ b/apps/qmsched/ChangeLog @@ -5,4 +5,5 @@ 0.05: Avoid immediately redrawing widgets on load 0.06: Fix: don't try to redraw widget when widgets not loaded 0.07: Option to switch theme - Changed time selection to 5-minute intervals \ No newline at end of file + Changed time selection to 5-minute intervals +0.08: Support new Bangle.js 2 menu \ No newline at end of file diff --git a/apps/qmsched/app.js b/apps/qmsched/app.js index e05eff6a2..8cd0fa8d9 100644 --- a/apps/qmsched/app.js +++ b/apps/qmsched/app.js @@ -1,8 +1,8 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -const modeNames = ["Off", "Alarms", "Silent"]; - +const modeNames = [/*LANG*/"Off", /*LANG*/"Alarms", /*LANG*/"Silent"]; +const B2 = process.env.HWVERSION===2; // load global settings let bSettings = require('Storage').readJSON('setting.json',true)||{}; let current = 0|bSettings.quiet; @@ -109,34 +109,26 @@ function setAppQuietMode(mode) { let m; function showMainMenu() { - let menu = { - "": {"title": "Quiet Mode"}, - "< Exit": () => load() - }; - // "Current Mode""Silent" won't fit on Bangle.js 2 - menu["Current"+((process.env.HWVERSION===2) ? "" : " Mode")] = { + let menu = {"": {"title": /*LANG*/"Quiet Mode"},}; + menu[B2 ? /*LANG*/"< Back" : /*LANG*/"< Exit"] = () => {load();}; + menu[/*LANG*/"Current Mode"] = { value: current, min:0, max:2, wrap: true, - format: () => modeNames[current], + format: v => modeNames[v], onchange: require("qmsched").setMode, // library calls setAppMode(), which updates `current` }; scheds.sort((a, b) => (a.hr-b.hr)); scheds.forEach((sched, idx) => { - menu[formatTime(sched.hr)] = { - format: () => modeNames[sched.mode], // abuse format to right-align text - onchange: () => { - m.draw = ()=> {}; // prevent redraw of main menu over edit menu (needed because we abuse format/onchange) - showEditMenu(idx); - } - }; + menu[formatTime(sched.hr)] = () => { showEditMenu(idx); }; + menu[formatTime(sched.hr)].format = () => modeNames[sched.mode]+' >'; // this does nothing :-( }); - menu["Add Schedule"] = () => showEditMenu(-1); - menu["Switch Theme"] = { + menu[/*LANG*/"Add Schedule"] = () => showEditMenu(-1); + menu[/*LANG*/"Switch Theme"] = { value: !!get("switchTheme"), format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", onchange: v => v ? set("switchTheme", v) : unset("switchTheme"), }; - menu["LCD Settings"] = () => showOptionsMenu(); + menu[/*LANG*/"LCD Settings"] = () => showOptionsMenu(); m = E.showMenu(menu); } @@ -150,25 +142,23 @@ function showEditMenu(index) { mins = Math.round((s.hr-hrs)*60); mode = s.mode; } - const menu = { - "": {"title": (isNew ? "Add" : "Edit")+" Schedule"}, - "< Cancel": () => showMainMenu(), - "Hours": { - value: hrs, - min:0, max:23, wrap:true, - onchange: v => {hrs = v;}, - }, - "Minutes": { - value: mins, - min:0, max:55, step:5, wrap:true, - onchange: v => {mins = v;}, - }, - "Switch to": { - value: mode, - min:0, max:2, wrap:true, - format: v => modeNames[v], - onchange: v => {mode = v;}, - }, + let menu = {"": {"title": (isNew ? /*LANG*/"Add Schedule" : /*LANG*/"Edit Schedule")}}; + menu[B2 ? /*LANG*/"< Back" : /*LANG*/"< Cancel"] = () => showMainMenu(); + menu[/*LANG*/"Hours"] = { + value: hrs, + min:0, max:23, wrap:true, + onchange: v => {hrs = v;}, + }; + menu[/*LANG*/"Minutes"] = { + value: mins, + min:0, max:55, step:5, wrap:true, + onchange: v => {mins = v;}, + }; + menu[/*LANG*/"Switch to"] = { + value: mode, + min:0, max:2, wrap:true, + format: v => modeNames[v], + onchange: v => {mode = v;}, }; function getSched() { return { @@ -176,7 +166,7 @@ function showEditMenu(index) { mode: mode, }; } - menu["> Save"] = function() { + menu[B2 ? /*LANG*/"Save" : /*LANG*/"> Save"] = function() { if (isNew) { scheds.push(getSched()); } else { @@ -186,7 +176,7 @@ function showEditMenu(index) { showMainMenu(); }; if (!isNew) { - menu["> Delete"] = function() { + menu[B2 ? /*LANG*/"Delete" : /*LANG*/"> Delete"] = function() { scheds.splice(index, 1); save(); showMainMenu(); @@ -196,7 +186,7 @@ function showEditMenu(index) { } function showOptionsMenu() { - const disabledFormat = v => v ? "Off" : "-"; + const disabledFormat = v => v ? /*LANG*/"Off" : "-"; function toggle(option) { // we disable wakeOn* events by setting them to `false` in options // not disabled = not present in options at all @@ -209,9 +199,9 @@ function showOptionsMenu() { } let resetTimeout; const oMenu = { - "": {"title": "LCD Settings"}, - "< Back": () => showMainMenu(), - "LCD Brightness": { + "": {"title": /*LANG*/"LCD Settings"}, + /*LANG*/"< Back": () => showMainMenu(), + /*LANG*/"LCD Brightness": { value: get("brightness", 0), min: 0, // 0 = use default max: 1, @@ -233,7 +223,7 @@ function showOptionsMenu() { } }, }, - "LCD Timeout": { + /*LANG*/"LCD Timeout": { value: get("timeout", 0), min: 0, // 0 = use default (no constant on for quiet mode) max: 60, @@ -246,17 +236,17 @@ function showOptionsMenu() { }, // we disable wakeOn* events by overwriting them as false in options // not disabled = not present in options at all - "Wake on FaceUp": { + /*LANG*/"Wake on FaceUp": { value: "wakeOnFaceUp" in options, format: disabledFormat, onchange: () => {toggle("wakeOnFaceUp");}, }, - "Wake on Touch": { + /*LANG*/"Wake on Touch": { value: "wakeOnTouch" in options, format: disabledFormat, onchange: () => {toggle("wakeOnTouch");}, }, - "Wake on Twist": { + /*LANG*/"Wake on Twist": { value: "wakeOnTwist" in options, format: disabledFormat, onchange: () => {toggle("wakeOnTwist");}, diff --git a/apps/qmsched/icon.js b/apps/qmsched/icon.js index c0f4e2b66..53aeb55af 100644 --- a/apps/qmsched/icon.js +++ b/apps/qmsched/icon.js @@ -1,2 +1 @@ -// https://icons8.com/icon/19324/no-reminders -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AElksF1wwtF4YwO0WiGFguBGFovfGB3MAAgwnFooxfGBAuJGEguLGEV/F5owh0YvpGH4vhGCQvd0YwQF7vMGCAveGCAvfGB4vgGBwvhGBouhGFLkIGEouIGEwvKGBguiGEQuNGEHN5owa5ouQ53P5/O5wyOGA3NDAIbBLyAUCAAQzCNBQwF0gVDXiQoBGQgAEEIILE0iSJdiozCFQw1FGBJgSABSVIeg7wQGSDDMFyQ0VCQQwdAAWcAAwPHGD4vPGD+iAAwRJGEgRLGEQRNeTwARF1wA/AH4AX")) \ No newline at end of file +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AElksF1wwtF4YwO0WiGFguBGFovfGB3MAAgwnFooxfGBAuJGEguLGEV/F5owh0YvpGH4vhGCQvd0YwQF7vMGCAveGCAvfGB4vgGBwvhGBouhGFLkIGEouIGEwvKGBguiGEQuNGEHN5owa5ouQ53P5/O5wyOGA3NDAIbBLyAUCAAQzCNBQwF0gVDXiQoBGQgAEEIILE0iSJdiozCFQw1FGBJgSABSVIeg7wQGSDDMFyQ0VCQQwdAAWcAAwPHGD4vPGD+iAAwRJGEgRLGEQRNeTwARF1wA/AH4AX")) diff --git a/apps/qmsched/metadata.json b/apps/qmsched/metadata.json index daeaad624..326a8fc4f 100644 --- a/apps/qmsched/metadata.json +++ b/apps/qmsched/metadata.json @@ -2,7 +2,7 @@ "id": "qmsched", "name": "Quiet Mode Schedule and Widget", "shortName": "Quiet Mode", - "version": "0.07", + "version": "0.08", "description": "Automatically turn Quiet Mode on or off at set times, change theme and LCD options while Quiet Mode is active.", "icon": "app.png", "screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"}, diff --git a/apps/quicklaunch/ChangeLog b/apps/quicklaunch/ChangeLog new file mode 100644 index 000000000..ae1d4a848 --- /dev/null +++ b/apps/quicklaunch/ChangeLog @@ -0,0 +1,2 @@ +0.01: Initial version +0.02: Moved settings from launcher to settings->apps menu diff --git a/apps/quicklaunch/app.png b/apps/quicklaunch/app.png new file mode 100644 index 000000000..3d1d0fdd2 Binary files /dev/null and b/apps/quicklaunch/app.png differ diff --git a/apps/quicklaunch/boot.js b/apps/quicklaunch/boot.js new file mode 100644 index 000000000..3670c4776 --- /dev/null +++ b/apps/quicklaunch/boot.js @@ -0,0 +1,67 @@ +(function() { + var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {}); + + //list all sources + var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{src:a.src};}); + + //populate empty app list + + if (!settings.leftapp) { + settings["leftapp"] = {"name":"(none)"}; + require("Storage").write("quicklaunch.json",settings); + } + if (!settings.rightapp) { + settings["rightapp"] = {"name":"(none)"}; + require("Storage").write("quicklaunch.json",settings); + } + if (!settings.upapp) { + settings["upapp"] = {"name":"(none)"}; + require("Storage").write("quicklaunch.json",settings); + } + if (!settings.downapp) { + settings["downapp"] = {"name":"(none)"}; + require("Storage").write("quicklaunch.json",settings); + } + if (!settings.tapapp) { + settings["tapapp"] = {"name":"(none)"}; + require("Storage").write("quicklaunch.json",settings); + } + + //activate on clock faces + var sui = Bangle.setUI; + Bangle.setUI = function(mode, cb) { + sui(mode,cb); + if(!mode) return; + if ("object"==typeof mode) mode = mode.mode; + if (!mode.startsWith("clock")) return; + + function tap() { + //tap, check if source exists, launch + if ((settings.tapapp.src) && apps.some(e => e.src === settings.tapapp.src)) load (settings.tapapp.src); + } + + let drag; + let e; + + Bangle.on("touch",tap); + Bangle.on("drag", e => { + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + //horizontal swipes, check if source exists, launch + if (Math.abs(dx)>Math.abs(dy)+10) { + if ((settings.leftapp.src) && apps.some(e => e.src === settings.leftapp.src) && dx<0) load(settings.leftapp.src); + if ((settings.rightapp.src) && apps.some(e => e.src === settings.rightapp.src) && dx>0) load(settings.rightapp.src); + } + //vertical swipes, check if source exists, launch + else if (Math.abs(dy)>Math.abs(dx)+10) { + if ((settings.upapp.src) && apps.some(e => e.src === settings.upapp.src) && dy<0) load(settings.upapp.src); + if ((settings.downapp.src) && apps.some(e => e.src === settings.downapp.src) && dy>0) load(settings.downapp.src); + } + } + }); + + }; +})(); diff --git a/apps/quicklaunch/metadata.json b/apps/quicklaunch/metadata.json new file mode 100644 index 000000000..49eafdd35 --- /dev/null +++ b/apps/quicklaunch/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "quicklaunch", + "name": "Quick Launch", + "icon": "app.png", + "version":"0.02", + "description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice. Configurations can be accessed through Settings->Apps.", + "type": "bootloader", + "tags": "tools, system", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"quicklaunch.settings.js","url":"settings.js"}, + {"name":"quicklaunch.boot.js","url":"boot.js"} + ], + "data": [{"name":"quicklaunch.json"}] +} diff --git a/apps/quicklaunch/settings.js b/apps/quicklaunch/settings.js new file mode 100644 index 000000000..ac4cc5805 --- /dev/null +++ b/apps/quicklaunch/settings.js @@ -0,0 +1,122 @@ +(function(back) { +var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {}); + +var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{name:a.name,type:a.type,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="launch" || app.type=="clock" || !app.type)); + +apps.sort((a,b)=>{ + var n=(0|a.sortorder)-(0|b.sortorder); + if (n) return n; // do sortorder first + if (a.nameb.name) return 1; + return 0; +}); + +function save(key, value) { + settings[key] = value; + require("Storage").write("quicklaunch.json",settings); +} + +// Quick Launch menu +function showMainMenu() { + var mainmenu = { + "" : { "title" : "Quick Launch" }, + "< Back" : ()=>{load();} + }; + + //List all selected apps + mainmenu["Left: "+settings.leftapp.name] = function() { E.showMenu(leftmenu); }; + mainmenu["Right: "+settings.rightapp.name] = function() { E.showMenu(rightmenu); }; + mainmenu["Up: "+settings.upapp.name] = function() { E.showMenu(upmenu); }; + mainmenu["Down: "+settings.downapp.name] = function() { E.showMenu(downmenu); }; + mainmenu["Tap: "+settings.tapapp.name] = function() { E.showMenu(tapmenu); }; + + return E.showMenu(mainmenu); +} + +//Left swipe menu +var leftmenu = { + "" : { "title" : "Left Swipe" }, + "< Back" : showMainMenu +}; + +leftmenu["(none)"] = function() { + save("leftapp", {"name":"(none)"}); + showMainMenu(); +}; +apps.forEach((a)=>{ + leftmenu[a.name] = function() { + save("leftapp", a); + showMainMenu(); + }; +}); + +//Right swipe menu +var rightmenu = { + "" : { "title" : "Right Swipe" }, + "< Back" : showMainMenu +}; + +rightmenu["(none)"] = function() { + save("rightapp", {"name":"(none)"}); + showMainMenu(); +}; +apps.forEach((a)=>{ + rightmenu[a.name] = function() { + save("rightapp", a); + showMainMenu(); + }; +}); + +//Up swipe menu +var upmenu = { + "" : { "title" : "Up Swipe" }, + "< Back" : showMainMenu +}; + +upmenu["(none)"] = function() { + save("upapp", {"name":"(none)"}); + showMainMenu(); +}; +apps.forEach((a)=>{ + upmenu[a.name] = function() { + save("upapp", a); + showMainMenu(); + }; +}); + +//Down swipe menu +var downmenu = { + "" : { "title" : "Down Swipe" }, + "< Back" : showMainMenu +}; + +downmenu["(none)"] = function() { + save("downapp", {"name":"(none)"}); + showMainMenu(); +}; +apps.forEach((a)=>{ + downmenu[a.name] = function() { + save("downapp", a); + showMainMenu(); + }; +}); + +//Tap menu +var tapmenu = { + "" : { "title" : "Tap" }, + "< Back" : showMainMenu +}; + +tapmenu["(none)"] = function() { + save("tapapp", {"name":"(none)"}); + showMainMenu(); +}; +apps.forEach((a)=>{ + tapmenu[a.name] = function() { + save("tapapp", a); + showMainMenu(); + }; +}); + +showMainMenu(); +}); \ No newline at end of file diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog index b9c26b4e3..b80dfef94 100644 --- a/apps/rebble/ChangeLog +++ b/apps/rebble/ChangeLog @@ -2,3 +2,4 @@ 0.02: Fix typo to Purple 0.03: Added dependancy on Pedometer Widget 0.04: Fixed icon and png to 48x48 pixels +0.05: added charging icon \ No newline at end of file diff --git a/apps/rebble/metadata.json b/apps/rebble/metadata.json index 212a7b5b3..b26fb6a27 100644 --- a/apps/rebble/metadata.json +++ b/apps/rebble/metadata.json @@ -2,7 +2,7 @@ "id": "rebble", "name": "Rebble Clock", "shortName": "Rebble", - "version": "0.04", + "version": "0.05", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "readme": "README.md", "icon": "rebble.png", diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js index d186ea8ec..7c7d57939 100644 --- a/apps/rebble/rebble.app.js +++ b/apps/rebble/rebble.app.js @@ -204,6 +204,14 @@ function drawBattery(x,y,wi,hi) { g.setColor(g.theme.fg); g.fillRect(x+wi-3,y+2+(((hi - 1)/2)-1),x+wi-2,y+2+(((hi - 1)/2)-1)+4); // contact g.fillRect(x+3, y+5, x +4 + E.getBattery()*(wi-12)/100, y+hi-1); // the level + + if( Bangle.isCharging() ) + { + g.setBgColor(settings.bg); + image = ()=> { return require("heatshrink").decompress(atob("j8OwMB/4AD94DC44DCwP//n/gH//EOgE/+AdBh/gAYMH4EAvkDAYP/+/AFAX+FgfzGAnAA=="));} + g.drawImage(image(),x+3,y+4); + } + } function getSteps() { @@ -270,3 +278,14 @@ for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} loadSettings(); loadLocation(); draw(); // queues the next draw for a minutes time +Bangle.on('charging', function(charging) { + //redraw the sidebar ( with the battery ) + switch(sideBar) { + case 0: + drawSideBar1(); + break; + case 1: + drawSideBar2(); + break; + } +}); \ No newline at end of file diff --git a/apps/recorder/ChangeLog b/apps/recorder/ChangeLog index 963944144..140567068 100644 --- a/apps/recorder/ChangeLog +++ b/apps/recorder/ChangeLog @@ -16,3 +16,7 @@ 0.10: Fix broken recorder settings (when launched from settings app) 0.11: Fix KML and GPX export when there is no GPS data 0.12: Fix 'Back' label positioning on track/graph display, make translateable +0.13: Fix for when widget is used before app +0.14: Remove unneeded variable assignment +0.15: Show distance more accurately in conjunction with new locale app (fix #1523) +0.16: Ability to append to existing track (fix #1712) diff --git a/apps/recorder/app-settings.json b/apps/recorder/app-settings.json index 4a3117a17..7410af213 100644 --- a/apps/recorder/app-settings.json +++ b/apps/recorder/app-settings.json @@ -1,6 +1,6 @@ { "recording":false, - "file":"record.log0.csv", + "file":"recorder.log0.csv", "period":10, "record" : ["gps"] } diff --git a/apps/recorder/app.js b/apps/recorder/app.js index d900c12c1..fb3dfab4f 100644 --- a/apps/recorder/app.js +++ b/apps/recorder/app.js @@ -31,7 +31,12 @@ function updateSettings() { } function getTrackNumber(filename) { - return parseInt(filename.match(/^recorder\.log(.*)\.csv$/)[1]||0); + var trackNum = 0; + var matches = filename.match(/^recorder\.log(.*)\.csv$/); + if (matches) { + trackNum = parseInt(matches[1]||0); + } + return trackNum; } function showMainMenu() { @@ -214,7 +219,7 @@ function viewTrack(filename, info) { f.erase(); viewTracks(); } else - viewTrack(n, info); + viewTrack(filename, info); }); }; menu['< Back'] = () => { viewTracks(); }; @@ -302,7 +307,7 @@ function viewTrack(filename, info) { g.fillCircle(ox,oy,5); if (info.qOSTM) g.setColor("#000"); else g.setColor(g.theme.fg); - g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20); + g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20); g.setFont("6x8",2); g.setFontAlign(0,0,3); var isBTN3 = "BTN3" in global; diff --git a/apps/recorder/metadata.json b/apps/recorder/metadata.json index 09873dada..3d50bbd14 100644 --- a/apps/recorder/metadata.json +++ b/apps/recorder/metadata.json @@ -2,7 +2,7 @@ "id": "recorder", "name": "Recorder", "shortName": "Recorder", - "version": "0.12", + "version": "0.16", "description": "Record GPS position, heart rate and more in the background, then download to your PC.", "icon": "app.png", "tags": "tool,outdoors,gps,widget", @@ -15,5 +15,5 @@ {"name":"recorder.wid.js","url":"widget.js"}, {"name":"recorder.settings.js","url":"settings.js"} ], - "data": [{"name":"recorder.json"},{"wildcard":"recorder.log?.csv","storageFile":true}] + "data": [{"name":"recorder.json","url":"app-settings.json"},{"wildcard":"recorder.log?.csv","storageFile":true}] } diff --git a/apps/recorder/widget.js b/apps/recorder/widget.js index e10c99c0c..be714c19d 100644 --- a/apps/recorder/widget.js +++ b/apps/recorder/widget.js @@ -11,7 +11,7 @@ settings.recording = false; return settings; } - + function updateSettings(settings) { require("Storage").writeJSON("recorder.json", settings); if (WIDGETS["recorder"]) WIDGETS["recorder"].reload(); @@ -142,7 +142,7 @@ }; } } - + /* eg. foobar.recorder.js (function(recorders) { recorders.foobar = { @@ -193,7 +193,7 @@ settings.record.forEach(r => { var recorder = recorders[r]; if (!recorder) { - console.log("Recorder for "+E.toJS(r)+"+not found"); + console.log(/*LANG*/"Recorder for "+E.toJS(r)+/*LANG*/"+not found"); return; } var activeRecorder = recorder(); @@ -231,9 +231,11 @@ },getRecorders:getRecorders,reload:function() { reload(); Bangle.drawWidgets(); // relayout all widgets - },setRecording:function(isOn) { + },setRecording:function(isOn, forceAppend) { var settings = loadSettings(); - if (isOn && !settings.recording && require("Storage").list(settings.file).length){ + if (isOn && !settings.recording && !settings.file) { + settings.file = "recorder.log0.csv"; + } else if (isOn && !forceAppend && !settings.recording && require("Storage").list(settings.file).length){ var logfiles=require("Storage").list(/recorder.log.*/); var maxNumber=0; for (var c of logfiles){ @@ -244,18 +246,20 @@ newFileName="recorder.log" + (maxNumber + 1) + ".csv"; updateSettings(settings); } - var buttons={Yes:"yes",No:"no"}; - if (newFileName) buttons["New"] = "new"; - var prompt = E.showPrompt("Overwrite\nLog " + settings.file.match(/\d+/)[0] + "?",{title:"Recorder",buttons:buttons}).then(selection=>{ - if (selection=="no") return false; // just cancel - if (selection=="yes") require("Storage").open(settings.file,"r").erase(); - if (selection=="new"){ + var buttons={/*LANG*/"Yes":"overwrite",/*LANG*/"No":"cancel"}; + if (newFileName) buttons[/*LANG*/"New"] = "new"; + buttons[/*LANG*/"Append"] = "append"; + return E.showPrompt(/*LANG*/"Overwrite\nLog " + settings.file.match(/\d+/)[0] + "?",{title:/*LANG*/"Recorder",buttons:buttons}).then(selection=>{ + if (selection==="cancel") return false; // just cancel + if (selection==="overwrite") + require("Storage").open(settings.file,"r").erase(); + if (selection==="new"){ settings.file = newFileName; updateSettings(settings); } - return WIDGETS["recorder"].setRecording(1); + // if (selection==="append") // we do nothing - all is fine + return WIDGETS["recorder"].setRecording(1,true/*force append*/); }); - return prompt; } settings.recording = isOn; updateSettings(settings); diff --git a/apps/red7game/ChangeLog b/apps/red7game/ChangeLog index 93dd81c5b..360b1a305 100644 --- a/apps/red7game/ChangeLog +++ b/apps/red7game/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial version of game 0.02: Fix mistake preventing game from ending in some cases. -0.03: Update help screen with more details. \ No newline at end of file +0.03: Update help screen with more details. +0.04: Update cards to draw rounded on newer firmware. Make sure in-game menu can't be pulled up during end of game. diff --git a/apps/red7game/metadata.json b/apps/red7game/metadata.json index c8f9e1aa1..15fec4d21 100644 --- a/apps/red7game/metadata.json +++ b/apps/red7game/metadata.json @@ -2,7 +2,7 @@ "name": "Red 7 Card Game", "shortName" : "Red 7", "icon": "icon.png", - "version":"0.03", + "version":"0.04", "description": "An implementation of the card game Red 7 for your watch. Play against the AI and be the last player still in the game to win!", "tags": "game", "supports":["BANGLEJS2"], diff --git a/apps/red7game/red7.js b/apps/red7game/red7.js index 901bc186f..2b22488b9 100644 --- a/apps/red7game/red7.js +++ b/apps/red7game/red7.js @@ -35,19 +35,27 @@ class Card { get clipRect() { return this.clippedRect; } + fillRect(x,y,x2,y2,r) { + //This function allows using new rounded corners on newer firmware + if(process.env.VERSION === "2v12" || process.env.VERSION === "2v11" || process.env.VERSION === "2v10") { + g.fillRect(x,y,x2,y2); + } else { + g.fillRect({x:x,y:y,x2:x2,y2:y2,r:r}); + } + } draw(x,y,outlined) { this.rect = {x1: x, x2: x+80, y1: y, y2: y+100}; this.clippedRect = {x1: x, x2: x+24, y1: y, y2: y+100}; var colorIndex = colors.indexOf(this.cardColor); var colorArr = colorsHex[colorIndex]; var colorRule = colorsRules[colorIndex]; - g.setColor(colorArr); - g.setBgColor(colorArr); - g.fillRect(x,y,x+80,y+100); if(outlined) { g.setColor(0,0,0); - g.drawRect(x,y,x+80,y+100); + this.fillRect(x-1,y-1,x+81,y+101,6); } + g.setColor(colorArr); + g.setBgColor(colorArr); + this.fillRect(x,y,x+80,y+100,6); g.setColor(255,255,255); g.setFont("Vector:40"); g.setFontAlign(0,0,0); @@ -61,21 +69,23 @@ class Card { drawBack(x,y,flipped) { this.rect = {x1: x, x2: x+80, y1: y, y2: y-100}; this.clippedRect = {x1: x, x2: x+24, y1: y, y2: y-100}; - g.setColor(255,255,255); g.setBgColor(0,0,0); if(flipped) { - g.fillRect(x,y,x+80,-100); g.setColor(0,0,0); - g.drawRect(x,y,x+80,-100); + this.fillRect(x-1,y+1,x+81,y-101,6); + g.setColor(255,255,255); + this.fillRect(x,y,x+80,y-100,6); g.setFontAlign(0,0,2); g.setColor(255,0,0); g.setBgColor(255,255,255); g.setFont("Vector:40"); //g.drawString(7,x+40,y-40,true); } else { - g.fillRect(x,y,x+80,y+100); g.setColor(0,0,0); - g.drawRect(x,y,x+80,y+100); + this.fillRect(x-1,y-1,x+81,y+101,6); + g.setColor(255,255,255); + this.fillRect(x,y,x+80,y+100,6); + g.setColor(0,0,0); g.setFontAlign(0,0,0); g.setColor(255,0,0); g.setBgColor(255,255,255); @@ -91,7 +101,7 @@ class Card { var colorRule = colorsRules[colorIndex]; g.setColor(colorArr); g.setBgColor(colorArr); - g.fillRect(x,y,x+110,y+45); + this.fillRect(x,y,x+110,y+45,6); g.setColor(255,255,255); g.setFontAlign(0,0,0); g.setFont("6x8:2"); @@ -104,7 +114,7 @@ class Card { var colorArr = colorsHex[colorIndex]; g.setColor(colorArr); g.setBgColor(colorArr); - g.fillRect(x,y,x+20,y+20); + this.fillRect(x,y,x+20,y+20,2); g.setFontAlign(0,0,0); g.setFont("6x8:2"); g.setColor(255,255,255); @@ -703,8 +713,8 @@ function drawScreenHelp() { } function drawGameOver(win) { + startedGame = false; E.showAlert(win ? "AI has given up. You Win!" : "You cannot play. AI wins.").then(function(){ - startedGame = false; undoStack = []; drawMainMenu(); }); diff --git a/apps/run/ChangeLog b/apps/run/ChangeLog index 0d61aa789..de070dbd8 100644 --- a/apps/run/ChangeLog +++ b/apps/run/ChangeLog @@ -6,3 +6,9 @@ 0.05: exstats updated so update 'distance' label is updated, option for 'speed' 0.06: Add option to record a run using the recorder app automatically 0.07: Fix crash if an odd number of active boxes are configured (fix #1473) +0.08: Added support for notifications from exstats. Support all stats from exstats +0.09: Fix broken start/stop if recording not enabled (fix #1561) +0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578) +0.11: Notifications fixes +0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11 +0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643) diff --git a/apps/run/README.md b/apps/run/README.md index 5b3bb635a..7f645b518 100644 --- a/apps/run/README.md +++ b/apps/run/README.md @@ -13,8 +13,9 @@ the red `STOP` in the bottom right turns to a green `RUN`. the GPS updates your position as it gets more satellites your position changes and the distance shown will increase, even if you are standing still. * `TIME` - the elapsed time for your run -* `PACE` - the number of minutes it takes you to run a kilometer **based on your run so far** -* `HEART` - Your heart rate +* `PACE` - the number of minutes it takes you to run a given distance, configured in settings (default 1km) **based on your run so far** +* `HEART (BPM)` - Your current heart rate +* `Max BPM` - Your maximum heart rate reached during the run * `STEPS` - Steps since you started exercising * `CADENCE` - Steps per second based on your step rate *over the last minute* * `GPS` - this is green if you have a GPS lock. GPS is turned on automatically @@ -24,9 +25,8 @@ so if you have no GPS lock you just need to wait. ## Recording Tracks -`Run` doesn't directly allow you to record your tracks at the moment. -However you can just install the `Recorder` app, turn recording on in -that, and then start the `Run` app. +When the `Recorder` app is installed, `Run` will automatically start and stop tracks +as needed, prompting you to overwrite or begin a new track if necessary. ## Settings @@ -35,13 +35,29 @@ Under `Settings` -> `App` -> `Run` you can change settings for this app. * `Record Run` (only displayed if `Recorder` app installed) should the Run app automatically record GPS/HRM/etc data every time you start a run? * `Pace` is the distance that pace should be shown over - 1km, 1 mile, 1/2 Marathon or 1 Marathon -* `Box 1/2/3/4/5/6` are what should be shown in each of the 6 boxes on the display. From the top left, down. - If you set it to `-` nothing will be displayed, so you can display only 4 boxes of information - if you wish by setting the last 2 boxes to `-`. +* `Boxes` leads to a submenu where you can configure what is shown in each of the 6 boxes on the display. + Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Max BPM", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence". + Any box set to "-" will display no information. + * Box 1 is the top left (defaults to "Distance") + * Box 2 is the top right (defaults to "Time") + * Box 3 is the middle left (defaults to "Pace (avg)") + * Box 4 is the middle right (defaults to "Heart (BPM)") + * Box 5 is the bottom left (defaults to "Steps") + * Box 6 is the bottom right (defaults to "Cadence") +* `Notifications` leads to a submenu where you can configure if the app will notify you after +your distance, steps, or time repeatedly pass your configured thresholds + * `Ntfy Dist`: The distance that you must pass before you are notified. Follows the `Pace` options + * "Off" (default), "1km", "1 mile", "1/2 Marathon", "1 Marathon" + * `Ntfy Steps`: The number of steps that must pass before you are notified. + * "Off" (default), 100, 500, 1000, 5000, 10000 + * `Ntfy Time`: The amount of time that must pass before you are notified. + * "Off" (default), "30 sec", "1 min", "2 min", "5 min", "10 min", "30 min", "1 hour" + * `Dist Pattern`: The vibration pattern to use to notify you about meeting your distance threshold + * `Step Pattern`: The vibration pattern to use to notify you about meeting your step threshold + * `Time Pattern`: The vibration pattern to use to notify you about meeting your time threshold ## TODO -* Allow this app to trigger the `Recorder` app on and off directly. * Keep a log of each run's stats (distance/steps/etc) ## Development diff --git a/apps/run/app.js b/apps/run/app.js index bc1d54de2..19dcd7e88 100644 --- a/apps/run/app.js +++ b/apps/run/app.js @@ -1,5 +1,5 @@ var ExStats = require("exstats"); -var B2 = process.env.HWVERSION==2; +var B2 = process.env.HWVERSION===2; var Layout = require("Layout"); var locale = require("locale"); var fontHeading = "6x8:2"; @@ -14,46 +14,75 @@ Bangle.drawWidgets(); // --------------------------- let settings = Object.assign({ - record : true, - B1 : "dist", - B2 : "time", - B3 : "pacea", - B4 : "bpm", - B5 : "step", - B6 : "caden", - paceLength : 1000 + record: true, + B1: "dist", + B2: "time", + B3: "pacea", + B4: "bpm", + B5: "step", + B6: "caden", + paceLength: 1000, + notify: { + dist: { + value: 0, + notifications: [], + }, + step: { + value: 0, + notifications: [], + }, + time: { + value: 0, + notifications: [], + }, + }, }, require("Storage").readJSON("run.json", 1) || {}); -var statIDs = [settings.B1,settings.B2,settings.B3,settings.B4,settings.B5,settings.B6].filter(s=>s!=""); +var statIDs = [settings.B1,settings.B2,settings.B3,settings.B4,settings.B5,settings.B6].filter(s=>s!==""); var exs = ExStats.getStats(statIDs, settings); // --------------------------- // Called to start/stop running function onStartStop() { var running = !exs.state.active; - if (running) { - exs.start(); - } else { - exs.stop(); - } - layout.button.label = running ? "STOP" : "START"; - layout.status.label = running ? "RUN" : "STOP"; - layout.status.bgCol = running ? "#0f0" : "#f00"; - // if stopping running, don't clear state - // so we can at least refer to what we've done - layout.render(); + var prepPromises = []; + // start/stop recording + // Do this first in case recorder needs to prompt for + // an overwrite before we start tracking exstats if (settings.record && WIDGETS["recorder"]) { if (running) { isMenuDisplayed = true; - WIDGETS["recorder"].setRecording(true).then(() => { - isMenuDisplayed = false; - layout.forgetLazyState(); - layout.render(); - }); + prepPromises.push( + WIDGETS["recorder"].setRecording(true).then(() => { + isMenuDisplayed = false; + layout.forgetLazyState(); + layout.render(); + }) + ); } else { - WIDGETS["recorder"].setRecording(false); + prepPromises.push( + WIDGETS["recorder"].setRecording(false) + ); } } + + if (!prepPromises.length) // fix for Promise.all bug in 2v12 + prepPromises.push(Promise.resolve()); + + Promise.all(prepPromises) + .then(() => { + if (running) { + exs.start(); + } else { + exs.stop(); + } + layout.button.label = running ? "STOP" : "START"; + layout.status.label = running ? "RUN" : "STOP"; + layout.status.bgCol = running ? "#0f0" : "#f00"; + // if stopping running, don't clear state + // so we can at least refer to what we've done + layout.render(); + }); } var lc = []; @@ -84,11 +113,27 @@ var layout = new Layout( { delete lc; layout.render(); +function configureNotification(stat) { + stat.on('notify', (e)=>{ + settings.notify[e.id].notifications.reduce(function (promise, buzzPattern) { + return promise.then(function () { + return Bangle.buzz(buzzPattern[0], buzzPattern[1]); + }); + }, Promise.resolve()); + }); +} + +Object.keys(settings.notify).forEach((statType) => { + if (settings.notify[statType].increment > 0 && exs.stats[statType]) { + configureNotification(exs.stats[statType]); + } +}); + // Handle GPS state change for icon Bangle.on("GPS", function(fix) { layout.gps.bgCol = fix.fix ? "#0f0" : "#f00"; if (!fix.fix) return; // only process actual fixes - if (fixCount++ == 0) { + if (fixCount++ === 0) { Bangle.buzz(); // first fix, does not need to respect quiet mode } }); diff --git a/apps/run/metadata.json b/apps/run/metadata.json index 7aabf8b53..afa52b2f7 100644 --- a/apps/run/metadata.json +++ b/apps/run/metadata.json @@ -1,6 +1,6 @@ { "id": "run", "name": "Run", - "version":"0.07", + "version":"0.13", "description": "Displays distance, time, steps, cadence, pace and more for runners.", "icon": "app.png", "tags": "run,running,fitness,outdoors,gps", diff --git a/apps/run/settings.js b/apps/run/settings.js index 7eb8a8611..c3bb31a0d 100644 --- a/apps/run/settings.js +++ b/apps/run/settings.js @@ -9,14 +9,28 @@ // This way saved values are preserved if a new version adds more settings const storage = require('Storage') let settings = Object.assign({ - record : true, - B1 : "dist", - B2 : "time", - B3 : "pacea", - B4 : "bpm", - B5 : "step", - B6 : "caden", - paceLength : 1000 + record: true, + B1: "dist", + B2: "time", + B3: "pacea", + B4: "bpm", + B5: "step", + B6: "caden", + paceLength: 1000, // TODO: Default to either 1km or 1mi based on locale + notify: { + dist: { + increment: 0, + notifications: [], + }, + step: { + increment: 0, + notifications: [], + }, + time: { + increment: 0, + notifications: [], + }, + }, }, storage.readJSON(SETTINGS_FILE, 1) || {}); function saveSettings() { storage.write(SETTINGS_FILE, settings) @@ -24,7 +38,7 @@ function getBoxChooser(boxID) { return { - min :0, max: statsIDs.length-1, + min: 0, max: statsIDs.length-1, value: Math.max(statsIDs.indexOf(settings[boxID]),0), format: v => statsList[v].name, onchange: v => { @@ -34,11 +48,19 @@ } } + function sampleBuzz(buzzPatterns) { + return buzzPatterns.reduce(function (promise, buzzPattern) { + return promise.then(function () { + return Bangle.buzz(buzzPattern[0], buzzPattern[1]); + }); + }, Promise.resolve()); + } + var menu = { '': { 'title': 'Run' }, '< Back': back, }; - if (WIDGETS["recorder"]) + if (global.WIDGETS&&WIDGETS["recorder"]) menu[/*LANG*/"Record Run"] = { value : !!settings.record, format : v => v?/*LANG*/"Yes":/*LANG*/"No", @@ -47,8 +69,55 @@ saveSettings(); } }; + var notificationsMenu = { + '< Back': function() { E.showMenu(menu) }, + } + menu[/*LANG*/"Notifications"] = function() { E.showMenu(notificationsMenu)}; ExStats.appendMenuItems(menu, settings, saveSettings); - Object.assign(menu,{ + ExStats.appendNotifyMenuItems(notificationsMenu, settings, saveSettings); + var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"]; + var vibTimes = [ + [], + [[100, 1]], + [[300, 1]], + [[300, 1], [300, 0], [300, 1]], + [[300, 1],[300, 0], [100, 1], [300, 0], [300, 1]], + [[300, 1],[300, 0],[300, 1],[300, 0],[300, 1]], + ]; + notificationsMenu[/*LANG*/"Dist Pattern"] = { + value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.dist.notifications))), + min: 0, max: vibTimes.length - 1, + format: v => vibPatterns[v]||/*LANG*/"Off", + onchange: v => { + settings.notify.dist.notifications = vibTimes[v]; + sampleBuzz(vibTimes[v]); + saveSettings(); + } + } + notificationsMenu[/*LANG*/"Step Pattern"] = { + value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.step.notifications))), + min: 0, max: vibTimes.length - 1, + format: v => vibPatterns[v]||/*LANG*/"Off", + onchange: v => { + settings.notify.step.notifications = vibTimes[v]; + sampleBuzz(vibTimes[v]); + saveSettings(); + } + } + notificationsMenu[/*LANG*/"Time Pattern"] = { + value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.time.notifications))), + min: 0, max: vibTimes.length - 1, + format: v => vibPatterns[v]||/*LANG*/"Off", + onchange: v => { + settings.notify.time.notifications = vibTimes[v]; + sampleBuzz(vibTimes[v]); + saveSettings(); + } + } + var boxMenu = { + '< Back': function() { E.showMenu(menu) }, + } + Object.assign(boxMenu,{ 'Box 1': getBoxChooser("B1"), 'Box 2': getBoxChooser("B2"), 'Box 3': getBoxChooser("B3"), @@ -56,5 +125,6 @@ 'Box 5': getBoxChooser("B5"), 'Box 6': getBoxChooser("B6"), }); + menu[/*LANG*/"Boxes"] = function() { E.showMenu(boxMenu)}; E.showMenu(menu); }) diff --git a/apps/s7clk/icon.js b/apps/s7clk/icon.js index d5d9aaf68..dbb4fc6d3 100644 --- a/apps/s7clk/icon.js +++ b/apps/s7clk/icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mUygP/AC5BlH4MAn/gAwN/4EP/AFBsEMhkBwEAjEDgYJBgEGgHA4EYDwOAmEwBIIYyj/wgf+AoMH/kA/4eBJXwYLVxgAjh//AC3w")) +require("heatshrink").decompress(atob("mEqgInkn/gg/8Ao/AjEYgYF/AoZT/Kb4AiA=")) diff --git a/apps/s7clk/icon.png b/apps/s7clk/icon.png index cb08aec5e..cfb1c0349 100644 Binary files a/apps/s7clk/icon.png and b/apps/s7clk/icon.png differ diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog new file mode 100644 index 000000000..c68653a76 --- /dev/null +++ b/apps/sched/ChangeLog @@ -0,0 +1,10 @@ +0.01: New App! +0.02: Fix scheduling of other alarms if there is a pending alarm from the past (fix #1667) +0.03: Fix `getTimeToAlarm` for a timer already used at same day, don't set `last` for timers. +0.04: Fix `getTimeToAlarm` to check for next dow if alarm.t lower currentTime. +0.05: Export new functions (`newDefaultAlarm/Timer`), add Settings page +0.06: Refactor some methods to library +0.07: Update settings + Correct `decodeTime(t)` to return a more likely expected time +0.08: add day of week check to getActiveAlarms() +0.09: Move some functions to new time_utils module diff --git a/apps/sched/README.md b/apps/sched/README.md new file mode 100644 index 000000000..57acdc0b7 --- /dev/null +++ b/apps/sched/README.md @@ -0,0 +1,108 @@ +Sched: Scheduling library for alarms and timers +==================================== + +This provides boot code, a library and tools for alarms and timers. + +Other apps can use this to provide alarm functionality. + +App +--- + +The **Alarms & Timers** app allows you to add/modify any running alarms and timers. + +Global Settings +--------------- + +- `Unlock at Buzz` - If `Yes` the alarm/timer will unlock the watch +- `Default Auto Snooze` - Default _Auto Snooze_ value for newly created alarms (_Alarms_ only) +- `Default Snooze` - Default _Snooze_ value for newly created alarms/timers +- `Default Repeat` - Default _Repeat_ value for newly created alarms (_Alarms_ only) +- `Buzz Count` - The number of buzzes before the watch goes silent +- `Buzz Interval` - The interval between one buzz and the next +- `Default Alarm/Timer Pattern` - Default vibration pattern for newly created alarms/timers + +Internals / Library +------------------- + +Alarms are stored in an array in `sched.json`, and take the form: + +``` +{ + id : "mytimer", // optional ID for this alarm/timer, so apps can easily find *their* timers + appid : "myappid", // optional app ID for alarms that you set/use for your app + on : true, // is the alarm enabled? + t : 23400000, // Time of day since midnight in ms (if a timer, this is set automatically when timer starts) + dow : 0b1111111, // Binary encoding for days of the week to run alarm on + // SUN = 1 + // MON = 2 + // TUE = 4 + // WED = 8 + // THU = 16 + // FRI = 32 + // SAT = 64 + + date : "2022-04-04", // OPTIONAL date for the alarm, in YYYY-MM-DD format + // eg (new Date()).toISOString().substr(0,10) + msg : "Eat food", // message to display. + last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! (No change from 0 on timers) + rp : true, // repeat the alarm every day? + vibrate : "...", // OPTIONAL pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set) + hidden : false, // OPTIONAL if false, the widget should not show an icon for this alarm + as : false, // auto snooze + timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms + js : "load('myapp.js')" // OPTIONAL - a JS command to execute when the alarm activates (*instead* of loading 'sched.js') + // when this code is run, you're responsible for setting alarm.on=false (or removing the alarm) + data : { ... } // OPTIONAL - your app can store custom data in here if needed (don't store a lot of data here) +} +``` + +The [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched/lib.js) contains +a few helpful functions for getting/setting alarms and timers, but is intentionally sparse so as not to +use too much RAM. + +It can be used as follows: + +``` +// Get a new alarm with default values +let alarm = require("sched").newDefaultAlarm(); + +// Get a new timer with default values +let timer = require("sched").newDefaultTimer(); + +// Add/update an existing alarm +require("sched").setAlarm("mytimer", { + msg : "Wake up", + timer : 10 * 60 * 1000 // 10 minutes +}); +// Ensure the widget and alarm timer updates to schedule the new alarm properly +require("sched").reload(); + +// Get the time to the next alarm for us +let timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer")); +// timeToNext === undefined if no alarm or alarm disabled + +// Delete an alarm +require("sched").setAlarm("mytimer", undefined); +// Reload after deleting +require("sched").reload(); + +// Or add an alarm that runs your own code - in this case +// loading the settings app. The alarm will not be removed/stopped +// automatically. +require("sched").setAlarm("customrunner", { + appid : "myapp", + js : "load('setting.app.js')", + timer : 1 * 60 * 1000 // 1 minute +}); + +// If you have been specifying `appid` you can also find any alarms that +// your app has created with the following: +require("sched").getAlarms().filter(a => a.appid == "myapp"); + +// Get the scheduler settings +let settings = require("sched").getSettings(); +``` + +If your app requires alarms, you can specify that the alarms app needs to +be installed by adding `"dependencies": {"scheduler":"type"},` to your app's +metadata. diff --git a/apps/sched/app-icon.js b/apps/sched/app-icon.js new file mode 100644 index 000000000..05515e859 --- /dev/null +++ b/apps/sched/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkGswAhiMRCCAREAo4eHBIQLEAgwYHsIJDiwHB5gACBpIhHCoYZEGA4gFCw4ABGA4HEjgXJ4IXGAwcUB4VEmf//8zogICoJIFAodMBoNDCoIADmgJB4gXIFwXDCwoABngwFC4guB4k/CQXwh4EC+YMCC44iBp4qDC4n/+gNBC41sEIJCEC4v/GAPGC4dhXYRdFC4xhCCYIXCdQRdDC5HzegQXCsxGHC45IDCwQXCUgwXHJAIXGRogXJSIIXcOw4XIPAYXcBwv/mEDBAwXOgtQC65QGC5vzoEAJAx3Nmk/mEABIiPN+dDAQIwFC4zXGFwKRCGAjvMFwQECGAgXI4YuGGAUvAgU8C4/EFwwGCAgdMC4p4EFwobFOwoXDJAIoEAApGBC4xIEABJGHGAapEAAqNBFwwXD4heI+YuBC5BIBVQhdHIw4wD5inFS4IKCCxFmigNCokzCoMzogICoIWIsMRjgPCAA3BiMWC48RBQIXJEgMRFxAJCCw4lEC44IECooOIBAaBJKwhgIAH4ACA==")) diff --git a/apps/sched/app.png b/apps/sched/app.png new file mode 100644 index 000000000..1f5784bde Binary files /dev/null and b/apps/sched/app.png differ diff --git a/apps/sched/boot.js b/apps/sched/boot.js new file mode 100644 index 000000000..dbdf02593 --- /dev/null +++ b/apps/sched/boot.js @@ -0,0 +1,44 @@ +// check for alarms +(function() { // run in closure to ensure allocated vars get removed + if (Bangle.SCHED) { + clearTimeout(Bangle.SCHED); + delete Bangle.SCHED; + } + var alarms = require('Storage').readJSON('sched.json',1)||[]; + var time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + var d = time.getDate(); + var active = alarms.filter( + a=>a.on && // enabled + a.last!=d && // not already fired today + a.t+60000>currentTime && // is not in the past by >1 minute + (a.dow>>time.getDay())&1 && // is allowed on this day of the week + (!a.date || a.date==time.toISOString().substr(0,10)) // is allowed on this date + ); + if (active.length) { + active = active.sort((a,b)=>a.t-b.t); // sort by time + var t = active[0].t-currentTime; + if (t<1000) t=1000; // start alarm minimum 1 sec from now + /* execute alarm at the correct time. We avoid execing immediately + since this code will get called AGAIN when alarm.js is loaded. alarm.js + will then clearInterval() to get rid of this call so it can proceed + normally. + If active[0].js is defined, just run that code as-is and not alarm.js */ + Bangle.SCHED = setTimeout(active[0].js||'load("sched.js")',t); + } else { // check for new alarms at midnight (so day of week works) + Bangle.SCHED = setTimeout('eval(require("Storage").read("sched.boot.js"))', 86400000 - (Date.now()%86400000)); + } +})(); +/* DEBUGGING +=============== + +// show the current timer for the next event +global["\xff"].timers[Bangle.SCHED] + +// time in hours of scheduled timer event +global["\xff"].timers[Bangle.SCHED].time / (1024*1024*60*60) + +// set time 1 hour in the past +setTime(getTime() - 60*60) + +*/ diff --git a/apps/sched/lib.js b/apps/sched/lib.js new file mode 100644 index 000000000..063402e3d --- /dev/null +++ b/apps/sched/lib.js @@ -0,0 +1,115 @@ +// Return an array of all alarms +exports.getAlarms = function() { + return require("Storage").readJSON("sched.json",1)||[]; +}; +// Write a list of alarms back to storage +exports.setAlarms = function(alarms) { + return require("Storage").writeJSON("sched.json",alarms); +}; +// Return an alarm object based on ID +exports.getAlarm = function(id) { + return exports.getAlarms().find(a=>a.id==id); +}; +// Given a list of alarms from getAlarms, return a list of active alarms for the given time (or current time if time not specified) +exports.getActiveAlarms = function(alarms, time) { + if (!time) time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000) + +10000;// get current time - 10s in future to ensure we alarm if we've started the app a tad early + return alarms.filter(a=>a.on + &&(a.t> (time).getDay() & 1) + .sort((a,b)=>a.t-b.t); +} +// Set an alarm object based on ID. Leave 'alarm' undefined to remove it +exports.setAlarm = function(id, alarm) { + var alarms = exports.getAlarms().filter(a=>a.id!=id); + if (alarm !== undefined) { + alarm.id = id; + if (alarm.dow===undefined) alarm.dow = 0b1111111; + if (alarm.on!==false) alarm.on=true; + if (alarm.timer) { // if it's a timer, set the start time as a time from *now* + var time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + alarm.t = currentTime + alarm.timer; + } + alarms.push(alarm); + } + exports.setAlarms(alarms); +}; +/// Get time until the given alarm (object). Return undefined if alarm not enabled, or if 86400000 or more, alarm could be *more* than a day in the future +exports.getTimeToAlarm = function(alarm, time) { + if (!alarm) return undefined; + if (!time) time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + var active = alarm.on && (alarm.dow>>((time.getDay()+(alarm.t { + if (buzzCount--) { + setTimeout(buzz, settings.buzzIntervalMillis); + } else if (alarm.as) { // auto-snooze + buzzCount = settings.buzzCount; + setTimeout(buzz, settings.defaultSnoozeMillis); + } + }); + } + + if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1) + return; + + buzz(); +} + +// Check for alarms +let alarms = require("sched").getAlarms(); +let active = require("sched").getActiveAlarms(alarms); +if (active.length) { + // if there's an alarm, show it + showAlarm(active[0]); +} else { + // otherwise just go back to default app + setTimeout(load, 100); +} diff --git a/apps/sched/settings.js b/apps/sched/settings.js new file mode 100644 index 000000000..5ddb4dab2 --- /dev/null +++ b/apps/sched/settings.js @@ -0,0 +1,81 @@ +(function (back) { + let settings = require("sched").getSettings(); + + E.showMenu({ + "": { "title": /*LANG*/"Scheduler" }, + + /*LANG*/"< Back": () => back(), + + /*LANG*/"Unlock at Buzz": { + value: settings.unlockAtBuzz, + format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", + onchange: v => { + settings.unlockAtBuzz = v; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Default Auto Snooze": { + value: settings.defaultAutoSnooze, + format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", + onchange: v => { + settings.defaultAutoSnooze = v; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Default Snooze": { + value: settings.defaultSnoozeMillis / 60000, + min: 5, + max: 30, + step: 5, + format: v => v + /*LANG*/" min", + onchange: v => { + settings.defaultSnoozeMillis = v * 60000; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Default Repeat": { + value: settings.defaultRepeat, + format: v => v ? /*LANG*/"Yes" : /*LANG*/"No", + onchange: v => { + settings.defaultRepeat = v; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Buzz Count": { + value: settings.buzzCount, + min: 5, + max: 15, + step: 1, + onchange: v => { + settings.buzzCount = v; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Buzz Interval": { + value: settings.buzzIntervalMillis / 1000, + min: 1, + max: 5, + step: 1, + format: v => v + /*LANG*/"s", + onchange: v => { + settings.buzzIntervalMillis = v * 1000; + require("sched").setSettings(settings); + } + }, + + /*LANG*/"Default Alarm Pattern": require("buzz_menu").pattern(settings.defaultAlarmPattern, v => { + settings.defaultAlarmPattern = v; + require("sched").setSettings(settings); + }), + + /*LANG*/"Default Timer Pattern": require("buzz_menu").pattern(settings.defaultTimerPattern, v => { + settings.defaultTimerPattern = v; + require("sched").setSettings(settings); + }) + }); +}); diff --git a/apps/scicalc/ChangeLog b/apps/scicalc/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/scicalc/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/scicalc/README.md b/apps/scicalc/README.md new file mode 100644 index 000000000..740b4216b --- /dev/null +++ b/apps/scicalc/README.md @@ -0,0 +1,24 @@ +# SciCalc + +Simple scientific calculator. I needed one, so I wrote a basic one, no design frills. Input expressions are slightly post processed and then evaluated +by the JS interpreter. + +## Usage + +Buttons are arranged on 3 separate screens, swiping left or right switches between them. Swiping down has the same effect as hitting the "=" button. + +## Features + +The calculator supports the following operations: + + * basic arithmetic: +, -, *, /, ^ (raise to a power), +/- (invert sign), 1/x (inverse), use of parentheses + * trigonometric fucntions: sin, cos, tan, asin, acos, atan + * exponential exp, natural logarithm log, pow function (this one takes 2 comma separated arguments) + * Pi is provided as a constant + * a memory button "M" stores or recalls the last result (after hitting the "=" button or swiping down) + +![](scicalc_screenshot1.png) + +![](scicalc_screenshot2.png) + +![](scicalc_screenshot3.png) diff --git a/apps/scicalc/app-icon.js b/apps/scicalc/app-icon.js new file mode 100644 index 000000000..b8363e6ee --- /dev/null +++ b/apps/scicalc/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AJioAaF1wwSFzowRCQUZo4AWjIvVFy4ABF/4vXyGQAYov/R+sZFy8ZF6oAcF/4vvi4AeF/4SCjseAAMdAx8MAAYvVEAQABAx4v/R/TvvF96PUg8cAAMHd9QuCAAIv/R+rvvF96Pvd94vvR97vvF96Pvd94vvR97vsGDwuQGDouSAH4A/AGwA==")) diff --git a/apps/scicalc/app.js b/apps/scicalc/app.js new file mode 100644 index 000000000..5d914d0c5 --- /dev/null +++ b/apps/scicalc/app.js @@ -0,0 +1,113 @@ +const W = g.getWidth(); +const H = g.getHeight(); + +const dispH = H/5; +const butH = H-dispH; + +const buttons = [[['7', '8', '9'], + ['4', '5', '6'], + ['1', '2', '3'], + ['E', '0', '.']], + [['<', 'M', 'C'], + ['+', '-', '*'], + ['/', '(', ')'], + ['^', ',', '=']], + [['Sin', 'Cos', 'Tan'], + ['Asi', 'Aco', 'Ata'], + ['Pi', '1/x', '+/-'], + ['Log', 'Exp', 'Pow'] + ]]; + +var curPage = 0; +var inputStr = ''; +var memory = ''; +var qResult = false; + +function drawPage (p) { + g.clearRect(0, dispH, W-1, H-1); + g.setFont('Vector', butH/5).setFontAlign(0, 0, 0).setColor(g.theme.fg); + for (x=0; x<3; ++x) + for (y=0; y<4; ++y) + g.drawString(buttons[p][y][x], (x+0.5)*W/3, dispH+(y+0.7)*butH/4); + g.setColor(0.5, 0.5, 0.5); + for (x=1; x<3; ++x) g.drawLine(x*W/3, dispH+0.2*butH/4-2, x*W/3, H-1); + for (y=1; y<4; ++y) g.drawLine(0, dispH+(y+0.2)*butH/4, W-1, dispH+(y+0.2)*butH/4); + g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2); +} + +function updateDisp(s, len) { + var fh = butH/5; + if (s.toString().length>len) s = s.toString().substr(0,len); + g.setFont("Vector", butH/5).setColor(g.theme.fg).setFontAlign(1, 0, 0); + while (g.stringWidth(s) > W-1) { + fh /= 1.05; + g.setFont("Vector", fh); + } + g.clearRect(0, 0, W-1, dispH-1).drawString(s, W-2, dispH/2); + g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2); +} + +function processInp (s) { + var idx = s.indexOf("^"); + if (idx > 0) s = "Math.pow(" + s.slice(0,idx) + "," + s.slice(idx+1, s.length) + ")"; + ['Sin', 'Cos', 'Tan', 'Asin', 'Acos', 'Atan', 'Log', 'Exp', 'Pow'].forEach((x) => { + var i = s.indexOf(x); + while (i>-1) { + s = s.slice(0,i)+"Math."+s.slice(i,i+1).toLowerCase()+s.slice(i+1, s.length); + i = s.indexOf(x, i+6); + } + }); + idx = s.indexOf('Pi'); + if (idx>-1) s = s.slice(0,idx) + "Math.PI" + s.slice(idx+2, s.length); + idx = 0; + s.split('').forEach((x)=>{ if (x=='(') idx++; if (x==')') idx-- }); + s += ')'.repeat(idx); + return s; +} + +function compute() { + var res; + console.log(processInp(inputStr)); + try { res = eval(processInp(inputStr)); } + catch(e) { res = "error"; } + inputStr = res; + qResult = true; + updateDisp(inputStr, 19); +} + +function touchHandler(e, d) { + var x = Math.floor(d.x/(W/3)); + var y = Math.floor((d.y-dispH-0.2*butH/4)/(butH/4)); + var c = buttons[curPage][y][x]; + if (c=="=") { // do the computation + compute(); + return; + } + else if (c=="<" && inputStr.length>0) inputStr = inputStr.slice(0, -1); // delete last character + else if (c=='M' && qResult) memory = inputStr; + else if (c=='M') inputStr += memory; + else if (c=="C") inputStr = ''; // clear + else { + if ("Sin Cos Tan Log Exp Pow".indexOf(c)>-1 && c!='E') c += "("; + if ("Asi Aco Ata".indexOf(c)>-1) c += "n("; + if (c=='1/x') { inputStr = "1/("+inputStr+")"; compute(); return; } + if (c=='+/-') { inputStr = "-("+inputStr+")"; compute(); return; } + if (qResult && "+-*/^".indexOf(c)==-1) inputStr = c + inputStr + ")"; + else inputStr += c; + } + qResult = false; + updateDisp(inputStr, 32); +} + +function swipeHandler(e,d) { + curPage -= e; + if (curPage>buttons.length-1) curPage = 0; + if (curPage<0) curPage = buttons.length-1; + drawPage(curPage); + if (d==1) compute(); +} + +Bangle.on("touch", touchHandler); +Bangle.on("swipe", swipeHandler); +g.clear(); +drawPage(curPage); diff --git a/apps/scicalc/metadata.json b/apps/scicalc/metadata.json new file mode 100644 index 000000000..beda619e2 --- /dev/null +++ b/apps/scicalc/metadata.json @@ -0,0 +1,16 @@ +{ "id": "scicalc", + "name": "Scientific Calculator", + "shortName":"SciCalc", + "version":"0.01", + "description": "Scientific calculator", + "icon": "scicalc.png", + "screenshots" : [ { "url":"scicalc_screenshot1.png" }, { "url":"scicalc_screenshot2.png" }, { "url":"scicalc_screenshot3.png" } ], + "readme": "README.md", + "tags": "app,tool", + "allow_emulator": true, + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"scicalc.app.js","url":"app.js"}, + {"name":"scicalc.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/scicalc/scicalc.png b/apps/scicalc/scicalc.png new file mode 100644 index 000000000..b5aa6ff7e Binary files /dev/null and b/apps/scicalc/scicalc.png differ diff --git a/apps/scicalc/scicalc_screenshot1.png b/apps/scicalc/scicalc_screenshot1.png new file mode 100644 index 000000000..7f9d46860 Binary files /dev/null and b/apps/scicalc/scicalc_screenshot1.png differ diff --git a/apps/scicalc/scicalc_screenshot2.png b/apps/scicalc/scicalc_screenshot2.png new file mode 100644 index 000000000..795d922e8 Binary files /dev/null and b/apps/scicalc/scicalc_screenshot2.png differ diff --git a/apps/scicalc/scicalc_screenshot3.png b/apps/scicalc/scicalc_screenshot3.png new file mode 100644 index 000000000..9319157ba Binary files /dev/null and b/apps/scicalc/scicalc_screenshot3.png differ diff --git a/apps/score/metadata.json b/apps/score/metadata.json index fd72e197d..b593d7388 100644 --- a/apps/score/metadata.json +++ b/apps/score/metadata.json @@ -8,6 +8,7 @@ "type": "app", "tags": "", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "storage": [ {"name":"score.app.js","url":"score.app.js"}, {"name":"score.settings.js","url":"score.settings.js"}, diff --git a/apps/scribble/app.js b/apps/scribble/app.js index 99ee3f717..319a02d2c 100644 --- a/apps/scribble/app.js +++ b/apps/scribble/app.js @@ -368,8 +368,8 @@ class TextBox { // x and y are the center points this.x = x; this.y = y; - this.text = (typeof text !== undefined) ? text : "Default"; - this.col = (typeof col !== undefined) ? col : red; + this.text = text || "Default"; + this.col = col || red; // console.log(`Constr TextBox ${this.text} -> Center: (${this.x}, ${this.y}) | Col ${this.col}`); } diff --git a/apps/seiko-5actus/ChangeLog b/apps/seiko-5actus/ChangeLog new file mode 100644 index 000000000..978e5d6ea --- /dev/null +++ b/apps/seiko-5actus/ChangeLog @@ -0,0 +1,2 @@ +0.01: Initial Release +0.02: Shrink hand images to save memory diff --git a/apps/seiko-5actus/README.md b/apps/seiko-5actus/README.md new file mode 100644 index 000000000..4f09bf3c6 --- /dev/null +++ b/apps/seiko-5actus/README.md @@ -0,0 +1,16 @@ +# Seiko 5actus + +![](screenshot.png) + +This is built on the knowledge of what I gained through designing the rolex watch face and improves on it by getting more done right at the start. + +This watch is modeled after one I personally own and love, I have spent quite a bit of time designing this in a pixel art editor to try and make it as clean as possible and am quite happy with how it came out. + +This watch face works in both the light and dark themes but I personally think it looks a lot cleaner in the dark them. + +This watch whilst technically designed in a way that would work with the BangleJs has been only listed to work with the BangleJs2, if someones wants to test it on a first gen and let me know if it works then i'll allow it to be installed on both devices but I assume with how the images have been designed it would look strange on a first gen watch. + +Special thanks to: +* rozek (for his updated widget draw code for utilization with background images) +* Gordon Williams (Bangle.js, watchapps for reference code and documentation) +* The community (for helping drive such a wonderful project) diff --git a/apps/seiko-5actus/app-icon.js b/apps/seiko-5actus/app-icon.js new file mode 100644 index 000000000..796f24122 --- /dev/null +++ b/apps/seiko-5actus/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkEBpMB+fziAjRCQQXBHyoXEgIRLgMwC5EAj8gC5MC+QXJn4XKBgJHJMhkfJAYXEh/xC5cDBofzJocvIxKiCHpBGNExMCIxi2KeRIJFgMiiYBCkQ1Jh67EAAMSCgICBiQjFn8xDYX/AgQANn4qEgf/JIcDkcxiUSiMRY4cv+ZaFj6bDgZGBkMRDIIXD/7CHn5TDFYIADFIcRSxgAvXQwAQgRyDACcje4wAESQ4RDmMQgSGBj8zAAnyTgauH/65Cj7EBkMicAPyBYIABCgcRkYWCmYvCewMhmUiiMyF4gUBDoXzn7/Dj4RBF4IXB+QrDj40DmJgBiEyBYMDL4qcEgUikYqDCgSGKAAUSn8hbh8BgICBl/yFggwEbhMC/4sHAAcTIhIsBGYLcJGAQOHgLAEGBM/Jg0vRgsQZAMQBAQUBif/AwLJDfoQ1DkDOBkIOCgQDBFAKCDaIRPGSwqGBgM/dwUDeQiZEEAowCgXxb5jGGgQ+GaAjaHeIbmISYToHRYTPKYQIODE4cfEA4AEPghtEgQJCI5Mv+AXHJAiIIBggXFj/xDBMCcAYXGga7LcYIXIUpY1GC4SiJVRAXCiAWJA")) diff --git a/apps/seiko-5actus/app.js b/apps/seiko-5actus/app.js new file mode 100644 index 000000000..078b6e5c2 --- /dev/null +++ b/apps/seiko-5actus/app.js @@ -0,0 +1,181 @@ +var imgBg = { + width : 176, height : 176, bpp : 2, + transparent : 1, + buffer : require("heatshrink").decompress(atob("qoASv/8//1C6YASrorD6opjuorHBAIriQYwriV9FXFZldFjrSEFY6FjQcorPSYqtZFZaxaVoiDkZRArLv4rV/orT/5rGABtf/4rSq//+79UFaptIK5puHFZQUBFda5IABZuBCw4rKTAPVq4rUNw4rK/4rBroqRQAIrVQSd1N4QrQNZIAOFdbzBQ4IrOCQIIDWKQYFFZhqBHwaeBACBwBFazZQThQrJ/7CHABaSEFaTaWOIYrONJArTToorIdpDdRDQLJOCBDhRIxBoPABdXAwwrRVKQ+GZR7aZDYRHNM4IraOZyTQZbQrKaIwAKr7LOHRNdFaJzOr56RuppZACBgRAEtW1Wl1WqzWm1Nqy2qC5hyTrWq1IrFzWqyoXLv7mFQRlqqtp0tVy2V0uptNVA4JWK/72Fr4yFFY9VLINWyqJBFZtfFYwGGQZIrS////peF/6xM1KDDQQIrMKwJQFA4SEKQYLbGAoIrKv5PGGY4rGEYIAHyrZKQQoIDQhoARKwR6GBJIAXJpKECMIoAXUpaELBYQAICZR4CPYo3Kq4IBr7RITIzZFq7mOGwXXBYIjB//3/9drt/+5AGZ4RKHuoNEFZVdupQBuorBupjCJIyNIrqELr4mBr99vorEK476PBxAYC79//orCr4QBu5XIXAwiIcwxXCKYJlBFYSvIQRI7HTiLjBFYzZHAAzdBDA4AKDY6CMQgYQOABT8CEQjdJFTAAKuo8MADokkAH4A/AH4A/ABn1FldXFlfVTX4A/AH4A/AH4APr//ABVVrorcv4rL6tXFbgqL//1Qbv/1QAE1AED14re/wrK1Yr/Ff4rEcY3VFf4r/Ff4r/EaoAH/4rK14r/FZYALFb1/FZbTWAA9fFZYNBFjoA/AH4A/AH4A/vots+pu/AH4A/AH4A/ADdX6ousr4GFrohgABP/FbN/+o7O/6NZv5HOB4IrZ///LBlXB5wrO/qCNB5pzOOhgNBK7RICDhQNCX5P96td/91u4FBvpJLAoQPDrplEQRNdFYPVu93qvVO5JYJurZDSJV1FYP9FYP16oXBfJRKIbJpQB7vVv/3AwJvCbpTZVVIP9/9d6/9AALdTbJgAVEJDZMACoiCLAjZNAAqSKbpjZNSoo7PQg4zCIx9/FaBQBJ4rZRHqJQBFYzZRLCN/HooYRCQIRQYQ1dDCFVQR4A/ADFXCSK5RDJ40Iq6nPW6LcIq//fh39SrNf/6EN/47OFZp0NFbd/K5wPPI5obNM5F1FaYPNdYLcGfpA3IDQIrXABZ6FDSLcZTxAIBW4zcBFa4ZBFaLsNFZYZGFZBpIACCdBFZ7BFq7dSZJArOfQwAHHQYYBFaA+JABwhBIggrObirIJFZLuBbioXJFZI/JWJorBEJIJJS4KFRrqbKFZLvDupYSeZIrJbiwrBNwIrnTQYrReBIrNCpArKBRQAKIJQrLNhCvNJidXQSZYCPCiCTIQS6KEI4pVAAddFaF1FbAZHfioAVFYyUJWbRXHLrqxFFYhVeLI4rq6orCMAoAhFa4")) +}; + +/* Set hour hand image */ + +var imgHour = { + width : 14, height : 114, bpp : 2, + transparent : 0, + buffer : require("heatshrink").decompress(atob("AH4A/AB8P/4DB//wAz8D//8BIIKBn4DB54CBACPzAQP8EoImBD4PAJkQG/A34GIgbUBA")) +}; + +/* Set minute hand image */ + +var imgMin = { + width : 4, height : 168, bpp : 2, + transparent : 0, + buffer : require("heatshrink").decompress(atob("AH4AE/4A/AEI")) +}; + +/* Set second hand image */ + +var imgSec = { + width : 6, height : 176, bpp : 2, + transparent : 1, + buffer : require("heatshrink").decompress(atob("qoA/ADFf6v9AU1c6vNFlICWvtXAXlVA=")) +}; + +/* Sets the font for the date at an appropriate size of 14 */ + +Graphics.prototype.setFontWorkSans = function(scale) { + // Actual height 12 (12 - 1) + this.setFontCustom(atob("AAAAAAADwAAAPAAAAAAAAAvAAC/0AH/gAL/QAD9AAAIAAAAACQAAL/9AC/r9APAA8A8ADwDwAfAH//wAH/8AAAAAADwAAAtAAAH//8Af//wAAAAAAAAAABwAUAfgHwDwA/APAP8A8DzwD/9PAD/Q8AAABQAAA0AHwDwA9AHwDw4PAPDw8A///gA/f8AAAAAAAAQAAAvAAAP8AALzwAD8PAAv//wB///AAAPAAAAUAAAAAAC/88AP/y8A8NDwDywPAPD18A8L/AAAGgAAAAAAC/8AA//9ALTx8A8ODwDw4PALz/8ALD/AAAAAAPAAAA8AAADwB/APC/8A9/gAD/AAAPgAAAAAAAAAAAAB8fwAf//wDw8PAPDw8A8PDwB///AC8vwAAAAAAAAAAD/DgAv/PQDwsPAPC08A8PDwB//8AB//QAAAAAAAAAAA8DwADwPAABAE"), 46, atob("BAYJBggICQgJCAkJBA=="), 14+(scale<<8)+(2<<16)); + return this; +}; + +/* Sets the font for the day at an appropriate size of 12 */ + +Graphics.prototype.setFontWorkSansSmall = function(scale) { + // Actual height 12 (11 - 0) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAABAP/zwGqjQAAAAP0AAEAAAP4AAGAAAAEoAAs/gL//QL88AAt/gL/+QK88AAYAAAUFAD+PAHPDgv//8fr70Hj7QCx+AAAAAC8AAL/AAPHBgL/PQB68AAPgAC9/APT7wEDjwAC/QAAAAAAuAC7/gL/DwPPywL9/gCwvAAD7wAABgAAAAP0AAEAAAACkAC//wP0C8sAAPYAAGPQA+D//0Af9ABQAADzAAB/AAv8AAB/AADyAABAAAACAAADgAADgAC//AAr5AADgAADQAAABAAAD9AAD0AAAAACwAACwAACwAACwAAAgAAABAAADwAADQAAAkAAv0Af9AL+AAvAAAAKgAD//ALgLgPADwPADwD//QA/9AAAAAAwAADwAAL//gL//gAAAAAgBQD4DwPAPwPA/wLnzwD/TwAUBQAAFADwPQLADwPDDwPvjwD+/AAQYAAAoAAD8AAv8AD08AP//gL//gAA8AAAAAL/PAPrDwPPDwPPDwPH/AAAoAAKgAC/+AHzrgPPDwPPDwH3/gBS+AKAAAPAAAPAbgPL/gP/QAPwAALAAAAAYAD+/ALvjwPLDwPLDwH//gBk+AAYAAD/PALTzwPDzwPDjwD//AAv8AAQBAA8DwA4DgAAAAAEBAAPD9AOD4AAAAABQAADwAAP4AANsAA8OAA4PABwHAAIUAAM8AAM8AAM8AAM8AAM8AAMoABgCAA4LAA8OAAdsAAP8AAHwAADgABgAADwAAPCzgPDzwLvBAD9AAAQAAABoAAv/wC0A8DD/OLPX3OMDjONHDHP/7Dfi5B4HQAP9AAACgAC/gB/8AP48AP48AB/8AAC/gAACgAAAAL//gP//wPDDwPDDwLv3gD+/AAAYAAGQAB/+AH0fQPADwPADwPADwDwPQBgNAAAAAL//gP//wPADwPADwHQDgD//AA/8AAAAAAAAAL//gP//wPDDwPDDwPDDwPADwAAAAAAAAL//gP//gPDAAPDAAPDAAPAAAAGQAB/+AD0fQPADwPCjwPDzwHz/QBy/gAAAAAAAAL//gL//gADAAADAAADAAL//gL//gAAAAAAAAL//gL//gAAAAAAeAAAvgAADwAADwL//gP//AAAAAAAAAL//gL//gAPAAA/0ADx/APAPwIABgAAAAL//gL//wAADwAADwAADwAACgAAAAL//gP6qQC/gAAH/QAAvwAv9AL9AAP//wGqqQAAAAL//gL6qQC+AAAP0AAB/AL//wL//gAAAAAGQAB/+AH0fQPADwPADwPADwD6vQB/9AAGQAAAAAL//gP//gPDwAPDwAH7gAD/AAAAAAAGQAB/+AH0fQPADwPAD9PAD/D6vbB/9PAGQAAAAAL//gP//gPDgAPD0ALr/AD/LwAUAgAUFAD+PALvDwPHDwPDjwLT7gDx/AAAAALAAAPAAAPAAAP//wPqqQPAAAPAAAAAAAP/+AGqvgAADwAADwAADwL//AL/4AAAAAKAAAL+AAAv9AAAvwAB/gAv8AL9AAKAAAKQAAL/QAAf/gAAPwAv/QL+AAL/gAAL/gAAvwC/+AP9AAEAAAEABgPgLwD9+AAfwAA/8AL0fgPADwOAAAL0AAB/AAAH/wA/qQL4AAPAAAFACgPAPwPA/wPHzwPvDwP4DwLQDwAAAAAAAAv///uqqvsAAPdAAAf4AAB/0AAC/wAAC4cAAKsAAPv///Kqqp"), 32, atob("BAMFCAgLCAMEBAcHAwYDBQgFBwcHBwcHBwcEBAcHBwcLCAgICQgHCQkEBwgHCgkJCAkICAcJCAwHBwgEBQQ="), 12+(scale<<8)+(2<<16)); + return this; +}; + +/* Set variables to get screen width, height and center points */ + +let W = g.getWidth(); +let H = g.getHeight(); +let cx = W/2; +let cy = H/2; +let Timeout; + +Bangle.loadWidgets(); + +/* Custom version of Bangle.drawWidgets (does not clear the widget areas) Thanks to rozek */ + +Bangle.drawWidgets = function () { + var w = g.getWidth(), h = g.getHeight(); + + var pos = { + tl:{x:0, y:0, r:0, c:0}, // if r==1, we're right->left + tr:{x:w-1, y:0, r:1, c:0}, + bl:{x:0, y:h-24, r:0, c:0}, + br:{x:w-1, y:h-24, r:1, c:0} + }; + + if (global.WIDGETS) { + for (var wd of WIDGETS) { + var p = pos[wd.area]; + if (!p) continue; + + wd.x = p.x - p.r*wd.width; + wd.y = p.y; + + p.x += wd.width*(1-2*p.r); + p.c++; + } + + g.reset(); // also loads the current theme + + try { + for (var wd of WIDGETS) { + g.setClipRect(wd.x,wd.y, wd.x+wd.width-1,23); + wd.draw(wd); + } + } catch (e) { print(e); } + + g.reset(); // clears the clipping rectangle! + } + }; + +/* Draws the clock hands and date */ + +function drawHands() { + let d = new Date(); + + let hour = d.getHours() % 12; + let min = d.getMinutes(); + let sec = d.getSeconds(); + + let twoPi = 2*Math.PI; + let Pi = Math.PI; + + let hourAngle = (hour+(min/60))/12 * twoPi - Pi; + let minAngle = (min/60) * twoPi - Pi; + let secAngle = (sec/60) * twoPi - Pi; + + g.setFontWorkSans(); + g.setColor(g.theme.bg); + g.setFontAlign(0,0,0); + g.drawString(d.getDate(),162,90); + g.setFontWorkSansSmall(); + let weekDay = d.toString().split(" "); + if (weekDay[0] == "Sat"){g.setColor(0,0,1);} + else if (weekDay[0] == "Sun"){g.setColor(1,0,0);} + else {g.setColor(g.theme.bg);} + g.drawString(weekDay[0].toUpperCase(), 137, 90); + + handLayers = [ + {x:cx, + y:cy, + image:imgHour, + rotate:hourAngle, + center:true + }, + {x:cx, + y:cy, + image:imgMin, + rotate:minAngle, + center:true + }, + {x:cx, + y:cy, + image:imgSec, + rotate:secAngle, + center:true + }]; + + g.setColor(g.theme.fg); + g.drawImages(handLayers); +} + +function drawBackground() { + g.clear(1); + g.setBgColor(g.theme.bg); + g.setColor(g.theme.fg); + bgLayer = [ + {x:cx, + y:cy, + image:imgBg, + center:true + }]; + g.drawImages(bgLayer); + g.reset(); +} + +/* Refresh the display every second */ + +function displayRefresh() { + g.clear(true); + drawBackground(); + drawHands(); + Bangle.drawWidgets(); + + let Pause = 1000 - (Date.now() % 1000); + Timeout = setTimeout(displayRefresh,Pause); +} + +Bangle.on('lcdPower', (on) => { + if (on) { + if (Timeout != null) { clearTimeout(Timeout); Timeout = undefined;} + displayRefresh(); + } +}); + +Bangle.setUI("clock"); +// load widgets after 'setUI' so they're aware there is a clock active +Bangle.loadWidgets(); +displayRefresh(); diff --git a/apps/seiko-5actus/metadata.json b/apps/seiko-5actus/metadata.json new file mode 100644 index 000000000..33de8213b --- /dev/null +++ b/apps/seiko-5actus/metadata.json @@ -0,0 +1,17 @@ +{ "id": "seiko-5actus", + "name": "Seiko 5actus", + "shortName":"5actus", + "icon": "seiko-5actus.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.02", + "description": "A watch designed after then Seiko 5actus from the 1970's", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"seiko-5actus.app.js","url":"app.js"}, + {"name":"seiko-5actus.img","url":"app-icon.js","evaluate":true} + ] + } diff --git a/apps/seiko-5actus/screenshot.png b/apps/seiko-5actus/screenshot.png new file mode 100644 index 000000000..fb8638999 Binary files /dev/null and b/apps/seiko-5actus/screenshot.png differ diff --git a/apps/seiko-5actus/seiko-5actus.png b/apps/seiko-5actus/seiko-5actus.png new file mode 100644 index 000000000..73f1b8164 Binary files /dev/null and b/apps/seiko-5actus/seiko-5actus.png differ diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 39b4897b8..bfd32a130 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -45,3 +45,6 @@ 0.40: Moved off into Utils, put System after Apps 0.41: Stop users disabling all wake-up methods and locking themselves out (fix #1272) 0.42: Fix theme customizer on new Bangle 2 firmware +0.43: Add some Bangle 1 colours to theme customizer +0.44: Add "Start Week On X" option (#1780) + UI improvements to Locale and Date & Time menu diff --git a/apps/setting/README.md b/apps/setting/README.md index 42e3939fb..451b48c06 100644 --- a/apps/setting/README.md +++ b/apps/setting/README.md @@ -7,9 +7,9 @@ This is Bangle.js's settings menu * **Beep** most Bangle.js do not have a speaker inside, but they can use the vibration motor to beep in different pitches. You can change the behaviour here to use a Piezo speaker if one is connected * **Vibration** enable/disable the vibration motor * **Quiet Mode** prevent notifications/alarms from vibrating/beeping/turning the screen on - see below -* **Locale** set time zone/whether the clock is 12/24 hour (for supported clocks) +* **Locale** set time zone, the time format (12/24h, for supported clocks) and the first day of the week * **Select Clock** if you have more than one clock face, select the default one -* **Set Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader +* **Date & Time** Configure the current time - Note that this can be done much more easily by choosing 'Set Time' from the App Loader * **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below. * **Theme** Adjust the colour scheme * **Utils** Utilities - including resetting settings (see below) @@ -35,11 +35,15 @@ This is Bangle.js's settings menu `Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js. * **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement. +## Locale +* **Time Zone** your current Time zone. This is usually set automatically by the App Loader +* **Time Format** whether you want a 24 or 12 hour clock. However not all clocks will honour this. +* **Start Week On** start the displayed week on Sunday, or Monday. This currently only applies to the Alarm app. ## Quiet Mode -Quiet Mode is a hint to apps and widgets that you do not want to be disturbed. +Quiet Mode is a hint to apps and widgets that you do not want to be disturbed. The exact effects depend on the app. In general the watch will not wake up by itself, but will still respond to button presses. * **Quiet Mode** diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index 4bb5ec129..85dddf9db 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.42", + "version": "0.44", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", diff --git a/apps/setting/settings.js b/apps/setting/settings.js index f74c94db0..9b5bdae68 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -37,18 +37,19 @@ function internalToG(u) { function resetSettings() { settings = { - ble: true, // Bluetooth enabled by default - blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used? - log: false, // Do log messages appear on screen? - quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence - timeout: 10, // Default LCD timeout in seconds - vibrate: true, // Vibration enabled by default. App must support - beep: BANGLEJS2?true:"vib", // Beep enabled by default. App must support - timezone: 0, // Set the timezone for the device - HID: false, // BLE HID mode, off by default - clock: null, // a string for the default clock's name - "12hour" : false, // 12 or 24 hour clock? - brightness: 1, // LCD brightness from 0 to 1 + ble: true, // Bluetooth enabled by default + blerepl: true, // Is REPL on Bluetooth - can Espruino IDE be used? + log: false, // Do log messages appear on screen? + quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence + timeout: 10, // Default LCD timeout in seconds + vibrate: true, // Vibration enabled by default. App must support + beep: BANGLEJS2 ? true : "vib", // Beep enabled by default. App must support + timezone: 0, // Set the timezone for the device + HID: false, // BLE HID mode, off by default + clock: null, // a string for the default clock's name + "12hour" : false, // 12 or 24 hour clock? + firstDayOfWeek: 0, // 0 -> Sunday (default), 1 -> Monday + brightness: 1, // LCD brightness from 0 to 1 // welcomed : undefined/true (whether welcome app should show) options: { wakeOnBTN1: true, @@ -94,7 +95,7 @@ function showSystemMenu() { /*LANG*/'LCD': ()=>showLCDMenu(), /*LANG*/'Locale': ()=>showLocaleMenu(), /*LANG*/'Select Clock': ()=>showClockMenu(), - /*LANG*/'Set Time': ()=>showSetTimeMenu() + /*LANG*/'Date & Time': ()=>showSetTimeMenu() }; return E.showMenu(mainmenu); @@ -251,11 +252,15 @@ function showThemeMenu() { } upd(th); } - const rgb = { + let rgb = { black: "#000", white: "#fff", red: "#f00", green: "#0f0", blue: "#00f", cyan: "#0ff", magenta: "#f0f", yellow: "#ff0", }; + if (!BANGLEJS2) Object.assign(rgb, { + // these would cause dithering, which is not great for e.g. text + orange: "#ff7f00", purple: "#7f00ff", grey: "#7f7f7f", + }); let colors = [], names = []; for(const c in rgb) { names.push(c); @@ -474,6 +479,7 @@ function showLocaleMenu() { '< Back': ()=>showSystemMenu(), /*LANG*/'Time Zone': { value: settings.timezone, + format: v => (v > 0 ? "+" : "") + v, min: -11, max: 13, step: 0.5, @@ -482,13 +488,23 @@ function showLocaleMenu() { updateSettings(); } }, - /*LANG*/'Clock Style': { + /*LANG*/'Time Format': { value: !!settings["12hour"], - format: v => v ? "12hr" : "24hr", + format: v => v ? "12h" : "24h", onchange: v => { settings["12hour"] = v; updateSettings(); } + }, + /*LANG*/'Start Week On': { + value: settings["firstDayOfWeek"] || 0, + min: 0, // Sunday + max: 1, // Monday + format: v => require("date_utils").dow(v, 1), + onchange: v => { + settings["firstDayOfWeek"] = v; + updateSettings(); + }, } }; return E.showMenu(localemenu); @@ -602,11 +618,34 @@ function showClockMenu() { function showSetTimeMenu() { d = new Date(); const timemenu = { - '': { 'title': /*LANG*/'Set Time' }, + '': { 'title': /*LANG*/'Date & Time' }, '< Back': function () { setTime(d.getTime() / 1000); showSystemMenu(); }, + /*LANG*/'Day': { + value: d.getDate(), + onchange: function (v) { + this.value = ((v+30)%31)+1; + d.setDate(this.value); + } + }, + /*LANG*/'Month': { + value: d.getMonth() + 1, + format: v => require("date_utils").month(v), + onchange: function (v) { + this.value = ((v+11)%12)+1; + d.setMonth(this.value - 1); + } + }, + /*LANG*/'Year': { + value: d.getFullYear(), + min: 2019, + max: 2100, + onchange: function (v) { + d.setFullYear(v); + } + }, /*LANG*/'Hour': { value: d.getHours(), onchange: function (v) { @@ -627,28 +666,6 @@ function showSetTimeMenu() { this.value = (v+60)%60; d.setSeconds(this.value); } - }, - /*LANG*/'Date': { - value: d.getDate(), - onchange: function (v) { - this.value = ((v+30)%31)+1; - d.setDate(this.value); - } - }, - /*LANG*/'Month': { - value: d.getMonth() + 1, - onchange: function (v) { - this.value = ((v+11)%12)+1; - d.setMonth(this.value - 1); - } - }, - /*LANG*/'Year': { - value: d.getFullYear(), - min: 2019, - max: 2100, - onchange: function (v) { - d.setFullYear(v); - } } }; return E.showMenu(timemenu); diff --git a/apps/showimg/metadata.json b/apps/showimg/metadata.json index d5e44c0ee..e4dbc5738 100644 --- a/apps/showimg/metadata.json +++ b/apps/showimg/metadata.json @@ -7,6 +7,7 @@ "icon": "app.png", "tags": "tool", "supports" : ["BANGLEJS2"], + "readme": "README.md", "storage": [ {"name":"showimg.app.js","url":"app.js"}, {"name":"showimg.img","url":"app-icon.js","evaluate":true} diff --git a/apps/sleepphasealarm/ChangeLog b/apps/sleepphasealarm/ChangeLog index dbc3a0b82..208058472 100644 --- a/apps/sleepphasealarm/ChangeLog +++ b/apps/sleepphasealarm/ChangeLog @@ -1,3 +1,9 @@ 0.01: New App! 0.02: Respect Quiet Mode 0.03: Add compatibility for Bangle.js 2 and new firmware, added "Alarm at " for the alarm time +0.04: Read alarms from new scheduling library, account for higher acceleration sensor noise on Bangle.js 2 +0.05: Refactor decodeTime() to scheduling library +0.06: Add logging + use Layout library and display ETA +0.07: Add check for day of week +0.08: Update to new time_utils module diff --git a/apps/sleepphasealarm/README.md b/apps/sleepphasealarm/README.md new file mode 100644 index 000000000..c33c9c807 --- /dev/null +++ b/apps/sleepphasealarm/README.md @@ -0,0 +1,17 @@ +# Sleep Phase Alarm + +The alarm must be in the next 24h. + +The display shows: + +- the current time +- time of the next alarm or timer +- time difference between current time and alarm time (ETA) +- current state of the ESS algorithm, "Sleep" or "Awake", useful for debugging + +## Logging + +For each day of month (1..31) the ESS states are logged. An entry will be overwritten in the next month, e.g. an entry on the 4th May will overwrite an entry on the 4th April. +The logs can be viewed with the download button: + +![](screenshot.jpg) diff --git a/apps/sleepphasealarm/app.js b/apps/sleepphasealarm/app.js index 39f9b59db..febc8a259 100644 --- a/apps/sleepphasealarm/app.js +++ b/apps/sleepphasealarm/app.js @@ -1,6 +1,10 @@ -const BANGLEJS2 = process.env.HWVERSION == 2; //# check for bangle 2 -const alarms = require("Storage").readJSON("alarm.json",1)||[]; +const BANGLEJS2 = process.env.HWVERSION == 2; // check for bangle 2 +const Layout = require("Layout"); +const locale = require('locale'); +const alarms = require("Storage").readJSON("sched.json",1) || []; +const config = require("Storage").readJSON("sleepphasealarm.json",1) || {logs: []}; const active = alarms.filter(a=>a.on); +let logs = []; // Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS): // Marko Borazio, Eugen Berlin, Nagihan Kücükyildiz, Philipp M. Scholl and Kristof Van Laerhoven, "Towards a Benchmark for Wearable Sleep Analysis with Inertial Wrist-worn Sensing Units", ICHI 2014, Verona, Italy, IEEE Press, 2014. @@ -9,12 +13,12 @@ const active = alarms.filter(a=>a.on); // Function needs to be called for every measurement but returns a value at maximum once a second (see winwidth) // start of sleep marker is delayed by sleepthresh due to continous data reading const winwidth=13; -const nomothresh=0.006; +const nomothresh=0.03; // 0.006 was working on Bangle1, but Bangle2 has higher noise. const sleepthresh=600; var ess_values = []; var slsnds = 0; -function calc_ess(val) { - ess_values.push(val); +function calc_ess(acc_magn) { + ess_values.push(acc_magn); if (ess_values.length == winwidth) { // calculate standard deviation over ~1s @@ -29,11 +33,11 @@ function calc_ess(val) { if (nonmot) { slsnds+=1; if (slsnds >= sleepthresh) { - return true; // awake + return true; // sleep } } else { slsnds=0; - return false; // sleep + return false; // awake } } } @@ -42,64 +46,57 @@ function calc_ess(val) { var nextAlarm; active.forEach(alarm => { const now = new Date(); - const alarmHour = alarm.hr/1; - const alarmMinute = Math.round((alarm.hr%1)*60); - var dateAlarm = new Date(now.getFullYear(), now.getMonth(), now.getDate(), alarmHour, alarmMinute); + const time = require("time_utils").decodeTime(alarm.t); + var dateAlarm = new Date(now.getFullYear(), now.getMonth(), now.getDate(), time.h, time.m); if (dateAlarm < now) { // dateAlarm in the past, add 24h dateAlarm.setTime(dateAlarm.getTime() + (24*60*60*1000)); } - if (nextAlarm === undefined || dateAlarm < nextAlarm) { - nextAlarm = dateAlarm; + if ((alarm.dow >> dateAlarm.getDay()) & 1) { // check valid day of week + if (nextAlarm === undefined || dateAlarm < nextAlarm) { + nextAlarm = dateAlarm; + } } }); -function drawString(s, y) { //# replaced x: always centered - g.reset(); //# moved up to prevent blue background - g.clearRect(0, y - 12, 239, y + 8); //# minimized upper+lower clearing - g.setFont("Vector", 20); - g.setFontAlign(0, 0); // align centered - g.drawString(s, g.getWidth() / 2, y); //# set x to center -} +var layout = new Layout({ + type:"v", c: [ + {type:"txt", font:"10%", label:"Sleep Phase Alarm", bgCol:g.theme.bgH, fillx: true, height:Bangle.appRect.h/6}, + {type:"txt", font:"16%", label: ' '.repeat(20), id:"date", height:Bangle.appRect.h/6}, + {type:"txt", font:"12%", label: "", id:"alarm_date", height:Bangle.appRect.h/6}, + {type:"txt", font:"10%", label: ' '.repeat(20), id:"eta", height:Bangle.appRect.h/6}, + {type:"txt", font:"12%", label: ' '.repeat(20), id:"state", height:Bangle.appRect.h/6}, + ] +}, {lazy:true}); function drawApp() { - g.clearRect(0,24,239,215); //# no problem var alarmHour = nextAlarm.getHours(); var alarmMinute = nextAlarm.getMinutes(); if (alarmHour < 10) alarmHour = "0" + alarmHour; if (alarmMinute < 10) alarmMinute = "0" + alarmMinute; - const s = "Alarm at " + alarmHour + ":" + alarmMinute + "\n\n"; //# make distinct to time - E.showMessage(s, "Sleep Phase Alarm"); + layout.alarm_date.label = "Alarm at " + alarmHour + ":" + alarmMinute; + layout.render(); function drawTime() { if (Bangle.isLCDOn()) { const now = new Date(); - var nowHour = now.getHours(); - var nowMinute = now.getMinutes(); - var nowSecond = now.getSeconds(); - if (nowHour < 10) nowHour = "0" + nowHour; - if (nowMinute < 10) nowMinute = "0" + nowMinute; - if (nowSecond < 10) nowSecond = "0" + nowSecond; - const time = nowHour + ":" + nowMinute + (BANGLEJS2 ? "" : ":" + nowSecond); //# hide seconds on bangle 2 - drawString(time, BANGLEJS2 ? 85 : 105); //# remove x, adjust height for bangle 2 an newer firmware + layout.date.label = locale.time(now, BANGLEJS2 && Bangle.isLocked() ? 1 : 0); // hide seconds on bangle 2 + const diff = nextAlarm - now; + const diffHour = Math.floor((diff % 86400000) / 3600000).toString(); + const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString(); + layout.eta.label = "ETA: -"+ diffHour + ":" + diffMinutes.padStart(2, '0'); + layout.render(); } } - if (BANGLEJS2) { - drawTime(); - setTimeout(_ => { - drawTime(); - setInterval(drawTime, 60000); - }, 60000 - Date.now() % 60000); //# every new minute on bangle 2 - } else { - setInterval(drawTime, 500); // 2Hz - } + drawTime(); + setInterval(drawTime, 500); // 2Hz } var buzzCount = 19; function buzz() { if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence - Bangle.setLCDPower(1); - Bangle.buzz().then(()=>{ + Bangle.setLCDPower(1); + Bangle.buzz().then(()=>{ if (buzzCount--) { setTimeout(buzz, 500); } else { @@ -109,39 +106,58 @@ function buzz() { }); } +function addLog(time, type) { + logs.push({time: time, type: type}); + require("Storage").writeJSON("sleepphasealarm.json", config); +} + // run var minAlarm = new Date(); var measure = true; if (nextAlarm !== undefined) { - Bangle.loadWidgets(); //# correct widget load draw order + config.logs[nextAlarm.getDate()] = []; // overwrite log on each day of month + logs = config.logs[nextAlarm.getDate()]; + g.clear(); + Bangle.loadWidgets(); Bangle.drawWidgets(); + let swest_last; // minimum alert 30 minutes early minAlarm.setTime(nextAlarm.getTime() - (30*60*1000)); - setInterval(function() { + Bangle.on('accel', (accelData) => { // 12.5Hz const now = new Date(); - const acc = Bangle.getAccel().mag; + const acc = accelData.mag; const swest = calc_ess(acc); if (swest !== undefined) { if (Bangle.isLCDOn()) { - drawString(swest ? "Sleep" : "Awake", BANGLEJS2 ? 150 : 180); //# remove x, adjust height + layout.state.label = swest ? "Sleep" : "Awake"; + layout.render(); + } + // log + if (swest_last != swest) { + if (swest) { + addLog(new Date(now - sleepthresh*13/12.5*1000), "sleep"); // calculate begin of no motion phase, 13 values/second at 12.5Hz + } else { + addLog(now, "awake"); + } + swest_last = swest; } } if (now >= nextAlarm) { // The alarm widget should handle this one + addLog(now, "alarm"); setTimeout(load, 1000); } else if (measure && now >= minAlarm && swest === false) { + addLog(now, "alarm"); buzz(); measure = false; } - }, 80); // 12.5Hz + }); drawApp(); } else { E.showMessage('No Alarm'); setTimeout(load, 1000); } -// BTN2 to menu, BTN3 to main # on bangle 2 only BTN to main -if (!BANGLEJS2) setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); setWatch(() => load(), BANGLEJS2 ? BTN : BTN3, { repeat: false, edge: "falling" }); diff --git a/apps/sleepphasealarm/interface.html b/apps/sleepphasealarm/interface.html new file mode 100644 index 000000000..9a7cb0f93 --- /dev/null +++ b/apps/sleepphasealarm/interface.html @@ -0,0 +1,108 @@ + + + + + + +

Please select a wakeup day:

+
+ +
+
+ +
+ + + + + + + diff --git a/apps/sleepphasealarm/metadata.json b/apps/sleepphasealarm/metadata.json index ed0f21028..c74a617ab 100644 --- a/apps/sleepphasealarm/metadata.json +++ b/apps/sleepphasealarm/metadata.json @@ -2,13 +2,17 @@ "id": "sleepphasealarm", "name": "SleepPhaseAlarm", "shortName": "SleepPhaseAlarm", - "version": "0.03", + "version": "0.08", "description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.", "icon": "app.png", "tags": "alarm", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "dependencies": {"scheduler":"type"}, "storage": [ {"name":"sleepphasealarm.app.js","url":"app.js"}, {"name":"sleepphasealarm.img","url":"app-icon.js","evaluate":true} - ] + ], + "data": [{"name":"sleepphasealarm.json","storageFile":true}], + "interface": "interface.html" } diff --git a/apps/sleepphasealarm/screenshot.jpg b/apps/sleepphasealarm/screenshot.jpg new file mode 100644 index 000000000..b1fd05dec Binary files /dev/null and b/apps/sleepphasealarm/screenshot.jpg differ diff --git a/apps/slomoclock/app-icon.js b/apps/slomoclock/app-icon.js index 22e264124..46f668745 100644 --- a/apps/slomoclock/app-icon.js +++ b/apps/slomoclock/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("oFAwhC/ABOIABgfymYAKD+Z/9hGDL5c4wAf/XzjASTxqgQhAfPMB2IPxiACIBo+BDxqACIBg+CLxpANHwQPBABgvCIBT8CJ5owDD5iPOOAQfLBojiDCYQGFGIQfICIQfdBYJNMOI6SHD8jeNOIYzID8hfRD9LfEAoTdFBIifLAAIffBoQRBAJpxMD84JCD+S/GL56fID8ALBb6ZhID8qtJCZ4fgT4YDBABq/PD7RNEL6IRKD8WID5pfCD5kzNhKSFmYfMBwSeOGBoPDABgvCJ5wAON5pADABivPIAIAOd5xABABweOD4J+OD58IQBj8LD/6gUDyAfhXzgfiP/wA2")) +require("heatshrink").decompress(atob("mEw4UA///7k8//GnldDZ9RosUqNABQsFqoACqALFg2qAAWQBaMVEYdUBYseC4e0BYsaBYekBYt6BYetBYouDAAIKEgPqC4erNgkFBQYABNgke2oiDrxIEvXUBYcXHgl7FIkB9oEDBYKYBTwILEi4LCoEBBYUQHQX7EYyRCBYJrF95ICBYNFBQdRBYcWEYwLDit7otUHQMVqIvDL4L5BgL8CI4YLDqILDO4gXGBQUEEZQ7CEYprEI4prFoLGBqkFoILFNZaPFF4ZHCR4hrFa5ILMfYJeDfYse2ovDrxGCAAMF1QAEMgIpD9QKD1Y1EgBfFBQg8BC4Y6EAAMaBYekBYseBYZGESIQuDfIYACgwXDyALRgojDNQhsCBYZqFABI=")) diff --git a/apps/smclock/ChangeLog b/apps/smclock/ChangeLog index 0300d5ceb..2a3874d34 100644 --- a/apps/smclock/ChangeLog +++ b/apps/smclock/ChangeLog @@ -1,4 +1,6 @@ 0.01: Initial version 0.02: Add battery level -0.03: Fix battery display when full +0.03: Fix battery display when full (incorporating code by Ronin0000) 0.04: Add support for settings +0.05: Add ability to change background (3bit or 4bit) +0.06: Replace battery text with image diff --git a/apps/smclock/README.md b/apps/smclock/README.md index 635292d0c..2fc239ab2 100644 --- a/apps/smclock/README.md +++ b/apps/smclock/README.md @@ -4,13 +4,16 @@ Just a simple watch face for the Banglejs2. It shows battery level in the upper left corner, date information in the upper right, and time information in the bottom. -![](screenshot.png) +![](screenshot0.png) +![](screenshot1.png) ## Settings -**Analog Clock:** +**Analog Clock:** *Not yet implemented.* -**Human Readable Date:** When the setting is on, the date is shown in a more human-friendly format (e.g. "Oct 2"), otherwise the date is shown in a standard format (e.g. "02/10"). Default is off. +**Background:** When the setting is set as "3bit", a background with more accurate colors is chosen for the watchface. Otherwise, it uses a background following the 16-bit Mac Color Palette. + +**Date Format:** When the setting is set as "Long", the date is shown in a more human-friendly format (e.g. "Oct 2"), otherwise the date is shown in a standard format (e.g. "02/10"). Default is off. **Show Week Info:** When the setting is on, the weekday and week number are shown in the upper right box. When the setting is off, the full year is shown instead. Default is off. @@ -20,4 +23,4 @@ It shows battery level in the upper left corner, date information in the upper r Monogram Watch Face can be selected as the default clock or it can be run manually from the launcher. Its settings can be accessed and changed via the relevant menu. -Tapping on the "Alerts" area will replace the current time display with the time of the most immediate alert. +*Tapping on the "Alerts" area will replace the current time display with the time of the most immediate alert.* - *Feature not implemented yet.* diff --git a/apps/smclock/app.js b/apps/smclock/app.js index 350c0dd07..41bc2b5e4 100644 --- a/apps/smclock/app.js +++ b/apps/smclock/app.js @@ -1,23 +1,23 @@ const SETTINGSFILE = "smclock.json"; -const background = { - width: 176, - height: 176, - bpp: 3, - transparent: 1, - buffer: require("heatshrink").decompress( - atob( - "/4A/AH4ACUb8H9MkyVJAThB/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INP/AH4A/AAX8Yz4Afn5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/INI=" - ) - ), +const image3bit = { + width : 176, height : 176, bpp : 3, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/4A/AH4AC23btoCct/pkmSpICcIP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5Bp/4A/AH4AC/kAAH0/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5B/IP5BpA=")) +}; +const image4bit = { + width : 176, height : 176, bpp : 4, + transparent : 1, + buffer : require("heatshrink").decompress(atob("/4A/AH4Au1QAp1/2swApK/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K/5X/K+//AH4A/AF8AAH4AUK/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/AA==")) }; const monthName = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; const weekday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; // dynamic variables var batLevel = -1; -var batColor = [0, 0, 0]; +var batColor = ""; // settings variables +var backgroundImage; var dateFormat; var drawInterval; var pollInterval; @@ -31,6 +31,7 @@ function loadSettings() { function def(value, def) {return value !== undefined ? value : def;} var settings = require("Storage").readJSON(SETTINGSFILE, true) || {}; + backgroundImage = def(settings.backgroundImage, "3bit"); dateFormat = def(settings.dateFormat, "Short"); drawInterval = def(settings.drawInterval, 10); pollInterval = def(settings.pollInterval, 60); @@ -67,23 +68,29 @@ function getBatteryColor(level) { level = batLevel; } if (level > 80) { - color = [0, 0, 1]; + color = "#00f"; } else if (level > 60) { - color = [0, 1, 1]; + color = "#0ff"; } else if (level > 40) { - color = [0, 1, 0]; + color = "#0f0"; } else if (level > 20) { - color = [1, 1, 0]; + color = "#f40"; } else { - color = [1, 0, 0]; + color = "f00"; } return color; } function draw() { + var background; + if (backgroundImage == "3bit") { + background = image3bit; + } else { + background = image4bit; + } g.drawImage(background); - const color = getBatteryColor(batLevel); + batColor = getBatteryColor(batLevel); var bat = ""; const d = new Date(); const day = d.getDate(); @@ -95,32 +102,38 @@ function draw() { const m = d.getMinutes(); const time = d02(h) + ":" + d02(m); - if (E.getBattery() < 100) { - bat = d02(E.getBattery()) + "%"; - } else { - bat = E.getBattery() + "%"; - } - g.reset(); // draw battery info - g.setColor(1, 1, 1); + var x = 12; + var y = 16; + if (Bangle.isCharging()) { + g.setColor("#ff0").drawImage(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y); + } else { + g.clearRect(x,y,x+14,y+24); + g.setColor("#000").fillRect(x+2,y+2,x+12,y+22).clearRect(x+4,y+4,x+10,y+20).fillRect(x+5,y+1,x+9,y+2); + g.setColor(batColor).fillRect(x+4,y+20-(batLevel*16/100),x+10,y+20); + } + if (Bangle.isCharging()) { + g.setColor("#ff0"); + } else { + g.setColor(batColor); + } if (useVectorFont == true) { g.setFont("Vector", 16); - g.drawString("Bat:", 12, 22, false); } else { - g.setFont("4x6", 2); - g.drawString("Bat:", 10, 22, false); + g.setFont("4x6", 3); } - g.setColor(color[0], color[1], color[2]); if (batLevel < 100) { - g.drawString(bat, 52, 22, false); + bat = d02(batLevel) + "%"; + g.drawString(bat, 50, 22, false); } else { - g.drawString(bat, 46, 22, false); + bat = "100%"; + g.drawString(bat, 40, 22, false); } // draw date info - g.setColor(0, 0, 0); + g.setColor("#000"); if (useVectorFont == true) { g.setFont("Vector", 20); } else { @@ -136,7 +149,7 @@ function draw() { // draw week info if (showWeekInfo == true) { - date2 = weekday[d.getDay()] + " " + d02(week) + date2 = weekday[d.getDay()] + " " + d02(week); if (useVectorFont == true) { g.setFont("Vector", 18); } else { @@ -155,7 +168,7 @@ function draw() { } // draw time - g.setColor(1, 1, 1); + g.setColor("#fff"); if (useVectorFont == true) { g.setFont("Vector", 60); g.drawString(time, 10, 108, false); diff --git a/apps/smclock/metadata.json b/apps/smclock/metadata.json index cc995d587..ca40193a2 100644 --- a/apps/smclock/metadata.json +++ b/apps/smclock/metadata.json @@ -3,13 +3,13 @@ "name": "Monogram Watch Face", "shortName": "MonoClock", "icon": "app.png", - "screenshots": [{ "url": "screenshot.png" }], - "version": "0.04", + "screenshots": [{ "url": "screenshot0.png" }, {"url": "screenshot1.png" }], + "version": "0.06", "description": "A simple watchface based on my stylised monogram.", "type": "clock", "tags": "clock", "readme": "README.md", - "supports": ["BANGLEJS", "BANGLEJS2"], + "supports": ["BANGLEJS2"], "allow_emulator": true, "storage": [ { "name": "smclock.app.js", "url": "app.js" }, diff --git a/apps/smclock/screenshot.png b/apps/smclock/screenshot.png deleted file mode 100644 index c0e0bd0ee..000000000 Binary files a/apps/smclock/screenshot.png and /dev/null differ diff --git a/apps/smclock/screenshot0.png b/apps/smclock/screenshot0.png new file mode 100644 index 000000000..07eff8ddf Binary files /dev/null and b/apps/smclock/screenshot0.png differ diff --git a/apps/smclock/screenshot1.png b/apps/smclock/screenshot1.png new file mode 100644 index 000000000..da25b2579 Binary files /dev/null and b/apps/smclock/screenshot1.png differ diff --git a/apps/smclock/settings.js b/apps/smclock/settings.js index a6c7d1b98..ee4a35a26 100644 --- a/apps/smclock/settings.js +++ b/apps/smclock/settings.js @@ -52,6 +52,7 @@ writeSettings(); }, }, + "Background": stringInSettings("backgroundImage", ["3bit", "4bit"]), Date: stringInSettings("dateFormat", ["Long", "Short"]), "Draw Interval": { value: settings.drawInterval, diff --git a/apps/smpltmr/ChangeLog b/apps/smpltmr/ChangeLog new file mode 100644 index 000000000..bf128e2fb --- /dev/null +++ b/apps/smpltmr/ChangeLog @@ -0,0 +1,2 @@ +0.01: Release +0.02: Rewrite with new interface \ No newline at end of file diff --git a/apps/smpltmr/README.md b/apps/smpltmr/README.md new file mode 100644 index 000000000..eeb48d338 --- /dev/null +++ b/apps/smpltmr/README.md @@ -0,0 +1,18 @@ +# Simple Timer + +A simple app to set a timer quickly. Drag or tap on the up and down buttons over the hour, minute or second to set the time. + +This app uses the `sched` library, which allows the timer to continue to run in the background when this app is closed. + +![](screenshot_1.png) +![](screenshot_2.png) +![](screenshot_3.png) +![](screenshot_4.png) + +# Creators +[David Peer](https://github.com/peerdavid) + +[Sir Indy](https://github.com/sir-indy) + +# Thanks to... +Time icon created by CreativeCons - Flaticon \ No newline at end of file diff --git a/apps/smpltmr/app-icon.js b/apps/smpltmr/app-icon.js new file mode 100644 index 000000000..bc4261459 --- /dev/null +++ b/apps/smpltmr/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwMAg//AAXgApcAvAZBhwCBuAFuGoUeAQM4AQM8AQl8Bwn4AQMPgEB+AFBg+AgZYBgED4AHBAoIPBCYIAC/AfCGwQrCGAQ3CAAMcIYQFCJ4QABnoREvIdE/eeAgUB+fPAoUD8/nIIUHz/zJoUPn/5LIUev/8MoU8//+OIU5XwO8AoN7AoPeAoNzAoPOAsrFKg4QBAAPgApYA==")) \ No newline at end of file diff --git a/apps/smpltmr/app.js b/apps/smpltmr/app.js new file mode 100644 index 000000000..4e95d3a30 --- /dev/null +++ b/apps/smpltmr/app.js @@ -0,0 +1,190 @@ +/* + * SIMPLE TIMER + * + * Creator: David Peer + * Date: 02/2022 + * + * Modified: Sir Indy + * Date: 05/2022 + */ + +const Layout = require("Layout"); +const alarm = require("sched") +const TIMER_IDX = "smpltmr"; + +const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)}); +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +function formatTime(s) { + var t = secondsToTime(s); + if (t.h) { + return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2); + } else { + return t.m + ':' + ("0" + t.s).substr(-2); + } +} + +var seconds = 5 * 60; // Default to 5 minutes +var drawTimeout; +//var timerRunning = false; +function timerRunning() { + return (alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX)) != undefined) +} +const imgArrow = atob("CQmBAAgOBwfD47ndx+OA"); +const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w=="); +const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA=="); + +function onDrag(event) { + if (!timerRunning()) { + Bangle.buzz(40, 0.3); + var diff = -Math.round(event.dy/5); + if (event.x < timePickerLayout.hours.w) { + diff *= 3600; + } else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) { + diff *= 60; + } + updateTimePicker(diff); + } +} + +function onTouch(button, xy) { + if (xy.y > (timePickerLayout.btnStart.y||timerLayout.btnStart.y)) { + Bangle.buzz(40, 0.3); + onButton(); + return; + } + if (!timerRunning()) { + var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2; + var diff = 0; + Bangle.buzz(40, 0.3); + if (xy.y > 24 && xy.y < touchMidpoint - 10) { + diff = 1; + } else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) { + diff = -1; + } + if (xy.x < timePickerLayout.hours.w) { + diff *= 3600; + } else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) { + diff *= 60; + } + updateTimePicker(diff); + } + +} + +function onButton() { + g.clearRect(Bangle.appRect); + if (timerRunning()) { + timerStop(); + } else { + if (seconds > 0) { + timerRun(); + } + } +} + +function updateTimePicker(diff) { + seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1); + var set_time = secondsToTime(seconds); + updateLayoutField(timePickerLayout, 'hours', set_time.h); + updateLayoutField(timePickerLayout, 'mins', set_time.m); + updateLayoutField(timePickerLayout, 'secs', set_time.s); +} + +function updateTimer() { + var timeToNext = alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX)); + updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000)); + queueDraw(1000); +} + +function queueDraw(millisecs) { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + updateTimer(); + }, millisecs - (Date.now() % millisecs)); +} + +function timerRun() { + alarm.setAlarm(TIMER_IDX, { + vibrate : ".-.-", + hidden: true, + timer : seconds * 1000 + }); + alarm.reload(); + g.clearRect(Bangle.appRect); + timerLayout.render(); + updateTimer(); +} + +function timerStop() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + var timeToNext = alarm.getTimeToAlarm(alarm.getAlarm(TIMER_IDX)); + if (timeToNext != undefined) { + seconds = timeToNext / 1000; + } + alarm.setAlarm(TIMER_IDX, undefined); + alarm.reload(); + g.clearRect(Bangle.appRect); + timePickerLayout.render(); + updateTimePicker(); +} + +var timePickerLayout = new Layout({ + type:"v", c: [ + {type:undefined, height:2}, + {type:"h", c: [ + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Hours"}, + {type:"img", pad:8, src:imgArrow}, + {type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, r:2} + ]}, + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Minutes"}, + {type:"img", pad:8, src:imgArrow}, + {type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, r:2} + ]}, + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Seconds"}, + {type:"img", pad:8, src:imgArrow}, + {type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, r:2} + ]}, + ]}, + {type:"btn", src:imgPlay, id:"btnStart", fillx:1 } + ], filly:1 +}); + +var timerLayout = new Layout({ + type:"v", c: [ + {type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 }, + {type:"btn", src:imgPause, id:"btnStart", cb: l=>timerStop(), fillx:1 } + ], filly:1 +}); + +function updateLayoutField(layout, field, value) { + layout.clear(layout[field]); + layout[field].label = value; + layout.render(layout[field]); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +Bangle.setUI({ + mode : "custom", + touch : function(n,e) {onTouch(n,e);}, + drag : function(e) {onDrag(e);}, + btn : function(n) {onButton();}, +}); + +g.clearRect(Bangle.appRect); +if (timerRunning()) { + timerLayout.render(); + updateTimer(); +} else { + timePickerLayout.render(); + updateTimePicker(); +} diff --git a/apps/smpltmr/app.png b/apps/smpltmr/app.png new file mode 100644 index 000000000..04ed03751 Binary files /dev/null and b/apps/smpltmr/app.png differ diff --git a/apps/smpltmr/metadata.json b/apps/smpltmr/metadata.json new file mode 100644 index 000000000..cb1ef6eab --- /dev/null +++ b/apps/smpltmr/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "smpltmr", + "name": "Simple Timer", + "shortName": "Simple Timer", + "version": "0.02", + "description": "A very simple app to start a timer.", + "icon": "app.png", + "tags": "tool,alarm,timer", + "dependencies": {"scheduler":"type"}, + "supports": ["BANGLEJS2"], + "screenshots": [{"url":"screenshot_1.png"}, {"url": "screenshot_2.png"}, {"url": "screenshot_3.png"}, {"url": "screenshot_4.png"}], + "readme": "README.md", + "storage": [ + {"name":"smpltmr.app.js","url":"app.js"}, + {"name":"smpltmr.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/smpltmr/screenshot_1.png b/apps/smpltmr/screenshot_1.png new file mode 100644 index 000000000..54eb9d20c Binary files /dev/null and b/apps/smpltmr/screenshot_1.png differ diff --git a/apps/smpltmr/screenshot_2.png b/apps/smpltmr/screenshot_2.png new file mode 100644 index 000000000..fb0145f17 Binary files /dev/null and b/apps/smpltmr/screenshot_2.png differ diff --git a/apps/smpltmr/screenshot_3.png b/apps/smpltmr/screenshot_3.png new file mode 100644 index 000000000..efa10d9c1 Binary files /dev/null and b/apps/smpltmr/screenshot_3.png differ diff --git a/apps/smpltmr/screenshot_4.png b/apps/smpltmr/screenshot_4.png new file mode 100644 index 000000000..c0f984378 Binary files /dev/null and b/apps/smpltmr/screenshot_4.png differ diff --git a/apps/speedalt/ChangeLog b/apps/speedalt/ChangeLog index 0550f9b86..78c14594b 100644 --- a/apps/speedalt/ChangeLog +++ b/apps/speedalt/ChangeLog @@ -9,3 +9,4 @@ 0.09: Add third screen mode with large clock and waypoint selection display to ease visibility in bright daylight. 0.10: Add Kalman filter to smooth the speed and altitude values. Can be disabled in settings. 0.11: Now also runs on Bangle.js 2 with basic functionality +0.12: Full functionality on Bangle.js 2: Bangle.js 1 buttons mapped to touch areas. diff --git a/apps/speedalt/README.md b/apps/speedalt/README.md index c21828aff..6f0d4efe5 100644 --- a/apps/speedalt/README.md +++ b/apps/speedalt/README.md @@ -2,23 +2,21 @@ You can switch between three display modes. One showing speed and altitude (A), one showing speed and distance to waypoint (D) and a large dispay of time and selected waypoint. -*Note for **Bangle.js 2:** Currently only the BTN3 functionality is working with the Bangle.js 2 button.* - Within the [A]ltitude and [D]istance displays modes one figure is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed. The waypoints list is the same as that used with the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app so the same set of waypoints can be used across both apps. Refer to that app for waypoint file information. ## Buttons and Controls -BTN3 : Cycles the modes between Speed+[A]ltitude, Speed+[D]istance and large Time/Waypoint +*(Mapping for **Bangle.js 2**: BTN2 = Touch upper right side; BTN3 = Touch lower right side; BTN4 = Touch left side)* -***Bangle.js 2:** Currently only this button function is working* +BTN3 : Cycles the modes between Speed+[A]ltitude, Speed+[D]istance and large Time/Waypoint ### [A]ltitude mode BTN1 : Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum speed/alt values recorded. -BTN1 : Long press > 2 secs resets the recorded maximum values. +BTN1 : Long press > 2 secs resets the recorded maximum values. *(Bangle.js 2: Long press > 0.4 secs)* ### [D]istance mode @@ -32,7 +30,7 @@ BTN1 : Select next waypoint. BTN2 : Disables/Restores power saving timeout. Locks the screen on and GPS in SuperE mode to enable reading for longer periods but uses maximum battery drain. Red LED (dot) at top of screen when screen is locked on. Press again to restore power saving timeouts. -BTN3 : Long press exit and return to watch. +BTN3 : Long press exit and return to watch. *(Bangle.js 2: Long press BTN > 2 secs)* BTN4 : Left Display Tap : Swaps which figure is in the large display. You can have either speed or [A]ltitude/[D]istance on the large primary display. diff --git a/apps/speedalt/app.js b/apps/speedalt/app.js index f979762f1..79db932db 100644 --- a/apps/speedalt/app.js +++ b/apps/speedalt/app.js @@ -349,7 +349,7 @@ function drawSecondary(n,u) { s = 30; // Font size if (BANGLEJS2) s *= fontFactorB2; buf.setFontVector(s); - buf.drawString(u,xu - (BANGLEJS2*20),screenH_TwoThirds-25); + buf.drawString(u,xu - (BANGLEJS2*xu/5),screenH_TwoThirds-25); } function drawTime() { @@ -391,7 +391,7 @@ function drawWP() { // from waypoints.json - see README.md buf.setFontAlign(-1,1); //left, bottom if (BANGLEJS2) s *= fontFactorB2; buf.setFontVector(s); - buf.drawString(nm.substring(0,6),72,screenH_TwoThirds-(BANGLEJS2 * 20)); + buf.drawString(nm.substring(0,6),72,screenH_TwoThirds-(BANGLEJS2 * 15)); } if ( cfg.modeA == 2 ) { // clock/large mode @@ -421,7 +421,7 @@ function drawSats(sats) { buf.drawString('A',screenW,140-(BANGLEJS2 * 40)); if ( showMax ) { buf.setFontAlign(0,1); //centre, bottom - buf.drawString('MAX',120,164); + buf.drawString('MAX',screenW_Half,screenH_TwoThirds + 4); } } if ( cfg.modeA == 0 ) buf.drawString('D',screenW,140-(BANGLEJS2 * 40)); @@ -536,22 +536,18 @@ function onGPS(fix) { } -function setButtons(){ -if (!BANGLEJS2) { // Buttons for Bangle.js - // Spd+Dist : Select next waypoint - setWatch(function(e) { - var dur = e.time - e.lastTime; - if ( cfg.modeA == 1 ) { - // Spd+Alt mode - Switch between fix and MAX - if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display - else { max.spd = 0; max.alt = 0; } // Long press resets max values. - } - else nxtWp(1); // Spd+Dist or Clock mode - Select next waypoint - onGPS(lf); - }, BTN1, { edge:"falling",repeat:true}); - // Power saving on/off - setWatch(function(e){ +function btn1press(longpress) { + if(emulator) console.log("Btn1, long="+longpress); + if ( cfg.modeA == 1 ) { // Spd+Alt mode - Switch between fix and MAX + if ( !longpress ) showMax = !showMax; // Short press toggle fix/max display + else { max.spd = 0; max.alt = 0; } // Long press resets max values. + } + else nxtWp(1); // Spd+Dist or Clock mode - Select next waypoint + onGPS(lf); + } +function btn2press(){ + if(emulator) console.log("Btn2"); pwrSav=!pwrSav; if ( pwrSav ) { LED1.reset(); @@ -564,52 +560,51 @@ if (!BANGLEJS2) { // Buttons for Bangle.js Bangle.setLCDPower(1); LED1.set(); } - }, BTN2, {repeat:true,edge:"falling"}); - - // Toggle between alt or dist - setWatch(function(e){ - cfg.modeA = cfg.modeA+1; - if ( cfg.modeA > 2 ) cfg.modeA = 0; - savSettings(); - onGPS(lf); - }, BTN3, {repeat:true,edge:"falling"}); - - // Touch left screen to toggle display - setWatch(function(e){ - cfg.primSpd = !cfg.primSpd; - savSettings(); - onGPS(lf); // Update display - }, BTN4, {repeat:true,edge:"falling"}); - -} else { // Buttons for Bangle.js 2 - setWatch(function(e){ // Bangle.js BTN3 + } +function btn3press(){ + if(emulator) console.log("Btn3"); cfg.modeA = cfg.modeA+1; if ( cfg.modeA > 2 ) cfg.modeA = 0; if(emulator)console.log("cfg.modeA="+cfg.modeA); savSettings(); onGPS(lf); - }, BTN1, {repeat:true,edge:"falling"}); - -/* Bangle.on('tap', function(data) { // data - {dir, double, x, y, z} + } +function btn4press(){ + if(emulator) console.log("Btn4"); cfg.primSpd = !cfg.primSpd; - if(emulator)console.log("!cfg.primSpd"); - }); */ + savSettings(); + onGPS(lf); // Update display + } -/* Bangle.on('swipe', function(dir) { - if (dir < 0) { // left: Bangle.js BTN3 - cfg.modeA = cfg.modeA+1; - if ( cfg.modeA > 2 ) cfg.modeA = 0; - if(emulator)console.log("cfg.modeA="+cfg.modeA); - } + +function setButtons(){ +if (!BANGLEJS2) { // Buttons for Bangle.js 1 + setWatch(function(e) { + btn1press(( e.time - e.lastTime) > 2); // > 2 sec. is long press + }, BTN1, { edge:"falling",repeat:true}); + + // Power saving on/off (red dot visible if off) + setWatch(btn2press, BTN2, {repeat:true,edge:"falling"}); + + // Toggle between alt or dist + setWatch(btn3press, BTN3, {repeat:true,edge:"falling"}); + + // Touch left screen to toggle display + setWatch(btn4press, BTN4, {repeat:true,edge:"falling"}); + +} else { // Buttons for Bangle.js 2 + setWatch(function(e) { + btn1press(( e.time - e.lastTime) > 0.4); // > 0.4 sec. is long press + }, BTN1, { edge:"falling",repeat:true}); + + Bangle.on('touch', function(btn_l_r, e) { + if(e.x < screenW_Half) btn4press(); else - { // right: Bangle.js BTN4 - cfg.primSpd = !cfg.primSpd; - if(emulator)console.log("!cfg.primSpd"); - } + if (e.y < screenH_Half) + btn2press(); + else + btn3press(); }); -*/ - savSettings(); - onGPS(lf); } } @@ -700,18 +695,6 @@ Bangle.on('lcdPower',function(on) { else stopDraw(); }); -/* -function onGPSraw(nmea) { - var nofGP = 0, nofBD = 0, nofGL = 0; - if (nmea.slice(3,6) == "GSV") { - // console.log(nmea.slice(1,3) + " " + nmea.slice(11,13)); - if (nmea.slice(0,7) == "$GPGSV,") nofGP = Number(nmea.slice(11,13)); - if (nmea.slice(0,7) == "$BDGSV,") nofBD = Number(nmea.slice(11,13)); - if (nmea.slice(0,7) == "$GLGSV,") nofGL = Number(nmea.slice(11,13)); - SATinView = nofGP + nofBD + nofGL; - } } -if(BANGLEJS2) Bangle.on('GPS-raw', onGPSraw); -*/ var gpssetup; try { diff --git a/apps/speedalt/metadata.json b/apps/speedalt/metadata.json index 617ac4b8e..e03d23c8b 100644 --- a/apps/speedalt/metadata.json +++ b/apps/speedalt/metadata.json @@ -2,7 +2,7 @@ "id": "speedalt", "name": "GPS Adventure Sports", "shortName": "GPS Adv Sport", - "version": "0.11", + "version": "0.12", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", diff --git a/apps/stardateclock/ChangeLog b/apps/stardateclock/ChangeLog new file mode 100644 index 000000000..431463bc8 --- /dev/null +++ b/apps/stardateclock/ChangeLog @@ -0,0 +1 @@ +0.01: Initial release on the app repository for Bangle.js 1 and 2 diff --git a/apps/stardateclock/README.md b/apps/stardateclock/README.md new file mode 100644 index 000000000..2b5da3a41 --- /dev/null +++ b/apps/stardateclock/README.md @@ -0,0 +1,23 @@ +# Stardate Clock + +A clock face displaying a stardate along with a "standard" digital/analog clock +in LCARS design. + +That design has been made popular by various Star Trek shows. Credits for the +original LCARS designs go to Michael Okuda, copyrights are owned by Paramount Global, +usage of that type of design is permitted freely for non-profit use cases. +The Bangle.js version has been created by Robert Kaiser . + +The stardate concept used leans on the shows released from the late 80s onward +by using 1000 units per Earth year, but to apply this more cleanly, this split +is applied exactly. Also, to give more relationship to the shows and +incidentally make values look similar to those depicted there, the zero point +is set to the first airing of the original 'Star Trek' series in the US on +Thursday, September 8, 1966, at 8:30 p.m. Eastern Time. + +The clock face supports Bangle.js 1 and 2 with some compromises (e.g. the +colors will look best on Bangle.js 1, the font sizes will look best on +Bangle.js 2). + +Any tap on the diaply while unlocked switches the "standard" Earth-style clock +between digital and analog display. diff --git a/apps/stardateclock/app-icon.js b/apps/stardateclock/app-icon.js new file mode 100644 index 000000000..d38013a98 --- /dev/null +++ b/apps/stardateclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgkiAA0gDRwX/C/4X/C5MO9wBDgnkAIMAAQQXKAItehwECAQIXK8gBEIQIeDC5YAF8EAAIIECC48hE4oYCAogXIkQvHDIgCBiQXHiCPFAIaaCgECJBChDAIsOU4RIJbJwwIDIVEABIYBMJAXOAC8SmYAHmHdABJfBCxAXNCpEyRxoWVgETC46+OkYXHRpxGWC5EwBQMBkQDBiK4DKQMBiAXKNQMggQ2CgI7CkcgC5UjicwkYXCgUxmakBC5kCmERC4MiAoMjgMTC50AC4KYCkcAgYXRPgJFBC6YABgYEBC6iQBC6cRgMgL6ikBR4IXOiR3EX4IXPAgTXDBgIXNgUiiClCAAMikIKBC5YAMC64AXogAGoAX/C6w")) \ No newline at end of file diff --git a/apps/stardateclock/app.js b/apps/stardateclock/app.js new file mode 100644 index 000000000..70f1070fc --- /dev/null +++ b/apps/stardateclock/app.js @@ -0,0 +1,362 @@ +// Stardate clock face, by KaiRo.at, 2021-2022 + +var redrawClock = true; +var clockface = "digital"; + +// note: Bangle.js 1 has 240x240x16, 2 has 176x176x3 screen +var bpp = g.getBPP ? g.getBPP() : 16; + +// Load fonts +Graphics.prototype.setFontAntonio27 = function(scale) { + // Actual height 23 (23 - 1) + g.setFontCustom(atob("AAAAAAGAAAAwAAAGAAAAwAAAGAAAAwAAAAAAAAAAAAAAAAAADAAAA4AAAHAAAAAAAAAAAAAAAAAAAAAA4AAB/AAD/4AH/4AP/wAf/gAD/AAAeAAAAAAAAAAAAA///AP//+D///4eAAPDgAA4cAAHD///4P//+A///gAAAAAAAAAAAAAAYAAAHAAAA4AAAOAAAD///4f///D///4AAAAAAAAAAAAAAAAAAAAAAA/gD4P8B/D/g/4cAfzDgP4Yf/8DD/+AYP/ADAGAAAAAAAAAAAAHwD8B+AfwfwD/DgMA4cDgHDgeA4f///B/3/wH8P8AAAAAAAAAAAAOAAAPwAAP+AAP/wAf8OAf4BwD///4f///D///4AABwAAAGAAAAAAAAAAAAAAD/4Pwf/h/D/4P4cMAHDjgA4cf//Dh//4cH/8AAAAAAAAAAAAAAH//8B///wf///Dg4A4cHAHDg4A4f3//B+f/wHh/8AAAAAAAAAAAAAAcAAADgAA4cAD/DgH/4cH//Dv/4Af/gAD/gAAfAAADgAAAAAAAAAAAAH4f8B///wf///Dg8A4cDAHDg8A4f///B///wH8/8AAAAAAAAAAAAAAH/h4B/+Pwf/5/DgHA4cA4HDgHA4f///B///wH//8AAAAAAAAAAAAAAAAAAAHgeAA8DwAHgeAAAAAAAAAA"), 45, atob("CQcKDAsMDAwMDAwMDAc="), 27+(scale<<8)+(1<<16)); +}; +Graphics.prototype.setFontAntonio42 = function(scale) { + // Actual height 36 (36 - 1) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAHgAAAAAHgAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAfgAAAAH/gAAAB//gAAAf//gAAH//4AAB//+AAAf//gAAH//4AAAf/+AAAAf/gAAAAf4AAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAH////+AP/////Af/////gf/////gfAAAAPgeAAAAHgeAAAAHgfAAAAPgf/////gf/////gP/////AH////+AB////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAB4AAAAAB4AAAAADwAAAAAHwAAAAAP/////gf/////gf/////gf/////gf/////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AAPgH/8AD/gP/8AP/gP/8A//gf/8B//gfAAH/ngeAAf+HgeAB/4HgfAH/gHgf//+AHgP//4AHgH//wAHgD/+AAHgAPgAAAAAAAAAAAAAAAAAAAAAAAAAA+AAfwAH+AAf+AP+AAf/AP+AAf/Af+AAf/gfADwAPgeADwAHgeADwAHgfAH4APgf///h/gf/////AP/+///AH/+f/+AB/4H/4AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAA/gAAAAH/gAAAB//gAAAP//gAAB//HgAAf/wHgAD/8AHgAf/AAHgAf/////gf/////gf/////gf/////gf/////gAAAAHgAAAAAHgAAAAAHAAAAAAAAAAAAAAAAAAAAAAAf//gP8Af//gP+Af//gP/Af//gP/gf/+AAfgeB8AAHgeB4AAHgeB8AAHgeB////geB////geA////AeAf//+AAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///gAD////8AH/////AP/////Af/////gfAPgAfgeAPAAHgeAPAAHgeAPAAHgf+PgAPgf+P///gP+H///AH+H//+AB+B//8AAAAD8AAAAAAAAAAAAAAAAAAAAAAAeAAAAAAeAAAAAAeAAAAPgeAAAP/geAAD//geAA///geAH///geB///+AeP//4AAe//8AAAf//AAAAf/wAAAAf+AAAAAfwAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wH/4AH/8f/+AP/////Af/////gf/////geAH4APgeADgAHgeADgAHgeAHwAHgf/////gf/////gP/////AH/8//+AB/wH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//gPgAH//4P+AP//8P/Af//+P/AfwB+P/geAAeAPgeAAeAHgeAAeAHgfAAeAPgf/////gP/////AP/////AH////8AA////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4APgAAH4AfgAAH4AfgAAH4AfgAAH4AfgAAD4APgAAAAAAAAAAAAAAA="), 45, atob("DgsPEhESEhISEhISEgo="), 42+(scale<<8)+(1<<16)); +}; +const fontName = "Antonio27"; +const fontNameLarge = "Antonio42"; +const fontSize = 1; +const fontSizeLarge = 1; +const fontHeightLarge = 42 * fontSizeLarge; + +// LCARS dimensions +if (g.getWidth() < 200) { // Bangle.js 2 + const baseUnit1 = 3; + const baseUnit2 = 2; + const baseUnit3 = 7; +} +else { + const baseUnit1 = 5; + const baseUnit2 = 3; + const baseUnit3 = 10; +} +const widgetsHeight = 24; +const sbarWid = baseUnit3 * 5; +const hbarHt = baseUnit1; +const outRad = baseUnit1 * 5; +const inRad = outRad - hbarHt; +const gap = baseUnit2; +const divisionPos = baseUnit3 * 8; +const sbarGapPos = baseUnit3 * 15; +const lowerTop = divisionPos+gap+1; + +// Star Trek famously premiered on Thursday, September 8, 1966, at 8:30 p.m. +// See http://www.startrek.com/article/what-if-the-original-star-trek-had-debuted-on-friday-nights +const gSDBase = new Date("September 8, 1966 20:30:00 EST"); +const sdatePosBottom = divisionPos - hbarHt - 1; +const sdatePosRight = g.getWidth() - baseUnit2; +const sdateDecimals = 1; +const secondsPerYear = 86400 * 365.2425; +const sdateDecFactor = Math.pow(10, sdateDecimals); + +const clockAreaLeft = sbarWid + inRad / 2; +const clockAreaTop = lowerTop + hbarHt + inRad / 2; +const clockWid = g.getWidth() - clockAreaLeft; +const clockHt = g.getHeight() - clockAreaTop; + +const ctimePosTop = clockAreaTop + baseUnit1 * 5; +const ctimePosCenter = clockAreaLeft + clockWid / 2; +const cdatePosTop = ctimePosTop + fontHeightLarge; +const cdatePosCenter = clockAreaLeft + clockWid / 2; + +const clockCtrX = Math.floor(clockAreaLeft + clockWid / 2); +const clockCtrY = Math.floor(clockAreaTop + clockHt / 2); +const analogRad = Math.floor(Math.min(clockWid, clockHt) / 2); + +const analogMainLineLength = baseUnit1 * 2; +const analogSubLineLength = baseUnit1; + +const analogHourHandLength = analogRad / 2; +const analogMinuteHandLength = analogRad - analogMainLineLength / 2; + +const colorBg = "#000000"; +const colorTime = "#9C9CFF"; +const colorDate = "#A09090"; +const colorStardate = "#FFCF00"; +// On low-bpp devices (Bangle.js 2), use basic colors for analog clock. +const colorHours = bpp > 3 ? "#9C9CFF" : "#00FF00"; +const colorSeconds = bpp > 3 ? "#E7ADE7" : "#FFFF00"; +const colorHands = bpp > 3 ? "#A09090" : "#00FFFF"; +const colorLCARSGray = "#A09090"; +const colorLCARSOrange = "#FF9F00"; +const colorLCARSPink = "#E7ADE7"; +const colorLCARSPurple = "#A06060"; +const colorLCARSBrown = "#C09070"; +// More colors: teal #008484, yellow FFCF00, purple #6050B0 + +var lastSDateString; +var lastTimeStringToMin; +var lastTimeStringSec; +var lastDateString; +var lastAnalogDate; + +function updateStardate() { + var curDate = new Date(); + + // Note that the millisecond division and the 1000-unit multiplier cancel each other out. + var sdateval = (curDate - gSDBase) / secondsPerYear; + + var sdatestring = (Math.floor(sdateval * sdateDecFactor) / sdateDecFactor).toFixed(sdateDecimals); + + // Reset the state of the graphics library. + g.reset(); + g.setBgColor(colorBg); + // Set Font + g.setFont(fontName, fontSize); + if (lastSDateString) { + // Clear the area where we want to draw the time. + //g.setBgColor("#FF6600"); // for debugging + g.clearRect(sdatePosRight - g.stringWidth(lastSDateString) - 1, + sdatePosBottom - g.getFontHeight(), + sdatePosRight, + sdatePosBottom); + } + // Draw the current stardate. + g.setColor(colorStardate); + g.setFontAlign(1, 1, 0); // Align following string to bottom right. + g.drawString(sdatestring, sdatePosRight, sdatePosBottom); + lastSDateString = sdatestring; + + // Schedule next when an update to the last decimal is due. + var mstonextUpdate = (Math.ceil(sdateval * sdateDecFactor) / sdateDecFactor - sdateval) * secondsPerYear; + if (redrawClock) { + setTimeout(updateStardate, mstonextUpdate); + } +} + +function updateConventionalTime() { + var curDate = new Date(); + + if (clockface == "digital") { + drawDigitalClock(curDate); + } + else { + drawAnalogClock(curDate); + } + + // Schedule next when an update to the last second is due. + var mstonextUpdate = Math.ceil(curDate / 1000) * 1000 - curDate; + if (redrawClock) { + setTimeout(updateConventionalTime, mstonextUpdate); + } +} + +function drawDigitalClock(curDate) { + var timestringToMin = ("0" + curDate.getHours()).substr(-2) + ":" + + ("0" + curDate.getMinutes()).substr(-2) + ":"; + var timestringSec = ("0" + curDate.getSeconds()).substr(-2); + var datestring = "" + curDate.getFullYear() + "-" + + ("0" + (curDate.getMonth() + 1)).substr(-2) + "-" + + ("0" + curDate.getDate()).substr(-2); + + // Reset the state of the graphics library. + g.reset(); + g.setBgColor(colorBg); + // Set Font + g.setFont(fontNameLarge, fontSizeLarge); + var ctimePosLeft = ctimePosCenter - g.stringWidth("12:34:56") / 2; + if (ctimePosLeft + g.stringWidth("00:00:00") > g.getWidth()) { + ctimePosLeft = g.getWidth() - g.stringWidth("00:00:00"); + } + g.setColor(colorTime); + if (timestringToMin != lastTimeStringToMin) { + if (lastTimeStringToMin) { + // Clear the area where we want to draw the time. + //g.setBgColor("#FF6600"); // for debugging + g.clearRect(ctimePosLeft, + ctimePosTop, + ctimePosLeft + g.stringWidth(lastTimeStringToMin) + 1, + ctimePosTop + g.getFontHeight()); + } + // Draw the current time. + g.drawString(timestringToMin, ctimePosLeft, ctimePosTop); + lastTimeStringToMin = timestringToMin; + } + var ctimePosLeftSec = ctimePosLeft + g.stringWidth(timestringToMin); + if (lastTimeStringSec) { + // Clear the area where we want to draw the seconds. + //g.setBgColor("#FF6600"); // for debugging + g.clearRect(ctimePosLeftSec, + ctimePosTop, + ctimePosLeftSec + g.stringWidth(lastTimeStringSec) + 1, + ctimePosTop + g.getFontHeight()); + } + // Draw the current seconds. + g.drawString(timestringSec, ctimePosLeftSec, ctimePosTop); + lastTimeStringSec = timestringSec; + + if (datestring != lastDateString) { + // Set Font + g.setFont(fontName, fontSize); + var cdatePosLeft = cdatePosCenter - g.stringWidth("1234-56-78") / 2; + if (lastDateString) { + // Clear the area where we want to draw the time. + //g.setBgColor("#FF6600"); // for debugging + g.clearRect(cdatePosLeft, + cdatePosTop, + cdatePosLeft + g.stringWidth(lastDateString) + 1, + cdatePosTop + g.getFontHeight()); + } + // Draw the current date. + g.setColor(colorDate); + //g.setFontAlign(0, -1, 0); // Align following string to bottom right. + g.drawString(datestring, cdatePosLeft, cdatePosTop); + lastDateString = datestring; + } +} + +function drawLine(x1, y1, x2, y2, color) { + g.setColor(color); + // On high-bpp devices, use anti-aliasing. Low-bpp (Bangle.js 2) doesn't clear nicely with AA. + if (bpp > 3 && g.drawLineAA) { + g.drawLineAA(x1, y1, x2, y2); + } + else { + g.drawLine(x1, y1, x2, y2); + } +} + +function clearLine(x1, y1, x2, y2) { + drawLine(x1, y1, x2, y2, colorBg); +} + +function drawAnalogClock(curDate) { + // Reset the state of the graphics library. + g.reset(); + g.setBgColor(colorBg); + // Init variables for drawing any seconds we have not drawn. + // If minute changed, we'll set for the full wheel below. + var firstDrawSecond = lastAnalogDate ? lastAnalogDate.getSeconds() + 1 : curDate.getSeconds(); + var lastDrawSecond = curDate.getSeconds(); + if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) { + // Draw the main hour lines. + //g.setColor("#9C9CFF"); + //g.drawCircle(clockCtrX, clockCtrY, analogRad); + for (let i = 0; i < 60; i = i + 15) { + let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30); + let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30); + let innerX = clockCtrX + (analogRad - analogMainLineLength) * Math.sin(i * Math.PI / 30); + let innerY = clockCtrY - (analogRad - analogMainLineLength) * Math.cos(i * Math.PI / 30); + drawLine(edgeX, edgeY, innerX, innerY, colorHours); + } + // Set for drawing the full second wheel. + firstDrawSecond = 0; + lastDrawSecond = 59; + } + // Draw the second wheel, or the parts of it that we haven't done yet. + for (let i = firstDrawSecond; i <= lastDrawSecond; i++) { + let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30); + let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30); + let innerX = clockCtrX + (analogRad - analogSubLineLength) * Math.sin(i * Math.PI / 30); + let innerY = clockCtrY - (analogRad - analogSubLineLength) * Math.cos(i * Math.PI / 30); + if (i <= curDate.getSeconds()) { + drawLine(edgeX, edgeY, innerX, innerY, colorSeconds); + } + else if (i % 5 == 0) { + drawLine(edgeX, edgeY, innerX, innerY, colorHours); + } + else { + clearLine(edgeX, edgeY, innerX, innerY); + } + } + if (lastAnalogDate) { + // Clear previous hands. + if (curDate.getMinutes() != lastAnalogDate.getMinutes()) { + // Clear hour hand. + let HhAngle = (lastAnalogDate.getHours() + lastAnalogDate.getMinutes() / 60) * Math.PI / 6; + let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle); + let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle); + clearLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY); + // Clear minute hand. + let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(lastAnalogDate.getMinutes() * Math.PI / 30); + let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(lastAnalogDate.getMinutes() * Math.PI / 30); + clearLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY); + } + } + if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) { + // Draw hour hand. + let HhAngle = (curDate.getHours() + curDate.getMinutes() / 60) * Math.PI / 6; + let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle); + let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle); + drawLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY, colorHands); + // Draw minute hand. + let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(curDate.getMinutes() * Math.PI / 30); + let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(curDate.getMinutes() * Math.PI / 30); + drawLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY, colorHands); + } + lastAnalogDate = curDate; +} + +function switchClockface() { + if (clockface == "digital") { + clockface = "analog"; + } + else { + clockface = "digital"; + } + // Clear whole lower area. + g.clearRect(clockAreaLeft,clockAreaTop,g.getWidth(),g.getHeight()); + lastTimeStringToMin = undefined; + lastTimeStringSec = undefined; + lastDateString = undefined; + lastAnalogDate = undefined; +} + +// Clear the screen once, at startup. +g.setBgColor(colorBg); +g.clear(); +// Draw LCARS borders. +// Upper section: rounded corner. +g.setColor(colorLCARSGray); +g.fillCircle(outRad, divisionPos - outRad, outRad); +g.fillRect(outRad, divisionPos - outRad, sbarWid + inRad, divisionPos); +g.fillRect(outRad, divisionPos - hbarHt, sbarWid + outRad, divisionPos); // div bar stub +g.fillRect(0, 0, sbarWid, divisionPos - outRad); // side bar +g.setColor(colorBg); // blocked out areas of corner +g.fillCircle(sbarWid + inRad + 1, divisionPos - hbarHt - inRad - 1, inRad); +g.fillRect(sbarWid + 1, divisionPos - outRad * 2, sbarWid + outRad, divisionPos - hbarHt - inRad); +// upper division bar +g.setColor(colorLCARSPurple); +g.fillRect(sbarWid + outRad + gap + 1, divisionPos - hbarHt, g.getWidth(), divisionPos); +// Lower section: rounded corner. +g.setColor(colorLCARSPink); +g.fillCircle(outRad, lowerTop + outRad, outRad); +g.fillRect(outRad, lowerTop, sbarWid + inRad, lowerTop + outRad); +g.fillRect(outRad, lowerTop, sbarWid + outRad, lowerTop + hbarHt); // div bar stub +g.fillRect(0, lowerTop + outRad, sbarWid, sbarGapPos); // side bar +g.setColor(colorBg); // blocked out areas of corner +g.fillCircle(sbarWid + inRad + 1, lowerTop + hbarHt + inRad + 1, inRad); +g.fillRect(sbarWid + 1, lowerTop + hbarHt + inRad, sbarWid + outRad, lowerTop + outRad * 2); +// lower division bar +g.setColor(colorLCARSOrange); +g.fillRect(sbarWid + outRad + gap + 1, lowerTop, g.getWidth(), lowerTop + hbarHt); +// second color of side bar +g.setColor(colorLCARSBrown); +g.fillRect(0, sbarGapPos + gap + 1, sbarWid, g.getHeight()); +// Draw immediately at first. +updateStardate(); +updateConventionalTime(); +// Make sure widgets can be shown. +//g.setColor("#FF0000"); g.fillRect(0, 0, g.getWidth(), widgetsHeight); // debug +Bangle.loadWidgets(); +Bangle.drawWidgets(); +// Show launcher on button press as usual for a clock face +Bangle.setUI("clock", Bangle.showLauncher); +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower', on => { + if (on) { + redrawClock = true; + // Draw immediately to kick things off. + updateStardate(); + updateConventionalTime(); + } + else { + redrawClock = false; + } +}); +Bangle.on('touch', button => { + // button == 1 is left, 2 is right + switchClockface(); +}); diff --git a/apps/stardateclock/app.png b/apps/stardateclock/app.png new file mode 100644 index 000000000..86426721b Binary files /dev/null and b/apps/stardateclock/app.png differ diff --git a/apps/stardateclock/metadata.json b/apps/stardateclock/metadata.json new file mode 100644 index 000000000..2f4f27425 --- /dev/null +++ b/apps/stardateclock/metadata.json @@ -0,0 +1,23 @@ +{ + "id": "stardateclock", + "name":"Stardate Clock", + "shortName":"Stardate Clock", + "description": "A clock displaying a stardate along with a 'standard' digital/analog clock in LCARS design", + "version":"0.01", + "icon": "app.png", + "type":"clock", + "tags": "clock", + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "storage": [ + {"name": "stardateclock.app.js","url": "app.js"}, + {"name": "stardateclock.img","url": "app-icon.js","evaluate": true} + ], + "screenshots": [ + {"url": "screenshot1.png"}, + {"url": "screenshot2.png"}, + {"url": "screenshot3.png"}, + {"url": "screenshot4.png"} + ] +} diff --git a/apps/stardateclock/screenshot1.png b/apps/stardateclock/screenshot1.png new file mode 100644 index 000000000..98bd0cea3 Binary files /dev/null and b/apps/stardateclock/screenshot1.png differ diff --git a/apps/stardateclock/screenshot2.png b/apps/stardateclock/screenshot2.png new file mode 100644 index 000000000..8a70c3f52 Binary files /dev/null and b/apps/stardateclock/screenshot2.png differ diff --git a/apps/stardateclock/screenshot3.png b/apps/stardateclock/screenshot3.png new file mode 100644 index 000000000..7acb91d0a Binary files /dev/null and b/apps/stardateclock/screenshot3.png differ diff --git a/apps/stardateclock/screenshot4.png b/apps/stardateclock/screenshot4.png new file mode 100644 index 000000000..c4c83c35f Binary files /dev/null and b/apps/stardateclock/screenshot4.png differ diff --git a/apps/stopwatch/ChangeLog b/apps/stopwatch/ChangeLog index 104fce19d..14c84afd5 100644 --- a/apps/stopwatch/ChangeLog +++ b/apps/stopwatch/ChangeLog @@ -1,2 +1,3 @@ 0.01: first release 0.02: Adjust for touch events outside of screen g dimensions +0.03: Do not register as watch, manually start clock on button diff --git a/apps/stopwatch/metadata.json b/apps/stopwatch/metadata.json index cc13ec92f..7840dd9b5 100644 --- a/apps/stopwatch/metadata.json +++ b/apps/stopwatch/metadata.json @@ -1,7 +1,7 @@ { "id": "stopwatch", "name": "Stopwatch Touch", - "version": "0.02", + "version": "0.03", "description": "A touch based stop watch for Bangle JS 2", "icon": "stopwatch.png", "screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}], diff --git a/apps/stopwatch/stopwatch.app.js b/apps/stopwatch/stopwatch.app.js index e2be95451..92e7a9977 100644 --- a/apps/stopwatch/stopwatch.app.js +++ b/apps/stopwatch/stopwatch.app.js @@ -227,4 +227,4 @@ g.fillRect(0,0,w,h); Bangle.loadWidgets(); Bangle.drawWidgets(); draw(); -Bangle.setUI("clock"); // Show launcher when button pressed +setWatch(() => load(), BTN, { repeat: false, edge: "falling" }); diff --git a/apps/sunclock/README.md b/apps/sunclock/README.md new file mode 100644 index 000000000..4076767d9 --- /dev/null +++ b/apps/sunclock/README.md @@ -0,0 +1,6 @@ +# Sun Clock +Clock showing date/time, sunset/sunrise, H = current sun height/noon sun height, Az = sun azimuth + +![](screenshot_sunclock.png) + +Location set with mylocation app, time zone set with settings app. diff --git a/apps/sunclock/app-icon.js b/apps/sunclock/app-icon.js new file mode 100644 index 000000000..977aec98d --- /dev/null +++ b/apps/sunclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("kEgwhC/AC8N6APo7oPJBQndBQYPEhoaFAogZIEokO93u8AuGAAYOCCAgOLCBQOFAAIeNEBAPPBw4wHB5wuIGAwPthGIxwIC8UowUuB4eIwAPBxEk91CAgIGGwAhBBYeCAwMoA4ZwEBIIOCAxAA/ABwA=")) diff --git a/apps/sunclock/app.js b/apps/sunclock/app.js new file mode 100644 index 000000000..4609565a2 --- /dev/null +++ b/apps/sunclock/app.js @@ -0,0 +1,79 @@ +/* sclock.app.js for Bangle2 +Peter Bernschneider 30.12.2021 +Update current latitude and longitude in My Location app +Update current Timezone in Settings app, menu item "System" +Update for summer time by incrementing Timezone += 1 */ +setting = require("Storage").readJSON("setting.json",1); +E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ +SunCalc = require("suncalc.js"); +loc = require('locale'); +const LOCATION_FILE = "mylocation.json"; +const xyCenter = g.getWidth() / 2 + 3; +const yposTime = 60; +const yposDate = 100; +const yposRS = 135; +const yposPos = 160; +var rise = "07:00"; +var set = "20:00"; +var pos = {altitude: 20, azimuth: 135}; +var noonpos = {altitude: 37, azimuth: 180}; +let idTimeout = null; + +function updatePos() { + coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"}; + pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon); + times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon); + rise = times.sunrise.toString().split(" ")[4].substr(0,5); + set = times.sunset.toString().split(" ")[4].substr(0,5); + noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon); +} + +function drawSimpleClock() { + var d = new Date(); // get date + var da = d.toString().split(" "); + g.clear(); + Bangle.drawWidgets(); + g.reset(); // default draw styles + g.setFontAlign(0, 0); // drawSting centered + + var time = da[4].substr(0, 5); // draw time + + g.setFont("Vector",60); + g.drawString(time, xyCenter, yposTime, true); + + var date = [loc.dow(new Date(),1), loc.date(d,1)].join(" "); // draw day of week, date + g.setFont("Vector",24); + g.drawString(date, xyCenter, yposDate, true); + + g.setFont("Vector",25); + g.drawString(`${rise} ${set}`, xyCenter, yposRS, true); // draw riseset + g.drawImage(require("Storage").read("sunrise.img"), xyCenter-16, yposRS-16); + + g.setFont("Vector",21); + g.drawString(`H${pos.altitude}/${noonpos.altitude} Az${pos.azimuth}`, xyCenter, yposPos, true); // draw sun pos + + let t = d.getSeconds()*1000 + d.getMilliseconds(); + idTimeout = setTimeout(drawSimpleClock, 60000 - t); // time till next minute +} + +// special function to handle display switch on +Bangle.on('lcdPower', function(on){ + if (on) { + drawSimpleClock(); + } else { + if(idTimeout) { + clearTimeout(idTimeout); + } + } +}); + +g.clear(); // clean app screen +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +setInterval(updatePos, 60*5E3); // refesh every 5 mins + +updatePos(); +drawSimpleClock(); // draw now + +setWatch(Bangle.showLauncher, BTN1, { repeat: false, edge: "falling" }); // Show launcher when button pressed diff --git a/apps/sunclock/app.png b/apps/sunclock/app.png new file mode 100644 index 000000000..72c5b10d5 Binary files /dev/null and b/apps/sunclock/app.png differ diff --git a/apps/sunclock/metadata.json b/apps/sunclock/metadata.json new file mode 100644 index 000000000..617d76821 --- /dev/null +++ b/apps/sunclock/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "sunclock", + "name": "Sun Clock", + "version": "0.01", + "description": "A clock with sunset/sunrise, sun height/azimuth", + "icon": "app.png", + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"sunclock.app.js","url":"app.js"}, + {"name":"sunclock.img","url":"app-icon.js","evaluate":true}, + {"name":"suncalc.js","url":"suncalc.js"} + ] +} diff --git a/apps/sunclock/screenshot_sunclock.png b/apps/sunclock/screenshot_sunclock.png new file mode 100644 index 000000000..a24af2116 Binary files /dev/null and b/apps/sunclock/screenshot_sunclock.png differ diff --git a/apps/sunclock/suncalc.js b/apps/sunclock/suncalc.js new file mode 100644 index 000000000..b1af0a0d9 --- /dev/null +++ b/apps/sunclock/suncalc.js @@ -0,0 +1,298 @@ +/* Module suncalc.js + (c) 2011-2015, Vladimir Agafonkin + SunCalc is a JavaScript library for calculating sun/moon position and light phases. + https://github.com/mourner/suncalc + +PB: Usage: +E.setTimeZone(2); // 1 = MEZ, 2 = MESZ +SunCalc = require("suncalc.js"); +pos = SunCalc.getPosition(Date.now(), 53.3, 10.1); +times = SunCalc.getTimes(Date.now(), 53.3, 10.1); +rise = times.sunrise; // Date object +rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm +*/ +var exports={}; + +// shortcuts for easier to read formulas + +var PI = Math.PI, + sin = Math.sin, + cos = Math.cos, + tan = Math.tan, + asin = Math.asin, + atan = Math.atan2, + acos = Math.acos, + rad = PI / 180; + +// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas + +// date/time constants and conversions + +var dayMs = 1000 * 60 * 60 * 24, + J1970 = 2440588, + J2000 = 2451545; + +function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } +function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021 +function toDays(date) { return toJulian(date) - J2000; } + + +// general calculations for position + +var e = rad * 23.4397; // obliquity of the Earth + +function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } +function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } + +function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } +function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } + +function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } + +function astroRefraction(h) { + if (h < 0) // the following formula works for positive altitudes only. + h = 0; // if h = -0.08901179 a div/0 would occur. + + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); +} + +// general sun calculations + +function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } + +function eclipticLongitude(M) { + + var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center + P = rad * 102.9372; // perihelion of the Earth + + return M + C + P + PI; +} + +function sunCoords(d) { + + var M = solarMeanAnomaly(d), + L = eclipticLongitude(M); + + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) + }; +} + +// calculates sun position for a given date and latitude/longitude + +exports.getPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = sunCoords(d), + H = siderealTime(d, lw) - c.ra; + + return { + azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg + altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg + }; +}; + + +// sun times configuration (angle, morning name, evening name) + +var times = [ + [-0.833, 'sunrise', 'sunset' ] +]; + +// calculations for sun times +var J0 = 0.0009; + +function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); } + +function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; } +function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); } + +function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); } +function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; } + +// returns set time for the given sun altitude +function getSetJ(h, lw, phi, dec, n, M, L) { + + var w = hourAngle(h, phi, dec), + a = approxTransit(w, lw, n); + return solarTransitJ(a, M, L); +} + + +// calculates sun times for a given date, latitude/longitude, and, optionally, +// the observer height (in meters) relative to the horizon + +exports.getTimes = function (date, lat, lng, height) { + + height = height || 0; + + var lw = rad * -lng, + phi = rad * lat, + + dh = observerAngle(height), + + d = toDays(date), + n = julianCycle(d, lw), + ds = approxTransit(0, lw, n), + + M = solarMeanAnomaly(ds), + L = eclipticLongitude(M), + dec = declination(L, 0), + + Jnoon = solarTransitJ(ds, M, L), + + i, len, time, h0, Jset, Jrise; + + + var result = { + solarNoon: fromJulian(Jnoon), + nadir: fromJulian(Jnoon - 0.5) + }; + + for (i = 0, len = times.length; i < len; i += 1) { + time = times[i]; + h0 = (time[0] + dh) * rad; + + Jset = getSetJ(h0, lw, phi, dec, n, M, L); + Jrise = Jnoon - (Jset - Jnoon); + + result[time[1]] = fromJulian(Jrise); + result[time[2]] = fromJulian(Jset); + } + + return result; +}; + + +// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas + +function moonCoords(d) { // geocentric ecliptic coordinates of the moon + + var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude + M = rad * (134.963 + 13.064993 * d), // mean anomaly + F = rad * (93.272 + 13.229350 * d), // mean distance + + l = L + rad * 6.289 * sin(M), // longitude + b = rad * 5.128 * sin(F), // latitude + dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt + }; +} + +getMoonPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = moonCoords(d), + H = siderealTime(d, lw) - c.ra, + h = altitude(H, phi, c.dec), + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + + h = h + astroRefraction(h); // altitude correction for refraction + + return { + azimuth: azimuth(H, phi, c.dec), + altitude: h, + distance: c.dist, + parallacticAngle: pa + }; +}; + + +// calculations for illumination parameters of the moon, +// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and +// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + +getMoonIllumination = function (date) { + + var d = toDays(date || new Date()), + s = sunCoords(d), + m = moonCoords(d), + + sdist = 149598000, // distance from Earth to Sun in km + + phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)), + inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)), + angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) - + cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra)); + + return { + fraction: (1 + cos(inc)) / 2, + phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI, + angle: angle + }; +}; + + +function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); +} + +// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + +getMoonTimes = function (date, lat, lng, inUTC) { + var t = new Date(date); + if (inUTC) t.setUTCHours(0, 0, 0, 0); + else t.setHours(0, 0, 0, 0); + + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; + } + + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + + if (rise && set) break; + + h0 = h2; + } + + var result = {}; + + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + + return result; +}; \ No newline at end of file diff --git a/apps/supmariodark/metadata.json b/apps/supmariodark/metadata.json index b56b19735..e5d0861ae 100644 --- a/apps/supmariodark/metadata.json +++ b/apps/supmariodark/metadata.json @@ -8,6 +8,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS"], + "readme": "README.md", "storage": [ {"name":"supmariodark.app.js","url":"supmariodark.js"}, {"name":"supmariodark.img","url":"supmariodark-icon.js","evaluate":true}, diff --git a/apps/swp2clk/metadata.json b/apps/swp2clk/metadata.json index aa95a6473..8b0cce2d8 100644 --- a/apps/swp2clk/metadata.json +++ b/apps/swp2clk/metadata.json @@ -5,7 +5,7 @@ "version": "0.01", "description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.", "icon": "app.png", - "type": "boot", + "type": "bootloader", "tags": "tools", "supports": ["BANGLEJS2"], "readme": "README.md", diff --git a/apps/tabanchi/ChangeLog b/apps/tabanchi/ChangeLog new file mode 100644 index 000000000..23b44dd5d --- /dev/null +++ b/apps/tabanchi/ChangeLog @@ -0,0 +1 @@ +0.0.1: Initial implementation diff --git a/apps/tabanchi/README.md b/apps/tabanchi/README.md new file mode 100644 index 000000000..71ad22558 --- /dev/null +++ b/apps/tabanchi/README.md @@ -0,0 +1,47 @@ +たばんち (tabanchi) +=================== + +A Tamagotchi clone watch app for the BangleJS2 smartwatch. + +Author +------ + +Written by pancake in 2022, powered by insomnia + +Source repository: https://github.com/trufae/tabanchi + +Features +-------- + +* [x] 12/24 clock with HH:mm:ss +* [x] Battery level indicator +* [x] Eating meals and snacks +* [x] Refusing to do things +* [x] Getting sick +* [x] Take a shower +* [x] Switch on/off the light +* [x] Status for happy/hunger/discipline +* [ ] Evolutions +* [ ] Hatching eggs +* [x] Playing a game +* [ ] Education +* [x] Medicine +* [ ] Death + + +Resources +--------- + +* Original pixmaps taken from: + - https://www.spriters-resource.com/resources/sheets/141/144400.png +* Espruino Image converter: + - https://www.espruino.com/Image+Converter +* Tamagotchi Essentials + - https://tamagotchi.fandom.com/wiki/Tamagotchi_(1996_Pet) +* Tamagotchi Emulator Source (Java) + - https://gist.github.com/aerospark/80c60e801398fd961e3f + +Screenshots +----------- +![tama on bangle](screenshot.jpg) + diff --git a/apps/tabanchi/app-icon.js b/apps/tabanchi/app-icon.js new file mode 100644 index 000000000..95796eb16 --- /dev/null +++ b/apps/tabanchi/app-icon.js @@ -0,0 +1 @@ +atob("MDDBAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gAAAAD//gAAAAD//gAAAAf//8AAAAf//8AAAAf//8AAAD/////gAD/////gAD/////gAf/////8Af/////8Af/////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8D//////8f//////gf//////gf//////gD/////gAD/////gAD/////gAAf///8AAAf///8AAAf///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") diff --git a/apps/tabanchi/app.js b/apps/tabanchi/app.js new file mode 100644 index 000000000..c87a08817 --- /dev/null +++ b/apps/tabanchi/app.js @@ -0,0 +1,1603 @@ +// GPL TAMAGOTCHI CLONE FOR THE BANGLEJS2 SMARTWATCH BY pancake 2022 +// TABANCHI -- たばんち + +const scale = 6; +let tool = -1; +const w = g.getWidth(); +const h = g.getHeight(); +let hd = 1; +let vd = 1; +let x = 20; +let sx = 0; // screen scroll x position +const y = 40 - scale; +let animated = true; +let transition = false; +let caca = null; +let egg = null; +let mode = ''; +let evolution = 1; +let callForAttention = false; // TODO : move into tama{} +let useAmPm = true; +let oldMode = ''; +let gameChoice = 0; +let gameTries = 0; +let gameWins = 0; + +g.setBgColor(0); + +const tama06eat0 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('/////4A/vd+B7+N3g/e714P39/f39/f3++/8H/////8=') +}; +const meal0 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('gXp6tbW1tYE=') +}; +const meal1 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('v19htbW1tYE=') +}; + +const meal2 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////5+htYE=') +}; +const snack0 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('358D08vA+fs=') +}; +const snack1 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('///708vA+fs=') +}; +const snack2 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////+vA+fs=') +}; + +const angry0 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////8/Pv/8=') +}; + +const angry1 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('8/Dg4fn/v/8=') +}; + +const right = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('7+eDgYPn7/8=') +}; + +const left = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('9+fBgcHn9/8=') +}; + +const img_on = { + width: 16, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('//+M73VvdW91r3Wvjc///w==') +}; + +const img_off = { + width: 16, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('//+MIXXvdGN173Xvje///w==') +}; + +const right0 = { + width: 3, + height: 5, + bpp: 1, + transparent: 1, + buffer: atob('d1Y=') +}; + +const right1 = { + width: 3, + height: 5, + bpp: 1, + transparent: 1, + buffer: atob('ZBY=') +}; + +const am = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('w7mBuf+Rqak=') +}; + +const pm = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('g52Dn/+Rqak=') +}; +const numbers = [ + { // 0 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('lmZmnw==') + }, { // 1 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('2d3d3w==') + }, { // 2 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('lu23Dw==') + }, { // 3 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('Hu3uHw==') + }, { // 4 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('lVVQ3w==') + }, { // 5 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('B3HuHw==') + }, { // 6 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('l3Fmnw==') + }, { // 7 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('Bm7d3w==') + }, { // 8 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('lmlmnw==') + }, { // 9 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('lmjunw==') + } +]; + +const snumbers = [ + { // 0 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4qqjw==') + }, { // 1 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/93d3w==') + }, { // 2 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/46Ljw==') + }, { // 3 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/46Ojw==') + }, { // 4 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/6qO7w==') + }, { // 5 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4uOjw==') + }, { // 6 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4uKjw==') + }, { // 7 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4ru7w==') + }, { // 8 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4qKjw==') + }, { // 9 + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/4qOjw==') + } +]; + +const colon = { + width: 4, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/f/9/w==') +}; + +const egg00 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('/////////D/7n/GP8e/n9+537nfvx/OP+Z/wD/////8=') +}; + +const h24 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('ldWxnf/bw9s=') +}; + +const discipline = { + width: 32, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('///v/x//7/9v/i//akqqI27erqtqWiqja1rqrxpK6qM=') +}; + +const linebar = { + width: 32, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////4AAAA9////3f///93////d////3f///94AAAA8=') +}; + +const hungry = { + width: 32, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////2////9qiKr/aqqa/wqquv9qqLz/aK6+/2/4+f8=') +}; + +const happy = { + width: 32, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////2/iP/9v6qv/bGqr/w9iK/9obvP/a277/2yu5/8=') +}; + +const vs = { + width: 16, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('Uf9V/1f/W/9d/7X/sf///w==') +}; + +const egg01 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('///////////8P/uf8Y/x7+P37nfud/PP+Z/gB/////8=') +}; + +const tama06no0 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('//////w/+9/3gey974Hv3e/B7/fv9+/39+/4H/////8=') +}; + +const tama06no1 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('//////w/+9+B7703gfe794P37/fv9+/39+/4H/////8=') +}; + +const caca00 = { + width: 12, + height: 12, + bpp: 1, + transparent: 1, + buffer: atob('/////733v72/+f4vw3wH////') +}; + +const caca01 = { + width: 12, + height: 12, + bpp: 1, + transparent: 1, + buffer: atob('////v/33v7+3+f4v0HwH////') +}; + +// var img = hs.decompress(atob("sFggP/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A+A")); +const tama00 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('///////////8H/vv9oHvveuB793vwd/34A////////8=') +}; +const tama01 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('/////////AH7vfeB7sfvwevd78Hv7+/v7+/33/g///8=') +}; + +const tool00 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('//////7v4f8zHwP8zHwP8zHwP8zHwP8THwP8DHwP8BHgP8AHgP8AHgP+AHgP+APgP/AfAP/g/AP/x/AP/x/AP/x/AP/w/gP/w/8P/w/8P/gf8P/gf8P/AP8P/AP4P/AP4P/gf4P/wf4P/4/8f/////A=') +}; + +const tool01 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('///////D///fD4/+Pn4/+P/4//P/7/v+Af3n4APjDwAHDjgADP/AGD//DPh/+H/h/+O5x/GOkxxCOkxBOG8xh/GYz//HBz//jBn//xjmPw4/ODx///Dx+AfH/8AP///////+Af//8AP///////////A=') +}; + +const tool02 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('///////////////+D///4B///x8///xUf/hmTf+An/f4AmTfwAmTfgAl8fABx8+AD4B8AH8D4AP//wAf//gA///AB//+AH//8AP//4A///wD//8AP//4A///4H///4H///8H///+P/////////////A=') +}; + +const tool03 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('/////////////x////g////gf//fwP/+P4P/+OMH/+GEH/8DCD/4BjB/wxhB/h4xg/D8Qw8H8Yw4H+M5wH+H/gD/H/gD/D/wB8B/wAwB/wAAz/wAD//gAH//CAf//PB////n//////////////////A=') +}; + +const tool10 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('////////////////wf///AH//yAD/Dkfh8B0/wAA8/wA44vnD545Bgx8ZA4x4H58xznP8RjjH8ZnmP8ZmGPw5gPAB58fAHx8fw/x4///j8///j8f//D8f/8H+D/gf/AAA//gAD//+Af///////////A=') +}; + +const tool11 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('//////////////////////B///wAD/+AAA/8D/wPwfz+Hg/z/Dj7z3xH5znYM5znIM5TmIOdT8YGeL85GOP45neP/xj+P/zz+P/zx+H/nx+H/n4///H4/h/P8QAGP+AAAf/D/g////////////////A=') +}; +const tool12 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('/////////////////////////////z////5////5///94///48/g/8c8AH8M4AGcEwAOEEgAyAgAAwAgAB4YwAH8YwAH+c4AH/c4AAf84gAB/4gAB/5wAB/54AD//8AH///gf/////////////////A=') +}; + +const tool13 = { + width: 30, + height: 30, + bpp: 1, + transparent: 1, + buffer: atob('/////////////////////////+A///8AP//weH//x/jg/j/yAPnPwOHnM4/jnM9/5n8//5k/+fkk/vHEmPPgMjAbgcxxjmc4fD/48AB/4+AQ/x//4fj//+AH///AP/////////////////////////A=') +}; + +const shower = { + width: 8, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('5cuXy+XLl8vly5fL5cuXyw==') +}; + +const tools = [ + tool00, tool01, tool02, tool03, + tool10, tool11, tool12, tool13 +]; + +const tamabg = { + width: 176, + height: 176, + bpp: 8, + buffer: require('heatshrink').decompress(atob('/wAHlUrldPp9VAH4A/AClPlcqlSnIVo1Vq2B1esACOrAAQTSDhIAHHaQkKDrAwXGpgJBwFWWQKtKkkrwGr6wA/AH4AdWQMrVxEqquBJ34A/AEWBlcAVwsAqurHd/X6+y2Quq6AAEWH+rqqvFp+lM7QYVVoOsAAKypV4qwaJQIAC1hGfwMrVwcrwBlcLyisB1utWIav/fxZPiq0qV4VV1ZkcMqReCVwIACMASxlJTJPGJwhPDI7urlauBlWAMbplSLwwADV74/FJJZQRfw5OKCQIACJyVWlX+lerV7XRV6iuJ1usWDymFV5S3IVyGJxOzV4JOEVYWsAAKxTwCvBqqvVMQ6vSL4wACMAKvkWBBWIJyavDJwPXVooQDWQZOO1crklWR5pNHV8iwDCg4AEeza3KV7BQBAAOyAwQAFWCOsqsqqxfSNBiDQ2SuIKYZREK4JbBAAJsDVyvRVxqvNJIyvBAgRCCVxBeEV8hZKV8Gz63X66mB1gPHWCwAOJyyqBVppeCJ5qvXLa6vRMIRjNMBqvtWAQAOV6N6V+OsV5xhaV96wPV75gkV5JeBMB4AB2RiNJjxOCISJNLJhqvBqyvkMpmsWBRsR1hiNKKAdNV4Swc2av4NBOyJ5av/JwSvbJhqvQVzRpJ2WsMDSvB1hQMKSSv/VsmsMxOyWDmsMRL4fAA2zWDSvbVzZlM2RgaV5RKjWAr8jV7XRAAJjd6+zAAJiX2avx66v0LzJiR6+y2SwXDIJQc6IABWCOyV7AnNV8hbOABCwKx+PV5ReKIJAGLKaawQxOJAYOz2b7JV9SwX66wJV5WzL5g/HA5ivT1ivTVpyvOJoi3TV6xlS1usMRw+HJBKvmVgKtBVyCvPMRBTFWF+sAAJiQHYxIKKKpIIIgQADVgKuSV7a5MV7CwGLoYAEFC5IfJIJHE2ezJLqv/MwKvBMQJkHEzKvhI4QADIbavgMsKvBUzpVPJDZFkV/5nBVshKFFUyv0Bg5h/AH6vhWIhY/AH6vsAH4A/V7cGV4WyAAQwmFSIRDACQ6VNEIib6CvCg9W1gAL1oAGCJPQAAoOHJoIWUIihFKCg49FHgwAQI5wgSqyvBLpAAV6IoFCx5MGC6AnQYZ4SEV7InMECGmqCvV2awLWIRHMJhSugE44oMVy4nPD6KvXADLJLAEabGbBqvYAAKvfkqvuAF5rJ1gABCY3RV7QAB6I8JFCGsV4T6NAH6vZWIYUSACRAM6IAFBIQ+B6KvEWFGzx6uw6KJWV9IAM1lWV4L2BQuCv46J6K1ivzmSvCWAKxaKo4cXdjyJh6IABEhxSaxCvEWDZrcLz6uPeqoqPV8KwYfRnRMS5gY1iuObTYyHAAQcY2avHJK3RV5iwBWJwaKV0ivjWAaviOCI2BNp6yEV6wZOV3I1GFSyvJEB6tUSxobRAALoEISytjGh5uKV5qwBWJatXbZgkWJAKxJERivzWIQABV6hPKVroqJE7KAKN4QACcpIAeKK76BAARxBV4R1IY46uhbUKHQNgSu7WY9WgKvBOY6vHV0KOP6PRD5xKHAF53f1lQg9WE4w0JX46vpWR+sV2x5gV4+sMJquxNxiuVaILTIBQfRWGavWLgQ1cV6wAcJSwBBAAixm1lWV4IrDQKQ1aV2ZPbQ4SAN6IABV7iCVIgI0WTT59OCYqvdGCKxU1lQkqvBBIh3VWSaugTYglPV+B/EV6YKGGKxpQ6KukFRBBDfg6tXKTI0PV5SwKx43QV1bgPLBQdTECAAS6IABPgusqyvJRTxnFV2SOS1gABV9izGHASvLRgKNfVsSuRRy/RV1hcGV5gAC1gveV360SV1RdBmSvOHr2sWECuQKP4tNqyvPLzmsD4XRV1yPsPbpKBV6SQbacDQDf9QArfgqvSWLIjLdYKu/V2R1BqCvTMq3REsBJnGaIllV7AYBVySvQAAITB6KudRE5xUK6KvD1iwVH5wlVFhwkRQrQAE6IrgLYIiLqEBqzAMABfRV05qLLwIqLVryuMOaB4SV4MHV4aOXVsyXMWASyH6J9Lx6vTfTh5SV445UHhKtpNBauMACh0cECavJHizlUL7IAtKC7nINSKvKDyRahV3esKTCvmAAPRcygaRaagAExOJV36VCEZCvfSpStNdyquRGCB7JV2RyQV6BGIVyRjSEqj8QWaivYEzavRaYyEZHpatcVxArVVsiwDAAKveNAPRQ7haYV7ZsDWZYcPKbawJV6oAgIAYkgSKSufODivZ1msRb/Q6PRV8CQTGwJaHDiR1cV7KtBHb4AjIgYAULI3RDCBQedQKvVVwg+iV+5YIEJr/fWAqvBgFWeAysGYwgAGDJYANDYInBWLiuhEpquhOoivBkivLVwJcOCALpUaMKvYE6pHYWB2sq0lqxCJQ5KxKVrR/SV1wpCAAiulFoSvDIhCvTWR4jSOCSuXTCauqV4vRBxJlVEBKJYORqurAFivEdZivfRbAjiVv6vFCJpliaajWL6KurFgStoAAKvQNp46WWSyxbVzDpKADgoCV4Msq6MaZ4LrZV6gvJDTStPWEpwE1lQqyvSRgytaEYaxTGJivhDxSteIg6vBqyVURoKtdSSh0R6KOZd6HRMi4oKV4KwBS6qukSRiUTaI6uiH6olOV4MrqzSWV86TI6IcVTARKTVqKxSaiCvBktWA4jVTWFPRVzJNDV1BzNDySvHFBjUIWFIAvVy5yKDyivJFJCtID4msTP6u/V8AiQWX+t6KHLVzRoL1ivfLhSx0NxqdRIhSJUGqqvYFDyygEggYT1iSQCJRdbV7fRbMSugephNTMoyvPLK7XRV44oO6KxvGBD3MfaAdHRBZUZWCivD1gzQV6pdYb5XRBYJHYZhSJHPSKweV4YoSWDB0LKa6xICyw2IVbhCSboIABqyvUWFCtSK4REUdJyukMBSsDV4cHV9x4LVqivXRKCvkAAPRdBeImSvVWDQ/KV6qIIDxhhQV84AMV7CwV1hkOWCREWVqDBSV/4AD6IAHEqrABVrTQKG6qfhE4OsV8xQBFRxRXVrT4HJCI1GMMBVPV42PbSiujKg57iciomb6IiQV4yu8WIgtrVxY5ZaaivBgKvWI4KCrFlquLRxgkW6IVJmUlV6b4cLqjarVxyOKaa/REQ6vSEpKErFVSuPWKfREC2sqyvPExiCmbgiyYfJqtSNRWPVwwABV7IbBfjKEYGKKwXI5pgPWB4yGVxyvH6KvDBxJMSWEBxVABJ5GI46vZETZNI1lQV4gQFJaitd6KOGFbAfJVz6wIQbesqyvEFIpMWe44ARFSHRVzJ1GV7bSXJ5SvFSI6wsFirQaZgivyJQuPx6vHI4hgTWBIABVsrcOEhphbaBJaQKAyvFxEyV4KGjWB6uYFRZ4UV7QgFH4I3MBwQWE2ezV4sHqwPG2QZCFZywWEjBiJJSR4GG7iuFV54AHDYKvCgyvCXYKtBAAiJbGAQjgMhYTQHQJmB1vRGrghBMAYUMTAoAG2esmUqq3XAAPW6wNDAoIA/ADxvBNQY/66+sqyvB6xD8AFatBAAZA7V4gA/6HQFM+y2ez2WyJ/esqqv/LwYADV0gAB1gABWLxOFJ63QV/ZVGV86sC1oAEWIJIVVxav/VqhWFMDiuK1iuC2ezV4iwMHxiuHJ6yvQ6/X2ZNBAAavmKwJfcABKpBVwQAGWAJHRIAquJV7F6V5pXBKQoHBCQ6/FYCCviGpZWFV/+sV55WJKgJVFAwOsAAawPKxBfYVoQ3DBg2sV8BBDV95WLNYuyYIITEWB5ZKL6jmGHYL1G1hHBV777JV9JPE2YABUQ2y64KFAAawdLJ42DHYmzGwRFBewJIJV5r5RVzKvVLQRbGLAJlYMxwQGJB7nFegRTJJL6vrMogAFx+JBZIADBwRmaBxJJRf4qvaWCauVV6xaDV6GPV7ZoQV5arEV/6vbACxmHJoyvc1g9QV5WyV5xKSV8xTJACZmFU6qvgI6CqXWLavP2Sv/V8esV8iyUV6CwbM4yv/I4KBLJkavYWARoZVwKv/exiuiWCKvPWDWzV0ZHJVzGz2ZvLV/6wCKgquSV8JGKVzKvqVx6vUNSusVwxiX65ZPV6xGIWESuQV6SyE1htL2atDVwJiMNSREPIJyuHNh48IVT6vZWIhuKVwRmQMCBiPeZWzAAIJHLAKIWJp6vtWIasINgJlUVx5iPeRKvIVwKtXfr6vgWARwHNoJgbNBJAQWB+sVzSvpqxCYOIhkBMqxXIWDQADfAQABWAZIXVyivzOIhkYV8A+CHgivDXAiuaV/5xNV7prIfGyvMK5av/V64KGRzawCVzj9RV+hhf1ivnJsj9iV/RYENpSu8UoxNgV/htMIP7ylV4MAV/4A/AFivCqusIn4A/AFWrqv+qurIn4A/V9cr/0rwBE/AH4AqwEq/0qqxE/AH4Ap1lVgH+/0r1ZG/AH4AowMrVwP+lVW1hH/AH4Am1dVVwQABleAWH4A/AEusq0qV4iw/AH6unwErVwqw/AH4Al1dWlSuHAAMqquA1ZQ/AH4Ab1mrwErVxQACldVWQIA/AH4AYq1VlcrVpgADgEqAAQXBY4IA/AH4ASgClI')) +}; + +const tama06happy = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('/////4Afvm+Hd+B74duDq7v7g/vv++/79/f4D/////8=') +}; + +const battery = { + width: 32, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/////x/t//9vxP//bG2arx9taa9obQuPa2177xy2i58=') +}; +const snack = { + width: 24, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('z//7t//7vDGbzb1q9aF5ta1qzbKa////') +}; +const meal = { + width: 24, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('////k//fqzjfqt7fqhDfqvbfuxlf////') +}; + +const face = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/8OlgZmBw/8=') +}; + +const year = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/6qpq8vrn/8=') +}; + +const weight = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/34A54G9pQA=') +}; + +const weight_g = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('49vj+cO7x/8=') +}; + +const heart0 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('yba+vt3r9/8=') +}; + +const heart1 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('yaCgoMHj9/8=') +}; + +g.clear(); +g.setColor(1, 1, 1); +g.fillRect(0, 0, 200, 200); + +g.setColor(0); + +g.drawString('Loading...', 10, 10); +egg = egg00; +n = tama00; + +const tama = { + // visible + age: 0, + weight: 1, + aspect: 6, + discipline: 0, + happy: 3, + sick: false, + hungry: 3, + cacas: 0, // move from cacas + // hidden + sickness: 0, + defenses: 100, + tummy: 100, + awake: 3 +}; + +function drawHearts (n) { + for (i = 0; i < 4; i++) { + const himg = (i < n) ? heart1 : heart0; + g.drawImage(himg, 1 + (scale * (8 * i)) - scale - scale, 40 + (scale * 8), { scale: (scale) }); + } +} + +function drawLinebar (n, arrow) { // 0-100 + const yy = 34; + g.drawImage(linebar, 0, yy + (scale * 8), { scale: scale }); + + let wop = scale * 2; // (frame++%2)? scale*3:scale*2; + if (frame % 2) { + wop += scale; + } + let twelve = 12; + if (arrow) { + twelve = 11; + } + const val = (n * twelve) / 100; + const max = val || twelve; + + for (let i = 0; i < max; i++) { + g.setColor(0, 0, 0); + + if (arrow) { + const x = wop + (i * scale * 2) + ((i % 2) * scale); + const y = yy + (scale * 11); + g.fillRect(x + (scale * 2), y, x + (scale * 3), y + scale); + g.fillRect(x + scale, y + scale, x + (scale * 2), y + (scale * 2)); + g.fillRect(x, y + (scale * 2), x + scale, y + (scale * 3)); + } else { + const x = (i * scale * 2) + (scale * 2); + const y = yy + (scale * 11); + g.fillRect(x, y, x + scale, y + scale * 3); + } + } +} + +function drawStatus () { + const yy = 34; + switch (statusMode) { + case 0: + g.drawImage(face, scale, yy, { scale: scale }); + g.drawImage(weight, scale, yy + (scale * 8), { scale: scale }); + g.drawImage(numbers[0], w - (scale * 14), yy, { scale: scale }); + g.drawImage(year, w - (scale * 8), yy, { scale: scale }); + g.drawImage(numbers[1], w - (scale * 14), yy + (scale * 9), { scale: scale }); + g.drawImage(weight_g, w - (scale * 8), yy + (scale * 9), { scale: scale }); + break; + case 1: // discipline + g.drawImage(discipline, 0, yy, { scale: scale }); + drawLinebar(tama.discipline, false); + break; + case 2: // hungry + g.drawImage(hungry, scale, yy, { scale: scale }); + drawHearts(tama.hungry); + break; + case 3: // happy + g.drawImage(happy, scale, yy, { scale: scale }); + drawHearts(tama.happy); + break; + case 5: // battery + g.drawImage(battery, scale, yy, { scale: scale }); + drawLinebar(E.getBattery(), true); + break; + default: + statusMode = 0; + drawStatus(); + break; + } +} + +function drawScene () { + if (Bangle.isLocked()) { + tool = -1; + } + g.setColor(0, 0, 0); + g.fillRect(0, 0, 200, 200); + g.drawImage(tamabg, 0, 0, { scale: 1 }); + g.setColor(1, 1, 1); + + if (evolution == 0) { + g.drawImage(egg, w / 4, 32, { scale: scale }); + return; + } + if (callForAttention) { + g.drawImage(tool13, 10 + 30 + 10 + 30 + 10 + 30 + 10, 135); + } + if (mode == 'game') { + drawGame(); + if (!transition) { + if (gameChoice == 2) { + g.drawImage(right, w - (scale * 7), 40 + (scale * 4), { scale: scale }); + } else if (gameChoice == 1) { + g.drawImage(left, 0, 40 + (scale * 4), { scale: scale }); + } + return; + } + } + if (gameTries > 4) { + mode = ''; + oldMode = ''; + const s0 = numbers[gameWins]; + const s1 = numbers[(5 - gameWins)]; + g.drawImage(s0, (scale * 5), 60, { scale: scale }); + g.drawImage(vs, (scale * 12), 60, { scale: scale }); + g.drawImage(s1, (scale * 22), 60, { scale: scale }); + + gameTries++; + if (gameTries > 10) { + const winrar = (gameWins > 2); + gameTries = 0; + gameWins = 0; + oldMode = ''; + mode = ''; + if (winrar) { + tama.happy++; + animateHappy(); + } + } + return; + } + + if (mode == 'clock') { + drawClock(); + if (!transition) { + return; + } + } + + drawTools(); + if (mode == 'status') { + drawStatus(); + return; + } + if (mode == 'food') { + drawFoodMenu(); + return; + } + if (mode == 'light') { + drawLight(); + return; + } + if (mode == 'happy') { + drawHappy(); + return; + } + if (mode == 'angry') { + drawAngry(); + return; + } + if (mode == 'medicine') { + if (tama.sick > 0) { + drawMedicine(); + } else { + animateAngry(); + } + return; + } + if (mode == 'eating') { + if (lightSelect == 0 && tama.hungry > 4) { + drawEatingNo(); + } else { + drawEating(); + } + return; + } + if (lightMode) { + // just dark screen and maybe zZz if its sleeping + g.setColor(0, 0, 0); + g.fillRect(0, 38, w + sx, h - 50); + if (tama.sleep) { + drawCaca(); + } + } else { + // draw tamagotchi + g.drawImage(n, x + sx, y, { scale: scale }); + // draw caca + drawCaca(); + } +} + +var statusMode = 0; +var lightSelect = 0; +var lightMode = 0; // on is zero +let frame = 0; + +function drawAngry () { + const one = angryState % 2; + g.drawImage(one ? tama06no0 : tama06no1, (scale * 5), 40, { scale: scale }); + g.drawImage(one ? angry0 : angry1, (scale * 20), 40, { scale: scale }); +} + +function drawHappy () { + const one = angryState % 2; + g.drawImage(one ? tama06happy : tama06no1, (scale * 5), 40, { scale: scale }); + if (one) { + g.drawImage(sun, (scale * 20), 46, { scale: scale }); + } +} + +function drawEatingNo () { // food eating animation + const one = angryState % 2; + + g.drawImage(lightSelect ? snack0 : meal0, scale, 40 + (scale * 7), { scale: scale }); + + g.drawImage(one ? tama06no0 : tama06no1, (scale * 10), 40, { scale: scale }); +} + +const med0 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('///4P/1//X/9f+AP+7/4P/o/+j/4P/g//H/+//7///8=') +}; +const med1 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('//////g//X/9f+AP+z/7P/o/+D/7P/g//H/+//7///8=') +}; + +const med2 = { + width: 16, + height: 16, + bpp: 1, + transparent: 1, + buffer: atob('////////+D/9f+AP+j/7P/s/+z/7v/g//H/+//7///8=') +}; + +function drawMedicine () { // food eating animation + const med = [med0, med1, med2]; + const img = med[0 | ((frame / 2) % 3)]; + if (img) { + g.drawImage(img, 0, 34, { scale: scale }); + } + g.drawImage(tama06no0, (scale * 10), 40, { scale: scale }); +} + +var sun = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('773nW9rnvfc=') +}; + +function drawEating () { // food eating animation + const one = angryState % 2; + const snack = [snack0, snack1, snack2]; + const meal = [meal0, meal1, meal2]; + const img = lightSelect ? snack[0 | (frame / 2)] : meal[0 | (frame / 2)]; + if (img) { + g.drawImage(img, scale, 40 + (scale * 7), { scale: scale }); + } + g.drawImage(one ? tama06no1 : tama06eat0, (scale * 10), 40, { scale: scale }); +} + +function drawFoodMenu () { // food menu + if (lightSelect == 0) { + g.drawImage(right, -scale, 40, { scale: scale }); + } else { + g.drawImage(right, -scale, 40 + (7 * scale), { scale: scale }); + } + g.drawImage(meal, scale * 5, 34, { scale: scale }); + g.drawImage(snack, scale * 5, 40 + (7 * scale), { scale: scale }); +} + +function drawLight () { + if (lightSelect == 0) { + g.drawImage(right, 2, 40, { scale: scale }); + } else { + g.drawImage(right, 2, 40 + (7 * scale), { scale: scale }); + } + g.drawImage(img_on, scale * 8, 34, { scale: scale }); + g.drawImage(img_off, scale * 8, 40 + (7 * scale), { scale: scale }); +} + +function drawTools () { + if (tool >= 0) { + // top actions + if (tool == 0) { g.drawImage(tool00, 10, 2); } + if (tool == 1) { g.drawImage(tool01, 10 + 30 + 10, 2); } + if (tool == 2) { g.drawImage(tool02, 10 + 30 + 10 + 30 + 10, 2); } + if (tool == 3) { g.drawImage(tool03, 10 + 30 + 10 + 30 + 10 + 30 + 10, 2); } + // bottom actions + if (tool == 4) { g.drawImage(tool10, 10, 135); } + if (tool == 5) { g.drawImage(tool11, 10 + 30 + 10, 135); } + if (tool == 6) { g.drawImage(tool12, 10 + 30 + 10 + 30 + 10, 135); } + } +} + +// this function is executed once per second. so the animations look stable and consistent +function updateAnimation () { + frame++; + if (evolution == 0) { + // animate the egg + egg = (egg == egg00) ? egg01 : egg00; + return; + } + if (mode == 'game') { + // console.log("update Animation"); + if (transition) { + const beep = frame % 4; + if (beep == 0) { + Bangle.beep(150, 4000); + } else if (beep == 2) { + Bangle.beep(150, 3200); + } + } else { + Bangle.beep(100); + } + if (gameChoice != 0) { + // do things + gameChoice = 0; + if ((0 | (Math.random() * 3)) > 0) { + animateHappy(); + gameWins++; + } else { + animateAngry(); + } + } + return; + } + if (mode == 'medicine') { + if (frame > 3) { + mode = ''; + tama.sick = 0; + } + } + x += (scale) * hd; + if (x + (tama00.width * scale) >= w) { + hd = -hd; + } + if (x < 0) { + hd = -hd; + } + caca = (caca == caca00) ? caca01 : caca00; + // y += vd * scale; + vd = -vd; + const width = (w / scale); + if (tama.sleep) { + n = tama00; + x = (width / 2); + } else { + n = n == tama00 ? tama01 : tama00; + if (tama.cacas > 0 || tama.sick > 0) { + if (x > (width / 2)) { + hd = -1; + x = (width / 2); + } + } + } +} + +function nextItem () { + tool++; + if (tool > 6) tool = 0; +} +function prevItem () { + tool--; + if (tool < 0) tool = 7; +} + +function activateItem () { + if (mode != '') { + return; + } + switch (tool) { + case -1: + animateToClock(); + break; + case 0: // food + if (tama.sleep) { + } else { + // evolution = 0; + mode = 'food'; + lightSelect = 0; + } + break; + case 1: // onoff + mode = 'light'; + break; + case 2: // game + if (tama.sleep) { + } else { + animateToGame(); + } + break; + case 3: // vax + if (tama.sleep) { + // cant medicate if sleeping + } else { + mode = 'medicine'; + frame = 0; + angryState = 0; + } + break; + case 4: // shower + if (tama.sleep) { + tama.happy = 0; + } + tama.awake = 10; // time to go to sleep again if in time + tama.sleep = false; + animateShower(); + break; + case 5: // status + mode = 'status'; + statusMode = 0; + break; + case 6: // blame + if (tama.sleep) { + tama.happy = 0; + tama.sleep = false; + } else if (callForAttention) { + if (tama.happy > 0 && tama.hungry > 0 && tama.sick < 1) { + tama.discipline += 2; + callForAttention = false; + } else if (tama.sick > 0) { + tama.discipline--; + } + } + animateAngry(); + break; + } +} + +const skull = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('gwFtARGDq/8=') +}; + +const zz0 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('//H9+/fRf/8=') +}; + +const zz1 = { + width: 8, + height: 8, + bpp: 1, + transparent: 1, + buffer: atob('/8P79+/fw/8=') +}; + +const zz2 = { + width: 8, + height: 8, + bpp: 1, + transparent: 0, + buffer: atob('AA4CBAgugAA=') +}; +const zz3 = { + width: 8, + height: 8, + bpp: 1, + transparent: 0, + buffer: atob('ADwECBAgPAA=') +}; + +function drawCaca () { + if (mode == 'game') { + return; + } + if (!caca) { + caca = caca00; + } + let zz = [zz0, zz1]; + + if (lightMode) { + zz = [zz2, zz3]; + g.setColor(1, 1, 1); + var fi = ((frame) / 2) % 2; + g.drawImage(zz[fi ? 1 : 0], sx + w - (scale * 9), 40, { scale: scale }); + return; + } + g.setColor(0, 0, 0); + if (tama.sleep) { + var fi = ((frame) / 2) % 2; + g.drawImage(zz[fi ? 1 : 0], sx + w - (scale * 9), 34, { scale: scale }); + if (tama.sick > 0) { + g.drawImage(skull, sx + w - (scale * 9), 34 + (scale * 6), { scale: scale }); + } else if (tama.cacas > 0) { + g.drawImage(caca, sx + w - (scale * 11), 32 + (scale * 6), { scale: scale }); + } + } else if (tama.sick > 0) { + g.drawImage(skull, sx + w - (scale * 9), 34 + scale, { scale: scale }); + if (tama.cacas > 0) { + g.drawImage(caca, sx + w - (scale * 11), 32 + (scale * 6), { scale: scale }); + } + } else { + if (tama.cacas > 0) { + g.drawImage(caca, sx + w - (scale * 11), 34 + (scale * 6), { scale: scale }); + } + if (tama.cacas > 1) { + g.drawImage(caca, sx + w - (scale * 11), 24, { scale: scale }); + } + } +} +var angryState = 0; + +function animateHappy () { + if (transition || mode == 'happy') { + return; + } + angryState = 0; + mode = 'happy'; + transition = true; + const width = w / scale; + const cx = w; + var iv = setInterval(function () { + angryState++; + if (angryState > 3) { + clearInterval(iv); + transition = false; + angryState = 0; + mode = oldMode; + if (mode == 'game') { + gameTries++; + } + } + drawScene(); + }, 1000); +} + +function animateAngry () { + if (transition || mode == 'angry') { + return; + } + angryState = 0; + mode = 'angry'; + transition = true; + const width = w / scale; + const cx = w; + var iv = setInterval(function () { + angryState++; + if (angryState > 3) { + clearInterval(iv); + transition = false; + angryState = 0; + mode = oldMode; + if (mode == 'game') { + gameTries++; + } + } + drawScene(); + }, 1000); +} + +function animateFood () { + if (transition || mode == 'eating') { + return; + } + // XXX TODO this is printing the angry state not the eating one + angryState = 0; + mode = 'eating'; + tama.hungry++; + if (lightSelect == 1) { // snack + tama.happy++; + tama.hungry++; + tama.sickness += 2; + } + frame = 0; + transition = true; + const width = w / scale; + const cx = w; + var iv = setInterval(function () { + angryState++; + if (angryState > 3) { + clearInterval(iv); + transition = false; + angryState = 0; + mode = 'food'; + } + drawScene(); + }, 1000); +} + +function animateShower () { + if (transition) { + return; + } + transition = true; + const width = w / scale; + let cx = w; + var iv = setInterval(function () { + sx -= scale * 4; + drawScene(); + cx -= scale * 4; + g.setColor(1, 1, 1); + g.drawImage(shower, cx, 40 - scale, { scale: scale }); + if (cx < 0) { + clearInterval(iv); + mode = ''; + transition = false; + animated = true; + sx += width; + if (sx < 0) sx = 0; + if (tama.cacas > 0) { + // if it was dirty, play the happy animation + } + tama.cacas = 0; + drawScene(); + } + }, 100); +} + +function animateToGame () { + if (transition || mode == 'game') { + return; + } + mode = 'game'; + gameChoice = 0; + transition = true; + let cx = 0; + sx = -w; + animated = false; + var iv = setInterval(function () { + sx += scale * 2; + updateAnimation(); + drawScene(); + cx += scale * 2; + if (cx > w) { + clearInterval(iv); + sx = 0; + animated = true; + transition = false; + drawScene(); + } + }, 100); +} + +function animateToClock () { + if (transition) { + return; + } + if (mode == 'clock') { + return; + } + mode = 'clock'; + transition = true; + const width = w / scale; + let cx = w; + sx = 0; + animated = false; + var iv = setInterval(function () { + sx -= scale * 4; + drawScene(); + cx -= scale * 4; + g.setColor(0, 0, 0); + if (cx < 0) { + clearInterval(iv); + mode = 'clock'; + transition = false; + animated = true; + drawScene(); + } + }, 100); +} + +function animateFromClock () { + if (transition) { + return; + } + if (mode != 'clock') { + return; + } + transition = true; + let cx = 0; + const width = w / scale; + animated = false; + var iv = setInterval(function () { + sx += scale * 4; + drawScene(); + cx += scale * 4; + if (cx > w) { + clearInterval(iv); + mode = ''; + sx = 0; + animated = true; + transition = false; + drawScene(); + } + }, 100); +} + +function button (n) { + if (evolution == 0) { + if (n == 3) { + evolution = 1; + return; + } + } + if (mode == 'happy' || mode == 'angry') { + return; + } + + if (mode == 'game') { + /* + if (gameTries > 3) { + mode = ""; + gameWins = 0; + gameTries = 0; + //tama.tired++; + } + */ + switch (n) { + case 1: + // pick left + gameChoice = 1; + drawScene(); + oldMode = 'game'; + break; + case 2: + // pick right + gameChoice = 2; + drawScene(); + oldMode = 'game'; + break; + case 3: + mode = ''; + // exit game + break; + } + return; + } + if (mode == 'eating') { + Bangle.buzz(); + return; + } + Bangle.beep(150); + + switch (n) { + case 1: + switch (mode) { + case 'clock': + useAmPm = !useAmPm; + drawScene(); + break; + case 'food': + case 'light': + lightSelect = lightSelect ? 0 : 1; + drawScene(); + break; + case 'status': + if (oldMode == 'clock') { + } else { + statusMode++; + drawScene(); + } + break; + default: + nextItem(); + drawScene(); + break; + } + break; + case 2: + switch (mode) { + case 'clock': + animateFromClock(); + break; + case 'status': + if (oldMode == 'clock') { + } else { + statusMode++; + drawScene(); + } + break; + case 'food': + animateFood(); + break; + case 'light': + mode = ''; + lightMode = lightSelect; + drawScene(); + break; + default: + activateItem(); + tool = -1; + drawScene(); + } + break; + case 3: + switch (mode) { + case 'clock': + animateFromClock(); + break; + case 'light': + case 'food': + mode = ''; + lightState = 0; + drawScene(); + break; + case 'status': + if (oldMode == 'clock') { + mode = 'clock'; + oldMode = ''; + } else { + mode = ''; + statusMode = 0; + drawScene(); + } + break; + default: + mode = ''; + tool = -1; + drawScene(); + break; + } + break; + } +} + +function drawGame () { + g.setColor(0, 0, 0); + + let one = frame % 2; + if (transition) { + one = 0; + g.drawImage(heart1, sx + w + (scale * 6), 40, { scale: scale }); + g.drawImage(heart1, sx + w + (scale * 16), 40, { scale: scale }); + g.drawImage(heart0, sx + w, 40 + (scale * 8), { scale: scale }); + g.drawImage(heart0, sx + w + (scale * 12), 40 + (scale * 8), { scale: scale }); + } else { + if (gameTries > 4) { + if (oldMode != '') { + if (gameWins > 2) { + animateHappy(); + } + } + mode = oldMode; + oldMode = ''; + // g.drawImage(); + } else { + g.drawImage(one ? tama06no1 : tama06no0, (scale * 7) + sx, 40, { scale: scale }); + } + } +} + +function drawClock () { + const d = new Date(); + let hh = ''; + if (useAmPm) { + const h = (d.getHours() > 12) ? d.getHours() - 12 : d.getHours(); + hh = (h < 10) ? ' ' + h : '' + h; + } else { + hh = (d.getHours() < 10) ? ' ' + d.getHours() : '' + d.getHours(); + } + const mm = (d.getMinutes() < 10) ? '0' + d.getMinutes() : '' + d.getMinutes(); + const ss = (d.getSeconds() < 10) ? '0' + d.getSeconds() : '' + d.getSeconds(); + const ts = hh + ':' + mm; + const useVector = false; + const wsx = w + sx + ((2.4) * scale); + + if (useVector) { + g.setFont('Vector', 60); + g.setColor(0, 0, 0); + g.drawString(ts, w + sx + 30, 54); + g.setFont('Vector', 24); + g.setColor(0, 0, 0); + g.drawString(ss, w + sx + (w - 20), 104); + } else { + const s0 = numbers[ts[0] - '0']; + const s1 = numbers[ts[1] - '0']; + const s2 = numbers[ts[3] - '0']; + const s3 = numbers[ts[4] - '0']; + const yy = 34; + // hours + if (s0) { + g.drawImage(s0, wsx, yy, { scale: scale }); + } + g.drawImage(s1, wsx + (5 * scale), yy, { scale: scale }); + g.drawImage(colon, wsx + (scale + scale + scale + (5 * scale)), yy, { scale: scale }); + // minutes + g.drawImage(s2, wsx + (2 * scale) + (5 * 2 * scale), yy, { scale: scale }); + g.drawImage(s3, wsx + (2 * scale) + (5 * 3 * scale), yy, { scale: scale }); + // seconds + const s4 = snumbers[ss[0] - '0']; + const s5 = snumbers[ss[1] - '0']; + g.drawImage(s4, wsx + (3 * scale) + (3 * 6 * scale), yy, { scale: scale }); + g.drawImage(s5, wsx + scale + (4 * 6 * scale), yy, { scale: scale }); + const arrows = [ + '00000', + '10000', + '11000', + '11100', + '11110', + '11111', + '01111', + '00111', + '00011', + '00001' + ]; + // arrow + for (let i = 0; i < 5; i++) { + const n = d.getSeconds() % 10; + const arrow = arrows[n]; + const img = (arrow[i] == '1') ? right1 : right0; + g.drawImage(img, wsx + (3 * i * scale) + (scale * 14), yy + (10 * scale), { scale: scale }); + } + } + if (useAmPm) { + if (d.getHours() < 13) { + g.drawImage(am, wsx, yy + (8 * scale), { scale: scale }); + } else { + g.drawImage(pm, wsx, yy + (8 * scale), { scale: scale }); + } + } else { + g.drawImage(h24, wsx, yy + (8 * scale), { scale: scale }); + // show something from tamagotchi stats + } +} + +setInterval(function () { + // if (animated) { + updateAnimation(); + drawScene(); + // } +}, 1000); + +let cacaLevel = 0; +let cacaBirth = null; + +setInterval(function () { + // poo maker + if (tama.hungry > 0 && !tama.sleep) { + const a = 0 | (cacaLevel / tama.tummy); + const b = 0 | ((cacaLevel + tama.hungry) / tama.tummy); + cacaLevel += tama.hungry; + if (a != b) { + if (tama.cacas == 0) { + cacaBirth = new Date(); + } + tama.hungry--; + tama.cacas++; + } + } + const d = new Date(); + const h = d.getHours(); + tama.sleep = (h > 22 || h < 8); + if (tama.awake > 0) { + tama.awake--; + tama.sleep = false; + } +}, 5000); + +setInterval(function () { + if (tama.sleep) { + return; + } + callForAttention = false; + + // health check + tama.sickness += tama.cacas; + if (tama.hungry == 0) { + callForAttention = true; + // tama.sickness++; + } + if (tama.hungry == 4) { + // tama.sickness++; + } + if (tama.sickness > tama.defenses) { + tama.sickness = 0; + tama.sick++; + } + if (tama.sick > 0) { + callForAttention = true; + } +}, 2000); + +updateAnimation(); + +Bangle.on('touch', function (r, s) { + const w4 = w / 3; + if (s.x > w - w4) { + if (s.y < 50) { + Bangle.beep(150); + if (oldMode == 'clock') { + oldMode = ''; + mode = 'clock'; + } else + if (mode == 'clock') { + mode = 'status'; + oldMode = 'clock'; + statusMode = 5; // battery + } else { + evolution = !evolution; + tool = -1; + } + drawScene(); + } else { + button(3); + } + } else if (s.x < w4) { + button(1); + } else { + button(2); + } +}); + diff --git a/apps/tabanchi/app.png b/apps/tabanchi/app.png new file mode 100644 index 000000000..7e653301d Binary files /dev/null and b/apps/tabanchi/app.png differ diff --git a/apps/tabanchi/metadata.json b/apps/tabanchi/metadata.json new file mode 100644 index 000000000..23de01869 --- /dev/null +++ b/apps/tabanchi/metadata.json @@ -0,0 +1,31 @@ +{ + "id": "tabanchi", + "name": "Tabanchi", + "shortName": "Tabanchi", + "version": "0.0.1", + "type": "app", + "description": "Tamagotchi WatchApp", + "icon": "app.png", + "allow_emulator": true, + "tags": "watch, pet", + "supports": [ + "BANGLEJS2" + ], + "readme": "README.md", + "storage": [ + { + "name": "tabanchi.app.js", + "url": "app.js" + }, + { + "name": "tabanchi.img", + "url": "app-icon.js", + "evaluate": true + } + ], + "screenshots": [ + { + "url": "screenshot.jpg" + } + ] +} diff --git a/apps/tabanchi/screenshot.jpg b/apps/tabanchi/screenshot.jpg new file mode 100644 index 000000000..fcd97df84 Binary files /dev/null and b/apps/tabanchi/screenshot.jpg differ diff --git a/apps/tapkb/ChangeLog b/apps/tapkb/ChangeLog new file mode 100644 index 000000000..624f1b0fb --- /dev/null +++ b/apps/tapkb/ChangeLog @@ -0,0 +1 @@ +0.01: Initial version \ No newline at end of file diff --git a/apps/tapkb/README.md b/apps/tapkb/README.md new file mode 100644 index 000000000..4f49b1494 --- /dev/null +++ b/apps/tapkb/README.md @@ -0,0 +1,7 @@ +# Tap Keyboard +This was originally designed for the Noteify app. With the new keyboard system in place, it has become its own keyboard app. + +## Usage +* Swipe left or right to cycle between the alphabet, numerals, and symbols. +* Hitting "caps" once will capitalize one character only. Hitting "caps" twice activates caps lock, and all subsequent characters will be capitalized until you hit "caps" again. +* "New line" is represented by a pilcrow (¶). When you hit the back button, these symbols will be converted into newline. diff --git a/apps/tapkb/app-icon.js b/apps/tapkb/app-icon.js new file mode 100644 index 000000000..c2b495917 --- /dev/null +++ b/apps/tapkb/app-icon.js @@ -0,0 +1 @@ +atob("MDCBAf////////////////AAAAAAD8AAAAAAA4AAAAAAAYAAAAAAAQf/////4A//////8A//////8A//////8A//////8A//////8A///8P/8A///8P/8A///8P/8A///8P/8A///8P/8A///8P/8A///8P//w///8P//w///8MP/w///8MP/w///8MMPw///8MMPw///8AMMA///8AAMA///8AAAA///8AAAA///8OAAA///8P+AA//wcP/8Af/AEP/8IA+AAP/8IA+AAP/8MA+DAP/8PA+BgP/8P//B4P/8P//g8P/8P//wfP/8P//4Pv/8P//8H//8P//8D//8P//+D//8P///B//4P///gAAAf///wAAAf///4AAA////8AADw==") \ No newline at end of file diff --git a/apps/tapkb/app.png b/apps/tapkb/app.png new file mode 100644 index 000000000..2b01ad280 Binary files /dev/null and b/apps/tapkb/app.png differ diff --git a/apps/tapkb/lib.js b/apps/tapkb/lib.js new file mode 100644 index 000000000..5ff524165 --- /dev/null +++ b/apps/tapkb/lib.js @@ -0,0 +1,162 @@ +exports.input = function(options) { + options = options||{}; + var text = options.text; + if ("string"!=typeof text) text=""; + +var layer = 0; +var caps = 0; + +class keyPad { + constructor(x1, y1, x2, y2, func) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + this.func = !func ? "" : func; + } + + draw() { + g.setColor(g.theme.fg).drawRect(this.x1, this.y1, this.x2, this.y2).clearRect(this.x1+1, this.y1+1, this.x2-1, this.y2-1).setFont("6x8",2).setFontAlign(0, 0, 0).drawString(this.func, (((this.x2-this.x1)/2)+this.x1), (((this.y2-this.y1)/2)+this.y1)); + } + + onTouch(xy) { + if (this.func == "space") text += " "; + else if (this.func == "<-") text = text.slice(0, -1); + else if (this.func == "new\nline") text += String.fromCharCode(182); + else if (this.func == "caps") { + caps = 1; + renderKeys(); + } + else if (this.func == "Caps") { + caps = 2; + renderKeys(); + } + else if (this.func == "CAPS") { + caps = 0; + renderKeys(); + } + else { + text += this.func; + if (caps == 1) caps = 0; + } + g.clearRect(25, 0, g.getWidth(), 25).setFontAlign(-1, -1).drawString(text.substring(text.length-12, text.length)+"_", 25, 7); + } +} + +function renderKeys() { + var a; + var i; + if (layer == 0) { + if (caps == 0) { + a = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "caps", "space", "<-"]; + } + else a = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "Caps", "space", "<-"]; + if (caps == 2) a[9] = "CAPS"; + for (i = 0; i < a.length; i++) { + pad[i].func = a[i]; + } + } + else if (layer == 1) { + if (caps == 0) { + a = ["j", "k", "l", "m", "n", "o", "p", "q", "r", "caps", "space", "<-"]; + } + else a = ["J", "K", "L", "M", "N", "O", "P", "Q", "R", "Caps", "space", "<-"]; + if (caps == 2) a[9] = "CAPS"; + for (i = 0; i < a.length; i++) { + pad[i].func = a[i]; + } + } + else if (layer == 2) { + if (caps == 0) { + a = ["s", "t", "u", "v", "w", "x", "y", "z", "0", "caps", "space", "<-"]; + } + else a = ["S", "T", "U", "V", "W", "X", "Y", "Z", "0", "Caps", "space", "<-"]; + if (caps == 2) a[9] = "CAPS"; + for (i = 0; i < a.length; i++) { + pad[i].func = a[i]; + } + } + else if (layer == 3) { + if (caps == 0) { + a = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "caps", "space", "<-"]; + } + else a = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "Caps", "space", "<-"]; + if (caps == 2) a[9] = "CAPS"; + for (i = 0; i < a.length; i++) { + pad[i].func = a[i]; + } + } + else if (layer == 4) { + if (caps == 0) { + a = [".", ",", "?", "!", "(", ")", "-", "\'", "new\nline", "caps", "space", "<-"]; + } + else a = ["-", "+", "/", "*", ":", "#", "$", "%", "new\nline", "Caps", "space", "<-"]; + if (caps == 2) a[9] = "CAPS"; + for (i = 0; i < a.length; i++) { + pad[i].func = a[i]; + } + } + + for (var j = 0; j < pad.length; j++) { + pad[j].draw(); + } +} + +var pad = []; +pad[0] = new keyPad(0, 29, 57, 64); +pad[1] = new keyPad(59, 29, 116, 64); +pad[2] = new keyPad(118, 29, 175, 64); +pad[3] = new keyPad(0, 66, 57, 101); +pad[4] = new keyPad(59, 66, 116, 101); +pad[5] = new keyPad(118, 66, 175, 101); +pad[6] = new keyPad(0, 103, 57, 138); +pad[7] = new keyPad(59, 103, 116, 138); +pad[8] = new keyPad(118, 103, 175, 138); +pad[9] = new keyPad(0, 140, 57, 175); +pad[10] = new keyPad(59, 140, 116, 175); +pad[11] = new keyPad(118, 140, 175, 175); +g.clear(); +renderKeys(); + +var drag; +var e; + +return new Promise((resolve,reject) => { + + Bangle.setUI({mode:"custom", drag:e=>{ + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } + else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + //horizontal swipes + if (Math.abs(dx)>Math.abs(dy)+10) { + //swipe left + if (dx<0) layer == 4 ? layer = 0 : layer++; + //swipe right + if (dx>0) layer == 0 ? layer = 4 : layer--; + } + } + renderKeys(); + },touch:(button, xy)=>{ + for (var i = 0; i < pad.length; i++) { + if ((xy.x >= pad[i].x1) && (xy.x <= pad[i].x2) && (xy.y >= pad[i].y1) && (xy.y <= pad[i].y2)) { + pad[i].onTouch(xy); + i = pad.length; + } + } + },back:()=>{ + Bangle.setUI(); + g.clear(); + resolve(text.replace(new RegExp(String.fromCharCode(182), 'g'), '\n')); + }}); + g.clearRect(25, 0, g.getWidth(), 25).setColor(g.theme.fg).setFont("6x8", 2); + if (text == "") g.setFontAlign(0, -1).drawString("<-Swipe->", g.getWidth()/2, 7); + else { + text = text.replace(/\n/g, String.fromCharCode(182)); + g.setFontAlign(-1, -1).drawString(text.substring(text.length-12, text.length)+"_", 25, 7); + } +}); + +}; diff --git a/apps/tapkb/metadata.json b/apps/tapkb/metadata.json new file mode 100644 index 000000000..c450f3d47 --- /dev/null +++ b/apps/tapkb/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "tapkb", + "name": "Tap keyboard", + "version":"0.01", + "description": "An onscreen tap keyboard.", + "icon": "app.png", + "type":"textinput", + "tags": "keyboard", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "screenshots": [{"url":"screenshot.png"}], + "storage": [ + {"name":"textinput","url":"lib.js"} + ] +} \ No newline at end of file diff --git a/apps/tapkb/screenshot.png b/apps/tapkb/screenshot.png new file mode 100644 index 000000000..bd1caed38 Binary files /dev/null and b/apps/tapkb/screenshot.png differ diff --git a/apps/terminalclock/ChangeLog b/apps/terminalclock/ChangeLog index 6515ab627..b752c829d 100644 --- a/apps/terminalclock/ChangeLog +++ b/apps/terminalclock/ChangeLog @@ -1,2 +1,5 @@ 0.01: New App! -0.02: Rename "Activity" in "Motion" and display the true values for it +0.02: Rename "Activity" in "Motion" and display the true values for it +0.03: Add Banglejs 1 compatibility +0.04: Fix settings bug +0.05: Add altitude display (only Bangle.js 2) diff --git a/apps/terminalclock/README.md b/apps/terminalclock/README.md index 5a54583d2..c7452397d 100644 --- a/apps/terminalclock/README.md +++ b/apps/terminalclock/README.md @@ -4,6 +4,7 @@ A clock displayed as a terminal cli. It can display : - time - date +- altitude - hrm - motion - steps diff --git a/apps/terminalclock/app.js b/apps/terminalclock/app.js index ab83a696f..61861f745 100644 --- a/apps/terminalclock/app.js +++ b/apps/terminalclock/app.js @@ -1,26 +1,39 @@ var locale = require("locale"); var fontColor = g.theme.dark ? "#0f0" : "#000"; -var paddingY = 2; -var font6x8At4Size = 32; -var font6x8At2Size = 18; var heartRate = 0; +var altitude = -9001; +// handling the differents versions of the Banglejs smartwatch +if (process.env.HWVERSION == 1){ + var paddingY = 3; + var font6x8At4Size = 48; + var font6x8At2Size = 27; + var font6x8FirstTextSize = 6; + var font6x8DefaultTextSize = 3; +} +else{ + var paddingY = 2; + var font6x8At4Size = 32; + var font6x8At2Size = 18; + var font6x8FirstTextSize = 4; + var font6x8DefaultTextSize = 2; +} function setFontSize(pos){ if(pos == 1) - g.setFont("6x8", 4); + g.setFont("6x8", font6x8FirstTextSize); else - g.setFont("6x8", 2); + g.setFont("6x8", font6x8DefaultTextSize); } function clearField(pos){ - var yStartPos = Bangle.appRect.y + - paddingY * (pos - 1) + - font6x8At4Size * Math.min(1, pos-1) + + var yStartPos = Bangle.appRect.y + + paddingY * (pos - 1) + + font6x8At4Size * Math.min(1, pos-1) + font6x8At2Size * Math.max(0, pos-2); - var yEndPos = Bangle.appRect.y + - paddingY * (pos - 1) + - font6x8At4Size * Math.min(1, pos) + + var yEndPos = Bangle.appRect.y + + paddingY * (pos - 1) + + font6x8At4Size * Math.min(1, pos) + font6x8At2Size * Math.max(0, pos-1); g.clearRect(Bangle.appRect.x, yStartPos, Bangle.appRect.x2, yEndPos); } @@ -32,9 +45,9 @@ function clearWatchIfNeeded(now){ function drawLine(line, pos){ setFontSize(pos); - var yPos = Bangle.appRect.y + - paddingY * (pos - 1) + - font6x8At4Size * Math.min(1, pos-1) + + var yPos = Bangle.appRect.y + + paddingY * (pos - 1) + + font6x8At4Size * Math.min(1, pos-1) + font6x8At2Size * Math.max(0, pos-2); g.drawString(line, 5, yPos, true); } @@ -72,6 +85,14 @@ function drawHRM(pos){ drawLine(">HR: unknown", pos); } +function drawAltitude(pos){ + clearField(pos); + if(altitude > 0) + drawLine(">Alt: " + altitude.toFixed(1) + "m", pos); + else + drawLine(">Alt: unknown", pos); +} + function drawActivity(pos){ clearField(pos); var health = Bangle.getHealthStatus('last'); @@ -92,6 +113,10 @@ function draw(){ drawDate(now, curPos); curPos++; } + if(settings.showAltitude){ + drawAltitude(curPos); + curPos++; + } if(settings.showHRM){ drawHRM(curPos); curPos++; @@ -112,6 +137,18 @@ Bangle.on('HRM',function(hrmInfo) { heartRate = hrmInfo.bpm; }); +var MEDIANLENGTH = 20; +var avr = [], median; +Bangle.on('pressure', function(e) { + while (avr.length>MEDIANLENGTH) avr.pop(); + avr.unshift(e.altitude); + median = avr.slice().sort(); + if (median.length>10) { + var mid = median.length>>1; + altitude = E.sum(median.slice(mid-4,mid+5)) / 9; + } +}); + // Clear the screen once, at startup g.clear(); @@ -123,7 +160,13 @@ var settings = Object.assign({ showHRM: true, showActivity: true, showStepCount: true, + showAltitude: process.env.HWVERSION != 1 ? true : false, }, require('Storage').readJSON("terminalclock.json", true) || {}); + +if(settings.showAltitude && process.env.HWVERSION != 1){ + Bangle.setBarometerPower(true, "app"); +} + // Show launcher when middle button pressed Bangle.setUI("clock"); // Load widgets diff --git a/apps/terminalclock/metadata.json b/apps/terminalclock/metadata.json index 6907da84d..7bc00bca4 100644 --- a/apps/terminalclock/metadata.json +++ b/apps/terminalclock/metadata.json @@ -3,11 +3,12 @@ "name": "Terminal Clock", "shortName":"Terminal Clock", "description": "A terminal cli like clock displaying multiple sensor data", - "version":"0.01", + "version":"0.05", "icon": "app.png", "type": "clock", "tags": "clock", - "supports": ["BANGLEJS2"], + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, "readme": "README.md", "storage": [ {"name": "terminalclock.app.js","url": "app.js"}, diff --git a/apps/terminalclock/settings.js b/apps/terminalclock/settings.js index 77df69b12..4b09aad6a 100644 --- a/apps/terminalclock/settings.js +++ b/apps/terminalclock/settings.js @@ -4,6 +4,7 @@ var settings = Object.assign({ HRMinConfidence: 50, showDate: true, + showAltitude: process.env.HWVERSION != 1 ? true : false, showHRM: true, showActivity: true, showStepCount: true, @@ -14,11 +15,11 @@ } // Show the menu - E.showMenu({ + var menu = { "" : { "title" : "Terminal Clock" }, "< Back" : () => back(), 'HR confidence': { - value: 50|settings.HRMinConfidence, // 0| converts undefined to 0 + value: settings.HRMinConfidence, min: 0, max: 100, onchange: v => { settings.HRMinConfidence = v; @@ -26,15 +27,23 @@ } }, 'Show date': { - value: !!settings.showDate, + value: settings.showDate, format: v => v?"Yes":"No", onchange: v => { settings.showDate = v; writeSettings(); } }, + 'Show Altitude': { + value: settings.showAltitude, + format: v => v?"Yes":"No", + onchange: v => { + settings.showAltitude = v; + writeSettings(); + } + }, 'Show HRM': { - value: !!settings.showHRM, + value: settings.showHRM, format: v => v?"Yes":"No", onchange: v => { settings.showHRM = v; @@ -42,7 +51,7 @@ } }, 'Show Activity': { - value: !!settings.showActivity, + value: settings.showActivity, format: v => v?"Yes":"No", onchange: v => { settings.showActivity = v; @@ -50,12 +59,16 @@ } }, 'Show Steps': { - value: !!settings.showStepCount, + value: settings.showStepCount, format: v => v?"Yes":"No", onchange: v => { settings.showStepCount = v; writeSettings(); } } - }); + } + if (process.env.HWVERSION == 1) { + delete menu['Show Altitude'] + } + E.showMenu(menu); }) diff --git a/apps/thermomF/app.js b/apps/thermomF/app.js index 2961e1efc..ac754d448 100644 --- a/apps/thermomF/app.js +++ b/apps/thermomF/app.js @@ -23,6 +23,6 @@ setInterval(function() { drawTemperature(); }, 20000); drawTemperature(); -E.showMessage("Loading..."); +E.showMessage(/*LANG*/"Loading..."); Bangle.loadWidgets(); Bangle.drawWidgets(); \ No newline at end of file diff --git a/apps/timecal/ChangeLog b/apps/timecal/ChangeLog index e48145b4b..57e7a1758 100644 --- a/apps/timecal/ChangeLog +++ b/apps/timecal/ChangeLog @@ -7,4 +7,5 @@ -> added settings to render cal view begin day (-1: today, 0:sunday, 1:monday [default]) 0.03: a lot of more settings for outline, colors and highlights 0.04: finalized README, fixed settings cancel, fixed border-setting -0.05: bugfix: default settings \ No newline at end of file +0.05: bugfix: default settings +0.06: bugfix: Mrk.Color doesn't reflect the color selected, fixes #1706 diff --git a/apps/timecal/README.md b/apps/timecal/README.md index d26f9ba4d..8c5d619ad 100644 --- a/apps/timecal/README.md +++ b/apps/timecal/README.md @@ -8,15 +8,15 @@ Shows the ### The settings menu Calendar View can be customized -* < Save: Exist and save the current settings -* Show date: Choose if and how the date is displayed: none, locale (default), monthfull or monthshort.yearshort #weeknum with 0 prefixed -* Start wday: Set day of week start. Values: 0=Sunday, 1=Monday,...,6=Saturday or -1=Relative to today (default 0: Sunday) -* Su color: Set Sundays color. Values: none (default), red, green or blue -* Border: show or none (default) +* < Save: Exit and save the current settings +* Show date: Choose if and how the date is displayed: none, locale [default], monthfull or monthshort.yearshort #weeknum with 0 prefixed +* Start wday: Set day of week start. Values: 0=Sunday, 1=Monday,...,6=Saturday or -1=Relative to today [default 0: Sunday] +* Su color: Set Sundays color. Values: none [default], red, green or blue +* Border: show or none [default] * Submenu Today settings - choose how today is highlighted * < Back: - * Color: none, red (default), green or blue - * Marker: Outline today graphically. Values: none (default), circle, rect(angle) - * Mrk.Color: Circle/rectangle color: red (default), green or blue - * Mrk.Size: Circle/rectangle thickness in pixel: min:1, max: 10, default:3 -* < Cancel: Exit and no change. Nevertheless missing default settings and superflous settings will be removed and saved. + * Color: none, red [default], green or blue + * Marker: Highlight today graphically. Values: none [default], circle, rectangle or filled + * Mrk.Color: Circle/rectangle color: red, green [default] or blue + * Mrk.Size: Circle/rectangle thickness in pixel: min: 1 to 10:max [default:3] +* < Cancel: Exit and no change. (Nevertheless missing default settings will be added and superflous settings will be removed.) diff --git a/apps/timecal/metadata.json b/apps/timecal/metadata.json index f439f4e9c..287dce0ae 100644 --- a/apps/timecal/metadata.json +++ b/apps/timecal/metadata.json @@ -1,7 +1,7 @@ { "id": "timecal", "name": "TimeCal", "shortName":"TimeCal", - "version":"0.05", + "version":"0.06", "description": "TimeCal shows the date/time along with a 3 week calendar", "icon": "icon.png", "type": "clock", diff --git a/apps/timecal/timecal.settings.js b/apps/timecal/timecal.settings.js index e86f3d8b8..8a7867c0d 100644 --- a/apps/timecal/timecal.settings.js +++ b/apps/timecal/timecal.settings.js @@ -64,9 +64,7 @@ format: v => v ? /*LANG*/"show" : /*LANG*/"none", onchange: v => chngdSttngs.calBrdr = v }, - /*LANG*/"Today settings": () => { - showTodayMenu(); - }, + /*LANG*/"Today settings": () => showTodayMenu(), /*LANG*/"< Cancel": () => cancelExitSettings() }); }; @@ -75,9 +73,9 @@ E.showMenu({ "": { "title": /*LANG*/"Today settings" - }, - "< Back": () => showMainMenu(), - /*LANG*/"Color": { + }, + "< Back": () => showMainMenu(), + /*LANG*/"Color": { value: chngdSttngs.tdyNumClr, min: 0, max: 3, format: v => [/*LANG*/"none", /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], @@ -91,8 +89,8 @@ }, /*LANG*/"Mrk.Color": { value: chngdSttngs.tdyMrkClr, - min: 0, max: 2, - format: v => [/*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], + min: 1, max: 3, + format: v => [undefined, /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], onchange: v => chngdSttngs.tdyMrkClr = v }, /*LANG*/"Mrk.Size": { @@ -101,8 +99,8 @@ format: v => v+"px", onchange: v => chngdSttngs.tdyMrkPxl = v }, - /*LANG*/"< Cancel": () => cancelExitSettings() - }); + /*LANG*/"< Cancel": () => cancelExitSettings() + }); }; showMainMenu(); diff --git a/apps/timerclk/ChangeLog b/apps/timerclk/ChangeLog index 5560f00bc..e17baa27c 100644 --- a/apps/timerclk/ChangeLog +++ b/apps/timerclk/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Add sunrise/sunset. Fix timer bugs. diff --git a/apps/timerclk/README.md b/apps/timerclk/README.md index fd6d2b16b..c27a8f6f8 100644 --- a/apps/timerclk/README.md +++ b/apps/timerclk/README.md @@ -11,6 +11,7 @@ A clock based on the Anton Clock with stopwatches, timers and alarms based on th * alarms * multiple stopwatches, timers and alarms * stopwatches and timers keep running in the background +* optional time of sunrise/sunset using the My Location app - hidden by default ## Images diff --git a/apps/timerclk/alarm.info b/apps/timerclk/alarm.info deleted file mode 100644 index 1289f8cef..000000000 --- a/apps/timerclk/alarm.info +++ /dev/null @@ -1 +0,0 @@ -{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/app.js b/apps/timerclk/app.js index eeb3ac4cd..c750fcfde 100644 --- a/apps/timerclk/app.js +++ b/apps/timerclk/app.js @@ -3,6 +3,23 @@ Graphics.prototype.setFontAnton = function(scale) { g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78+(scale<<8)+(1<<16)); }; +var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js"); +const LOCATION_FILE = "mylocation.json"; +let location; +var sunRise = "--:--"; +var sunSet = "--:--"; +var sunIcons = "\0" + atob("DwyBAAAAAAAAAAgAOAD4A/gP+D/4//gAAAAA") + "\0" + atob("FQyDAAAAAAAAAAAAAAAABAAAAAAABAAIABAAAAABAAABAAAAAAABJAAAAABAAJJJIABAABAJJJJIBAAAAJJJJJIAAAAJJJJJJIAAJBJJJJJJBIAAJJJJJJIAAAAAAAAAAAAA") + "\0" + atob("DwyBAAAAAAAAA//j/4P+A/gD4AOAAgAAAAAA"); + +function loadLocation() { + location = require('Storage').readJSON(LOCATION_FILE, true) || {lat:51.5072,lon:0.1276,location:"London"}; +} + +function updateSunRiseSunSet(location) { + var times = SunCalc.getTimes(new Date(), location.lat, location.lon); + sunRise = require("locale").time(times.sunrise, 1); + sunSet = require("locale").time(times.sunset, 1); +} + var timerclk = require("timerclk.lib.js"); var settings = require('Storage').readJSON("timerclk.json", true) || {}; settings = Object.assign({ @@ -12,11 +29,14 @@ settings = Object.assign({ "dateFontSize":2, "dowFont":"6x8", "dowFontSize":2, + "srssFont":"6x8", + "srssFontSize":2, "specialFont":"6x8", "specialFontSize":2, "shortDate":true, "showStopwatches":true, "showTimers":true, + "showSrss":false, }, settings.clock||{}); var stopwatches = [], timers = []; @@ -77,7 +97,12 @@ function drawSpecial() { queueDraw(drawSpecialTimeout, interval, drawSpecial); } +var drawCount=0; + function draw() { + if (drawCount++ % 60 == 0) { + updateSunRiseSunSet(location); + } var x = g.getWidth()/2; var y = g.getHeight()/2; g.reset(); @@ -85,6 +110,7 @@ function draw() { var timeStr = require("locale").time(date,1); var dateStr = require("locale").date(date,settings.shortDate).toUpperCase(); var dowStr = require("locale").dow(date).toUpperCase(); + var srssStr = sunRise + sunIcons + sunSet; // draw time if (settings.timeFont == "Anton") { @@ -105,6 +131,13 @@ function draw() { g.setFontAlign(0,0).setFont(settings.dowFont, settings.dowFontSize); y += g.stringMetrics(dowStr).height/2; g.drawString(dowStr,x,y); + if (settings.showSrss) { + // draw sun rise sun set + y += g.stringMetrics(dowStr).height/2; + g.setFontAlign(0,0).setFont(settings.srssFont, settings.srssFontSize); + y += g.stringMetrics(srssStr).height/2; + g.drawString(srssStr,x,y); + } // queue draw in one minute queueDraw(drawTimeout, 60000, draw); } @@ -147,5 +180,6 @@ Bangle.setUI("clock"); // Show launcher when middle button pressed g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); +loadLocation(); draw(); if (stopwatches || timers) drawSpecial(); diff --git a/apps/timerclk/boot.js b/apps/timerclk/boot.js index 9a09f68f3..b6eb05c14 100644 --- a/apps/timerclk/boot.js +++ b/apps/timerclk/boot.js @@ -1,26 +1,17 @@ var timerclkTimerTimeout; var timerclkAlarmTimeout; function timerclkCheckTimers() { + var expiresIn=require("timerclk.lib.js").timerExpiresIn; if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout); var timers = require('Storage').readJSON('timerclk.timer.json',1)||[]; timers = timers.filter(e=>e.start); if (timers.length) { - timers = timers.sort((a,b)=>{ - var at = a.timeAdd; - if (a.start) at += Date.now()-a.start; - at = a.period-at; - var bt = b.timeAdd; - if (b.start) bt += Date.now()-b.start; - bt = b.period-bt; - return at-bt; - }); + timers = timers.sort((a,b)=>expiresIn(a)-expiresIn(b)); if (!require('Storage').read("timerclk.timer.alert.js")) { console.log("No timer app!"); } else { - var time = timers[0].timeAdd; - if (timers[0].start) time += Date.now()-timers[0].start; - time = timers[0].time - time; - if (time<1000) t=1000; + var time = expiresIn(timers[0]); + if (time<1000) time=1000; if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout); timerclkTimerTimeout = setTimeout(() => load("timerclk.timer.alert.js"),time); } @@ -38,7 +29,7 @@ function timerclkCheckAlarms() { } else { var time = alarms[0].time-currentTime; if (alarms[0].last == new Date().getDate() || time < 0) time += 86400000; - if (time<1000) t=1000; + if (time<1000) time=1000; if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout); timerclkAlarmTimeout = setTimeout(() => load("timerclk.alarm.alert.js"),time); } diff --git a/apps/timerclk/lib.js b/apps/timerclk/lib.js index 718962fe0..dd3893fa1 100644 --- a/apps/timerclk/lib.js +++ b/apps/timerclk/lib.js @@ -125,3 +125,5 @@ exports.registerControls = function(o) { }); } }; + +exports.timerExpiresIn=t=>t.time-(Date.now()-t.start); diff --git a/apps/timerclk/metadata.json b/apps/timerclk/metadata.json index 6b415c0fc..7c6c7c9b3 100644 --- a/apps/timerclk/metadata.json +++ b/apps/timerclk/metadata.json @@ -2,7 +2,7 @@ "id": "timerclk", "name": "Timer Clock", "shortName":"Timer Clock", - "version":"0.01", + "version":"0.02", "description": "A clock with stopwatches, timers and alarms build in.", "icon": "app-icon.png", "type": "clock", @@ -28,10 +28,7 @@ {"name":"timerclk.timer.js","url":"timer.js"}, {"name":"timerclk.timer.alert.js","url":"timer.alert.js"}, {"name":"timerclk.alarm.js","url":"alarm.js"}, - {"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"}, - {"name":"timerclk.stopwatch.info","url":"stopwatch.info"}, - {"name":"timerclk.timer.info","url":"timer.info"}, - {"name":"timerclk.alarm.info","url":"alarm.info"} + {"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"} ], "data": [{"name":"timerclk.json"},{"name":"timerclk.stopwatch.json"},{"name":"timerclk.timer.json"},{"name":"timerclk.alarm.json"}], "sortorder": 0 diff --git a/apps/timerclk/settings.js b/apps/timerclk/settings.js index 556dded98..992985f52 100644 --- a/apps/timerclk/settings.js +++ b/apps/timerclk/settings.js @@ -12,9 +12,12 @@ "dowFontSize":2, "specialFont":"6x8", "specialFontSize":2, + "srssFont":"6x8", + "srssFontSize":2, "shortDate":true, "showStopwatches":true, "showTimers":true, + "showSrss":false, }, settings.clock||{}); settings.stopwatch = Object.assign({ "font":"Vector", @@ -108,6 +111,23 @@ writeSettings(); } }, + "sun font":{ + value: 0|g.getFonts().indexOf(settings.clock.srssFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.clock.srssFont = g.getFonts()[v]; + writeSettings(); + } + }, + "sun size":{ + value: 0|settings.clock.srssFontSize, + min: 0, + onchange: v => { + settings.clock.srssFontSize = v; + writeSettings(); + } + }, "short date": { value: !!settings.clock.shortDate, format: BOOL_FORMAT, @@ -132,6 +152,14 @@ writeSettings(); } }, + "sun times": { + value: !!settings.clock.showSrss, + format: v=>v?/*LANG*/"Show":/*LANG*/"Hide", + onchange: v => { + settings.clock.showSrss = v; + writeSettings(); + } + }, }; var stopwatchMenu = { diff --git a/apps/timerclk/stopwatch.info b/apps/timerclk/stopwatch.info deleted file mode 100644 index 72ad418b1..000000000 --- a/apps/timerclk/stopwatch.info +++ /dev/null @@ -1 +0,0 @@ -{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/timer.alert.js b/apps/timerclk/timer.alert.js index f51ea6767..96352097d 100644 --- a/apps/timerclk/timer.alert.js +++ b/apps/timerclk/timer.alert.js @@ -14,10 +14,7 @@ function showTimer(timer) { buttons : {/*LANG*/"Ok":true} }).then(function(ok) { buzzCount = 0; - if (ok) { - timer.time += Date.now() - timer.start; - timer.start = null; - } + timer.start = null; require("Storage").write("timerclk.timer.json",JSON.stringify(timers)); load(); }); @@ -45,16 +42,8 @@ console.log("checking for timers..."); var timers = require("Storage").readJSON("timerclk.timer.json",1)||[]; var active = timers.filter(e=>e.start); if (active.length) { - // if there's an timer, show it - active = active.sort((a,b)=>{ - var at = a.time; - if (a.start) at += Date.now()-a.start; - at = a.period-at; - var bt = b.time; - if (b.start) bt += Date.now()-b.start; - bt = b.period-bt; - return at-bt; - }); + // if there's an active timer, show it + active = active.sort((a,b)=>timerclk.timerExpiresIn(a)-timerclk.timerExpiresIn(b)); showTimer(active[0]); } else { // otherwise just go back to default app diff --git a/apps/timerclk/timer.info b/apps/timerclk/timer.info deleted file mode 100644 index 39a338693..000000000 --- a/apps/timerclk/timer.info +++ /dev/null @@ -1 +0,0 @@ -{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/timer.js b/apps/timerclk/timer.js index 060c07813..25052e6ae 100644 --- a/apps/timerclk/timer.js +++ b/apps/timerclk/timer.js @@ -34,18 +34,20 @@ function update() { } function play() { if (all[current].start) { // running - all[current].timeAdd += Date.now() - all[current].start; + all[current].timeAdd = Date.now() - all[current].start; all[current].start = null; update(); } else { // paused - all[current].start = Date.now(); + all[current].start = Date.now() - all[current].timeAdd; + all[current].timeAdd = 0; update(); } require("Storage").write("timerclk.timer.json",JSON.stringify(all)); timerclkCheckTimers(); } function reset() { - all[current] = defaultElement.clone(); + all[current].start = null; + all[current].timeAdd = 0; update(); require("Storage").write("timerclk.timer.json",JSON.stringify(all)); timerclkCheckTimers(); diff --git a/apps/todolist/ChangeLog b/apps/todolist/ChangeLog new file mode 100644 index 000000000..2e979ec12 --- /dev/null +++ b/apps/todolist/ChangeLog @@ -0,0 +1 @@ +0.01: Initial release \ No newline at end of file diff --git a/apps/todolist/README.md b/apps/todolist/README.md new file mode 100644 index 000000000..0e1beb74a --- /dev/null +++ b/apps/todolist/README.md @@ -0,0 +1,64 @@ +Todo List +======== + +This is a simple Todo List application. +The content is loaded from a JSON file. +A task can be marked as completed or uncompleted. + +![](screenshot2.png) + +Once installed, the list can be modified via the `Download data from app` icon in the [Bangle.js App Store](https://banglejs.com/apps/) (TodoList app). + +![](screenshot4.png) + + +JSON file content example: +```javascript +[ + { + "name": "Pro", + "children": [ + { + "name": "Read doc", + "done": true, + "children": [] + } + ] + }, + { + "name": "Pers", + "children": [ + { + "name": "Grocery", + "children": [ + { + "name": "Milk", + "done": false, + "children": [] + }, + { + "name": "Eggs", + "done": false, + "children": [] + }, + { + "name": "Cheese", + "done": false, + "children": [] + } + ] + }, + { + "name": "Workout", + "done": false, + "children": [] + }, + { + "name": "Learn Rust", + "done": false, + "children": [] + } + ] + } +] +``` \ No newline at end of file diff --git a/apps/todolist/app-icon.js b/apps/todolist/app-icon.js new file mode 100644 index 000000000..229852134 --- /dev/null +++ b/apps/todolist/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwgmjiMRiAWTgIXUCoYZQB4IADC4YHECxkSkIECkQYLEwMSkQQBkcyCAMTmYKEiIuGif/AAIXBmciiUzC4MvBQPyC44LCC4YADBYpIFiM/BYZDBC5EhC4wKCBYKLFEYkxC5UxCwsSBYgXK/5GEmYuDC5oAKC/4XUmK5DC6PziMfC6cimTRB+bbDiSpCC5ItBaIXxbIg2CF5QqBB4IcCAAQvMCYMhdIi//X7P/X6sz+S/CkQADX8gXCif/GQIADMwS/LZ4a//BgkyJBK/ll/zmYADX54FBX9cyB4ZHEO5wPDa/7RJAAshC4xyCABacBC40SGBsxiIWEgEBW4gAKFwowCABwWGACgA==")) \ No newline at end of file diff --git a/apps/todolist/app.js b/apps/todolist/app.js new file mode 100644 index 000000000..58cd3783c --- /dev/null +++ b/apps/todolist/app.js @@ -0,0 +1,129 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +// Const +let TODOLIST_FILE = "todolist.json"; +let MAX_DESCRIPTION_LEN = 14; + +// Clear todolist file +// require("Storage").erase(TODOLIST_FILE); + +let DEFAULT_TODOLIST = [ + { + name: "Pro", + children: [ + { + name: "Read doc", + done: true, + children: [], + }, + ], + }, + { + name: "Pers", + children: [ + { + name: "Grocery", + children: [ + { name: "Milk", done: false, children: [] }, + { name: "Eggs", done: false, children: [] }, + { name: "Cheese", done: false, children: [] }, + ], + }, + { name: "Workout", done: false, children: [] }, + { name: "Learn Rust", done: false, children: [] }, + ], + }, +]; + +// Load todolist +let todolist = + require("Storage").readJSON(TODOLIST_FILE, true) || DEFAULT_TODOLIST; +let menus = {}; + +function writeData() { + require("Storage").writeJSON(TODOLIST_FILE, todolist); +} + +function getChild(todolist, indexes) { + let childData = todolist; + for (let i = 0; i < indexes.length; i++) { + childData = childData[indexes[i]]; + childData = childData.children; + } + + return childData; +} + +function getName(item) { + let title = item.name.substr(0, MAX_DESCRIPTION_LEN); + return title; +} +function getParentTitle(todolist, indexes) { + let parentIndexes = indexes.slice(0, indexes.length - 1); + let lastIndex = indexes[indexes.length - 1]; + let item = getItem(todolist, parentIndexes, lastIndex); + return getName(item); +} + +function getItem(todolist, parentIndexes, index) { + let childData = getChild(todolist, parentIndexes, index); + return childData[index]; +} + +function toggleableStatus(todolist, indexes, index) { + const reminder = getItem(todolist, indexes, index); + return { + value: !!reminder.done, // !! converts undefined to false + format: (val) => (val ? "[X]" : "[-]"), + onchange: (val) => { + reminder.done = val; + writeData(); + }, + }; +} + +function showSubMenu(key) { + const sub_menu = menus[key]; + return E.showMenu(sub_menu); +} + +function createListItem(todolist, indexes, index) { + let reminder = getItem(todolist, indexes, index); + if (reminder.children.length > 0) { + let childIndexes = []; + for (let i = 0; i < indexes.length; i++) { + childIndexes.push(indexes[i]); + } + childIndexes.push(index); + createMenus(todolist, childIndexes); + return () => showSubMenu(childIndexes); + } else { + return toggleableStatus(todolist, indexes, index); + } +} + +function showMainMenu() { + const mainmenu = menus[""]; + return E.showMenu(mainmenu); +} + +function createMenus(todolist, indexes) { + const menuItem = {}; + if (indexes.length == 0) { + menuItem[""] = { title: "todolist" }; + } else { + menuItem[""] = { title: getParentTitle(todolist, indexes) }; + menuItem["< Back"] = () => + showSubMenu(indexes.slice(0, indexes.length - 1)); + } + for (let i = 0; i < getChild(todolist, indexes).length; i++) { + const item = getItem(todolist, indexes, i); + const name = getName(item); + menuItem[name] = createListItem(todolist, indexes, i); + } + menus[indexes] = menuItem; +} + +createMenus(todolist, []); +showMainMenu(); diff --git a/apps/todolist/app.png b/apps/todolist/app.png new file mode 100644 index 000000000..a93fc14ad Binary files /dev/null and b/apps/todolist/app.png differ diff --git a/apps/todolist/interface.html b/apps/todolist/interface.html new file mode 100644 index 000000000..5b9cb038e --- /dev/null +++ b/apps/todolist/interface.html @@ -0,0 +1,135 @@ + + + + + + + +
+ + + + + +

+
+    
+    
+  
+
diff --git a/apps/todolist/metadata.json b/apps/todolist/metadata.json
new file mode 100644
index 000000000..a8eb6118b
--- /dev/null
+++ b/apps/todolist/metadata.json
@@ -0,0 +1,24 @@
+{
+  "id": "todolist",
+  "name": "TodoList",
+  "shortName": "TodoList",
+  "version": "0.01",
+  "type": "app",
+  "description": "Simple Todo List",
+  "icon": "app.png",
+  "allow_emulator": true,
+  "tags": "tool,todo",
+  "supports": ["BANGLEJS", "BANGLEJS2"],
+  "readme": "README.md",
+  "interface": "interface.html",
+  "storage": [
+    { "name": "todolist.app.js", "url": "app.js" },
+    { "name": "todolist.img", "url": "app-icon.js", "evaluate": true }
+  ],
+  "data": [{ "name": "todolist.json" }],
+  "screenshots": [
+    { "url": "screenshot1.png" },
+    { "url": "screenshot2.png" },
+    { "url": "screenshot3.png" }
+  ]
+}
diff --git a/apps/todolist/screenshot1.png b/apps/todolist/screenshot1.png
new file mode 100644
index 000000000..523d60307
Binary files /dev/null and b/apps/todolist/screenshot1.png differ
diff --git a/apps/todolist/screenshot2.png b/apps/todolist/screenshot2.png
new file mode 100644
index 000000000..0337f9000
Binary files /dev/null and b/apps/todolist/screenshot2.png differ
diff --git a/apps/todolist/screenshot3.png b/apps/todolist/screenshot3.png
new file mode 100644
index 000000000..e5a4a85ac
Binary files /dev/null and b/apps/todolist/screenshot3.png differ
diff --git a/apps/todolist/screenshot4.png b/apps/todolist/screenshot4.png
new file mode 100644
index 000000000..43db1b0e6
Binary files /dev/null and b/apps/todolist/screenshot4.png differ
diff --git a/apps/toucher/app.js b/apps/toucher/app.js
index aab50fbda..19310592e 100644
--- a/apps/toucher/app.js
+++ b/apps/toucher/app.js
@@ -255,7 +255,7 @@ function run(){
     if (process.env.HWVERSION == 1) Bangle.setLCDMode();
     g.clear();
     g.flip();
-    E.showMessage("Loading...");
+    E.showMessage(/*LANG*/"Loading...");
     load(app.src);
   }
 
diff --git a/apps/touchmenu/metadata.json b/apps/touchmenu/metadata.json
index 825989d99..5335e9fd6 100644
--- a/apps/touchmenu/metadata.json
+++ b/apps/touchmenu/metadata.json
@@ -8,6 +8,7 @@
   "type": "bootloader",
   "tags": "tool",
   "supports": ["BANGLEJS2"],
+  "readme": "README.md",
   "storage": [
     {"name":"touchmenu.boot.js","url":"touchmenu.boot.js"}
   ]
diff --git a/apps/touchtimer/ChangeLog b/apps/touchtimer/ChangeLog
index 01904c6ea..f81907152 100644
--- a/apps/touchtimer/ChangeLog
+++ b/apps/touchtimer/ChangeLog
@@ -1,2 +1,5 @@
 0.01: Initial creation of the touch timer app
-0.02: Add settings menu
\ No newline at end of file
+0.02: Add settings menu
+0.03: Add ability to repeat last timer
+0.04: Add 5 second count down buzzer
+0.05: Fix 5 second count down buzzer to be only in the final 5 seconds
diff --git a/apps/touchtimer/app.js b/apps/touchtimer/app.js
index ffa1af80a..18c07feef 100644
--- a/apps/touchtimer/app.js
+++ b/apps/touchtimer/app.js
@@ -126,6 +126,19 @@ var main = () => {
       timerIntervalId = setInterval(() => {
         timerCountDown.draw();
 
+        // Buzz lightly when there are less then 5 seconds left
+        if (settings.countDownBuzz) {
+          var remainingSeconds = timerCountDown.getAdjustedTime().seconds;
+          var remainingMinutes = timerCountDown.getAdjustedTime().minutes;
+          var remainingHours = timerCountDown.getAdjustedTime().hours;
+          if (   remainingSeconds <= 5 
+              && remainingSeconds  > 0
+              && remainingMinutes <= 0
+              && remainingHours   <= 0) {
+            Bangle.buzz();
+          }
+        }
+
         if (timerCountDown.isFinished()) {
           buttonStartPause.value = "FINISHED!";
           buttonStartPause.draw();
@@ -141,6 +154,13 @@ var main = () => {
             if (buzzCount >= settings.buzzCount) {
               clearInterval(buzzIntervalId);
               buzzIntervalId = undefined;
+
+              buttonStartPause.value = "REPEAT";
+              buttonStartPause.draw();
+              buttonStartPause.value = "START";
+              timerCountDown = undefined;
+              timerEdit.draw();
+
               return;
             } else {
               Bangle.buzz(settings.buzzDuration * 1000, 1);
diff --git a/apps/touchtimer/metadata.json b/apps/touchtimer/metadata.json
index 645a0ce18..9261f3619 100644
--- a/apps/touchtimer/metadata.json
+++ b/apps/touchtimer/metadata.json
@@ -2,7 +2,7 @@
   "id": "touchtimer",
   "name": "Touch Timer",
   "shortName": "Touch Timer",
-  "version": "0.02",
+  "version": "0.05",
   "description": "Quickly and easily create a timer with touch-only input. The time can be easily set with a number pad.",
   "icon": "app.png",
   "tags": "tools",
diff --git a/apps/touchtimer/settings.js b/apps/touchtimer/settings.js
index 885670f57..79424f250 100644
--- a/apps/touchtimer/settings.js
+++ b/apps/touchtimer/settings.js
@@ -31,6 +31,14 @@
           writeSettings(settings);
         },
       },
+      "CountDown Buzz": {
+        value: !!settings.countDownBuzz,
+        format: value => value?"On":"Off",
+        onchange: (value) => {
+          settings.countDownBuzz = value;
+          writeSettings(settings);
+        },
+      },
       "Pause Between": {
         value: settings.pauseBetween,
         min: 1,
diff --git a/apps/vectorclock/ChangeLog b/apps/vectorclock/ChangeLog
index abbfcbb99..02831edde 100644
--- a/apps/vectorclock/ChangeLog
+++ b/apps/vectorclock/ChangeLog
@@ -5,3 +5,4 @@
 0.05: "Chime the time" (buzz or beep) with up/down swipe added
 0.06: Redraw widgets when time is updated
 0.07: Fix problem with "Bangle.CLOCK": github.com/espruino/BangleApps/issues/1437
+0.08: Redraw widgets only once per minute
diff --git a/apps/vectorclock/app.js b/apps/vectorclock/app.js
index 8d2961c4a..663a4c84f 100644
--- a/apps/vectorclock/app.js
+++ b/apps/vectorclock/app.js
@@ -81,7 +81,7 @@ function draw() {
 
   executeCommands();
 
-  Bangle.drawWidgets();
+  if (process.env.HWVERSION==2) Bangle.drawWidgets();
 }
 
 var timeout;
diff --git a/apps/vectorclock/metadata.json b/apps/vectorclock/metadata.json
index 0f558e3ee..541766fa2 100644
--- a/apps/vectorclock/metadata.json
+++ b/apps/vectorclock/metadata.json
@@ -1,7 +1,7 @@
 {
   "id": "vectorclock",
   "name": "Vector Clock",
-  "version": "0.07",
+  "version": "0.08",
   "description": "A digital clock that uses the built-in vector font.",
   "icon": "app.png",
   "type": "clock",
diff --git a/apps/viewstl/viewstl.app.js b/apps/viewstl/viewstl.app.js
index 0b2512176..34d018705 100644
--- a/apps/viewstl/viewstl.app.js
+++ b/apps/viewstl/viewstl.app.js
@@ -354,7 +354,7 @@ function loadFile(fn) {
   Bangle.setLCDMode("direct");
   g.clear();
   E.showMenu();
-  E.showMessage("Loading...", fn);
+  E.showMessage(/*LANG*/"Loading...", fn);
   readSTL(fn);
   zDist = 5*largestExtent(points);
   g.clear();
diff --git a/apps/viewstl/viewstl.min.js b/apps/viewstl/viewstl.min.js
index 77469042c..82975bbf9 100644
--- a/apps/viewstl/viewstl.min.js
+++ b/apps/viewstl/viewstl.min.js
@@ -216,7 +216,7 @@ function loadFile(fn) {
   Bangle.setLCDMode("direct");
   g.clear();
   E.showMenu();
-  E.showMessage("Loading...", fn);
+  E.showMessage(/*LANG*/"Loading...", fn);
   readSTL(fn);
   zDist = 5*largestExtent(points);
   g.clear();
diff --git a/apps/waveclk/ChangeLog b/apps/waveclk/ChangeLog
index 8c2a33143..f1fb77c59 100644
--- a/apps/waveclk/ChangeLog
+++ b/apps/waveclk/ChangeLog
@@ -1,2 +1,3 @@
 0.01: New App!
 0.02: Load widgets after setUI so widclk knows when to hide
+0.03: Show the day of the week
diff --git a/apps/waveclk/app.js b/apps/waveclk/app.js
index f1c67ce2f..18b28500b 100644
--- a/apps/waveclk/app.js
+++ b/apps/waveclk/app.js
@@ -41,6 +41,7 @@ function draw() {
   var date = new Date();
   var timeStr = require("locale").time(date,1);
   var dateStr = require("locale").date(date).toUpperCase();
+  var dowStr = require("locale").dow(date).toUpperCase();
   // draw time
   g.setFontAlign(0,0).setFont("ZCOOL");
   g.drawString(timeStr,x,y);
@@ -48,6 +49,9 @@ function draw() {
   y += 35;
   g.setFontAlign(0,0,1).setFont("6x8");
   g.drawString(dateStr,g.getWidth()-8,g.getHeight()/2);
+  // draw the day of the week
+  g.setFontAlign(0,0,3).setFont("6x8");
+  g.drawString(dowStr,8,g.getHeight()/2);
   // queue draw in one minute
   queueDraw();
 }
diff --git a/apps/waveclk/metadata.json b/apps/waveclk/metadata.json
index a8d270da2..0e9163157 100644
--- a/apps/waveclk/metadata.json
+++ b/apps/waveclk/metadata.json
@@ -1,13 +1,14 @@
 {
   "id": "waveclk",
   "name": "Wave Clock",
-  "version": "0.02",
+  "version": "0.03",
   "description": "A clock using a wave image by [Lillith May](https://www.instagram.com/_lilustrations_/)",
   "icon": "app.png",
   "screenshots": [{"url":"screenshot.png"}],
   "type": "clock",
   "tags": "clock",
   "supports": ["BANGLEJS","BANGLEJS2"],
+  "readme": "README.md",
   "allow_emulator": true,
   "storage": [
     {"name":"waveclk.app.js","url":"app.js"},
diff --git a/apps/waveclk/screenshot.png b/apps/waveclk/screenshot.png
index 7f05ce688..161ef96ef 100644
Binary files a/apps/waveclk/screenshot.png and b/apps/waveclk/screenshot.png differ
diff --git a/apps/waypointer/ChangeLog b/apps/waypointer/ChangeLog
new file mode 100644
index 000000000..7ccad08ea
--- /dev/null
+++ b/apps/waypointer/ChangeLog
@@ -0,0 +1,3 @@
+0.01: New app!
+0.02: Make Bangle.js 2 compatible
+0.03: Silently use built in heading when no magnav calibration file is present
diff --git a/apps/waypointer/README.md b/apps/waypointer/README.md
index e98fdbb7e..c0b4c5125 100644
--- a/apps/waypointer/README.md
+++ b/apps/waypointer/README.md
@@ -24,7 +24,7 @@ need to travel in to reach the selected waypoint. The blue text is
 the name of the current waypoint. NONE means that there is no
 waypoint set and so bearing and distance will remain at 0. To select
 a waypoint, press BTN2 (middle) and wait for the blue text to turn
-white. Then use BTN1 and BTN3 to select a waypoint. The waypoint
+white. Then use BTN1 and BTN3 (swipe up/down on Bangle.js 2) to select a waypoint. The waypoint
 choice is fixed by pressing BTN2 again. In the screen shot below a
 waypoint giving the location of Stone Henge has been selected.
 
diff --git a/apps/waypointer/app.js b/apps/waypointer/app.js
index d3aab7c50..bdb6f6857 100644
--- a/apps/waypointer/app.js
+++ b/apps/waypointer/app.js
@@ -1,24 +1,25 @@
-var pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
-var pal_bw = new Uint16Array([0x0000,0xffff],0,1);  // black, white
-var pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
+const scale = g.getWidth()/240;
+var pal_by = new Uint16Array([g.getBgColor(),0xFFC0],0,1); // black, yellow
+var pal_bw = new Uint16Array([g.getBgColor(),g.getColor()],0,1);  // black, white
+var pal_bb = new Uint16Array([g.getBgColor(),0x07ff],0,1); // black, blue
 
 // having 3 2 color pallette keeps the memory requirement lower
-var buf1 = Graphics.createArrayBuffer(160,160,1, {msb:true});
-var buf2 = Graphics.createArrayBuffer(80,40,1, {msb:true});
+var buf1 = Graphics.createArrayBuffer(160*scale,160*scale,1, {msb:true});
+var buf2 = Graphics.createArrayBuffer(g.getWidth()/3,40*scale,1, {msb:true});
 var arrow_img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));
 
 function flip1(x,y) {
-  g.drawImage({width:160,height:160,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
+  g.drawImage({width:160*scale,height:160*scale,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
   buf1.clear();
 }
 
 function flip2_bw(x,y) {
-  g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
+  g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
   buf2.clear();
 }
 
 function flip2_bb(x,y) {
-  g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
+  g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
   buf2.clear();
 }
 
@@ -51,12 +52,12 @@ function drawCompass(course) {
   previous.course = course;
   
   buf1.setColor(1);
-  buf1.fillCircle(80,80,79,79);
+  buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale);
   buf1.setColor(0);
-  buf1.fillCircle(80,80,69,69);
+  buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,69*scale);
   buf1.setColor(1);
-  buf1.drawImage(arrow_img, 80, 80, {scale:3,  rotate:radians(course)} );
-  flip1(40, 30);
+  buf1.drawImage(arrow_img, buf1.getWidth()/2, buf1.getHeight()/2, {scale:3*scale,  rotate:radians(course)} );
+  flip1(40*scale, Bangle.appRect.y+6*scale);
 }
 
 /***** COMPASS CODE ***********/
@@ -73,11 +74,14 @@ function newHeading(m,h){
     return hd;
 }
 
-var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
+var CALIBDATA = require("Storage").readJSON("magnav.json",1) || {};
 
 function tiltfixread(O,S){
-  var start = Date.now();
   var m = Bangle.getCompass();
+  if (O === undefined || S === undefined) {
+    // no valid calibration from magnav, use built in
+    return 360-m.heading;
+  }
   var g = Bangle.getAccel();
   m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z;
   var d = Math.atan2(-m.dx,m.dy)*180/Math.PI;
@@ -96,6 +100,7 @@ function tiltfixread(O,S){
 // Note actual mag is 360-m, error in firmware
 function read_compass() {
   var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
+  if (isNaN(d)) return; // built in compass heading can return NaN when uncalibrated
   heading = newHeading(d,heading);
   direction = wp_bearing - heading;
   if (direction < 0) direction += 360;
@@ -138,7 +143,7 @@ function distance(a,b){
 
 
 function drawN(){
-  buf2.setFont("Vector",24);
+  buf2.setFont("Vector",24*scale);
   var bs = wp_bearing.toString();
   bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
   var dst = loc.distance(dist);
@@ -147,12 +152,12 @@ function drawN(){
   
   // show distance on the left
   if (previous.dst !== dst) {
-    previous.dst = dst
+    previous.dst = dst;
     buf2.setColor(1);
     buf2.setFontAlign(-1,-1);
-    buf2.setFont("Vector", 20);
+    buf2.setFont("Vector", 20*scale);
     buf2.drawString(dst,0,0);
-    flip2_bw(0, 200);
+    flip2_bw(0, g.getHeight()-40*scale);
   }
   
   // bearing, place in middle at bottom of compass
@@ -160,9 +165,9 @@ function drawN(){
     previous.bs = bs;
     buf2.setColor(1);
     buf2.setFontAlign(0, -1);
-    buf2.setFont("Vector",38);
-    buf2.drawString(bs,40,0);
-    flip2_bw(80, 200);
+    buf2.setFont("Vector",38*scale);
+    buf2.drawString(bs,40*scale,0);
+    flip2_bw(g.getWidth()/3, g.getHeight()-40*scale);
   }
 
   // waypoint name on right
@@ -170,13 +175,13 @@ function drawN(){
     previous.selected = selected;
     buf2.setColor(1);
     buf2.setFontAlign(1,-1);     // right, bottom
-    buf2.setFont("Vector", 20);
-    buf2.drawString(wp.name, 80, 0);
+    buf2.setFont("Vector", 20*scale);
+    buf2.drawString(wp.name, 80*scale, 0);
 
     if (selected)
-      flip2_bw(160, 200);
+      flip2_bw(g.getWidth()/3*2, g.getHeight()-40*scale);
     else
-      flip2_bb(160, 200);
+      flip2_bb(g.getWidth()/3*2, g.getHeight()-40*scale);
   }
 }
 
@@ -229,9 +234,11 @@ function startdraw(){
 }
 
 function setButtons(){
-  setWatch(nextwp.bind(null,-1), BTN1, {repeat:true,edge:"falling"});
-  setWatch(doselect, BTN2, {repeat:true,edge:"falling"});
-  setWatch(nextwp.bind(null,1), BTN3, {repeat:true,edge:"falling"});
+  Bangle.setUI("updown", d=>{
+    if (d<0) { nextwp(-1); }
+    else if (d>0) { nextwp(1); }
+    else { doselect(); }
+  });
 }
  
 Bangle.on('lcdPower',function(on) {
diff --git a/apps/waypointer/metadata.json b/apps/waypointer/metadata.json
index cb477107b..707da94cf 100644
--- a/apps/waypointer/metadata.json
+++ b/apps/waypointer/metadata.json
@@ -1,11 +1,11 @@
 {
   "id": "waypointer",
   "name": "Way Pointer",
-  "version": "0.01",
+  "version": "0.03",
   "description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
   "icon": "waypointer.png",
   "tags": "tool,outdoors,gps",
-  "supports": ["BANGLEJS"],
+  "supports": ["BANGLEJS", "BANGLEJS2"],
   "readme": "README.md",
   "interface": "waypoints.html",
   "storage": [
diff --git a/apps/waypointer/waypoints.html b/apps/waypointer/waypoints.html
index d02260732..7a65821a2 100644
--- a/apps/waypointer/waypoints.html
+++ b/apps/waypointer/waypoints.html
@@ -73,8 +73,8 @@
         event.preventDefault()       
         var name = $name.value.trim()
         if(!name) return;
-        var lat = parseFloat($latitude.value).toPrecision(5);
-        var lon = parseFloat($longtitude.value).toPrecision(5);
+        var lat = parseFloat($latitude.value);
+        var lon = parseFloat($longtitude.value);
 
         waypoints.push({
           name, lat,lon,
diff --git a/apps/widalarmeta/metadata.json b/apps/widalarmeta/metadata.json
new file mode 100644
index 000000000..b6d8bd62b
--- /dev/null
+++ b/apps/widalarmeta/metadata.json
@@ -0,0 +1,15 @@
+{
+  "id": "widalarmeta",
+  "name": "Alarm & Timer ETA",
+  "shortName": "Alarm ETA",
+  "version": "0.01",
+  "description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h",
+  "icon": "widget.png",
+  "type": "widget",
+  "tags": "widget",
+  "supports": ["BANGLEJS","BANGLEJS2"],
+  "screenshots" : [ { "url":"screenshot.png" } ],
+  "storage": [
+    {"name":"widalarmeta.wid.js","url":"widget.js"}
+  ]
+}
diff --git a/apps/widalarmeta/screenshot.png b/apps/widalarmeta/screenshot.png
new file mode 100644
index 000000000..41a109557
Binary files /dev/null and b/apps/widalarmeta/screenshot.png differ
diff --git a/apps/widalarmeta/widget.js b/apps/widalarmeta/widget.js
new file mode 100644
index 000000000..0cddf953a
--- /dev/null
+++ b/apps/widalarmeta/widget.js
@@ -0,0 +1,35 @@
+(() => {
+  const alarms = require("Storage").readJSON("sched.json",1) || [];
+
+  function draw() {
+    const times = alarms.map(alarm => require("sched").getTimeToAlarm(alarm)).filter(a => a !== undefined);
+    const next = Math.min.apply(null, times);
+    if (next > 0 && next < 86400000) {
+      const hours = Math.floor((next % 86400000) / 3600000).toString();
+      const minutes = Math.floor(((next % 86400000) % 3600000) / 60000).toString();
+
+      g.reset(); // reset the graphics context to defaults (color/font/etc)
+      g.setFontAlign(0,0); // center fonts
+      g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23);
+
+      var text = hours.padStart(2, '0') + ":" + minutes.padStart(2, '0');
+      g.setFont("6x8:1x2");
+      g.drawString(text, this.x+this.width/2, this.y+12);
+      if (this.width === 0) {
+          this.width = 6*5+2;
+          Bangle.drawWidgets(); // width changed, re-layout
+      }
+    }
+  }
+
+  setInterval(function() {
+    WIDGETS["widalarmeta"].draw(WIDGETS["widalarmeta"]);
+  }, 30000); // update every half minute
+
+  // add your widget
+  WIDGETS["widalarmeta"]={
+    area:"tl",
+    width: 0, // hide by default = assume no timer
+    draw:draw
+  };
+})();
diff --git a/apps/widalarmeta/widget.png b/apps/widalarmeta/widget.png
new file mode 100644
index 000000000..cfd942ea0
Binary files /dev/null and b/apps/widalarmeta/widget.png differ
diff --git a/apps/widbaroalarm/ChangeLog b/apps/widbaroalarm/ChangeLog
new file mode 100644
index 000000000..5786741c7
--- /dev/null
+++ b/apps/widbaroalarm/ChangeLog
@@ -0,0 +1,3 @@
+0.01: Initial version
+0.02: Do not warn multiple times for the same exceedance
+0.03: Fix crash
diff --git a/apps/widbaroalarm/README.md b/apps/widbaroalarm/README.md
new file mode 100644
index 000000000..fdc239170
--- /dev/null
+++ b/apps/widbaroalarm/README.md
@@ -0,0 +1,24 @@
+# Barometer alarm widget
+
+Get a notification when the pressure reaches defined thresholds.
+
+
+## Settings
+* Interval: check interval of sensor data in minutes. 0 to disable automatic check.
+* Low alarm: Toggle low alarm
+  * Low threshold: Warn when pressure drops below this value
+* High alarm: Toggle high alarm
+  * High threshold: Warn when pressure exceeds above this value
+* Drop alarm: Warn when pressure drops more than this value in the recent 3 hours (having at least 30 min of data)
+    0 to disable this alarm.
+* Raise alarm: Warn when pressure raises more than this value in the recent 3 hours (having at least 30 min of data)
+    0 to disable this alarm.
+* Show widget: Enable/disable widget visibility
+* Buzz on alarm: Enable/disable buzzer on alarm
+
+
+## Widget
+The widget shows two rows: pressure value of last measurement and pressure average of the the last three hours.
+
+## Creator
+Marco ([myxor](https://github.com/myxor))
diff --git a/apps/widbaroalarm/default.json b/apps/widbaroalarm/default.json
new file mode 100644
index 000000000..3d81baa81
--- /dev/null
+++ b/apps/widbaroalarm/default.json
@@ -0,0 +1,11 @@
+{
+  "buzz": true,
+  "lowalarm": false,
+  "min": 950,
+  "highalarm": false,
+  "max": 1030,
+  "drop3halarm": 2,
+  "raise3halarm": 0,
+  "show": true,
+  "interval": 15
+}
diff --git a/apps/widbaroalarm/metadata.json b/apps/widbaroalarm/metadata.json
new file mode 100644
index 000000000..134f03623
--- /dev/null
+++ b/apps/widbaroalarm/metadata.json
@@ -0,0 +1,19 @@
+{
+  "id": "widbaroalarm",
+  "name": "Barometer Alarm Widget",
+  "shortName": "Barometer Alarm",
+  "version": "0.03",
+  "description": "A widget that can alarm on when the pressure reaches defined thresholds.",
+  "icon": "widget.png",
+  "type": "widget",
+  "tags": "tool,barometer",
+  "supports": ["BANGLEJS2"],
+  "dependencies": {"notify":"type"},
+  "readme": "README.md",
+  "storage": [
+    {"name":"widbaroalarm.wid.js","url":"widget.js"},
+    {"name":"widbaroalarm.settings.js","url":"settings.js"},
+    {"name":"widbaroalarm.default.json","url":"default.json"}
+  ],
+  "data": [{"name":"widbaroalarm.json"}, {"name":"widbaroalarm.log"}]
+}
diff --git a/apps/widbaroalarm/settings.js b/apps/widbaroalarm/settings.js
new file mode 100644
index 000000000..bea6319d1
--- /dev/null
+++ b/apps/widbaroalarm/settings.js
@@ -0,0 +1,95 @@
+(function(back) {
+  const SETTINGS_FILE = "widbaroalarm.json";
+  const storage = require('Storage');
+  let settings = Object.assign(
+    storage.readJSON("widbaroalarm.default.json", true) || {},
+    storage.readJSON(SETTINGS_FILE, true) || {}
+  );
+
+  function save(key, value) {
+    settings[key] = value;
+    storage.write(SETTINGS_FILE, settings);
+  }
+
+  function showMainMenu() {
+    let menu ={
+      '': { 'title': 'Barometer alarm widget' },
+      /*LANG*/'< Back': back,
+      "Interval": {
+        value: settings.interval,
+        min: 0,
+        max: 120,
+        step: 1,
+        format: x => {
+          return x != 0 ? x + ' min' : 'off';
+        },
+        onchange: x => save("interval", x)
+      },
+      "Low alarm": {
+        value: settings.lowalarm,
+        format: x => {
+          return x ? 'Yes' : 'No';
+        },
+        onchange: x => save("lowalarm", x),
+      },
+      "Low threshold": {
+        value: settings.min,
+        min: 600,
+        max: 1000,
+        step: 5,
+        onchange: x => save("min", x),
+      },
+      "High alarm": {
+        value: settings.highalarm,
+        format: x => {
+          return x ? 'Yes' : 'No';
+        },
+        onchange: x => save("highalarm", x),
+      },
+      "High threshold": {
+        value: settings.max,
+        min: 700,
+        max: 1100,
+        step: 5,
+        onchange: x => save("max", x),
+      },
+      "Drop alarm": {
+        value: settings.drop3halarm,
+        min: 0,
+        max: 10,
+        step: 1,
+        format: x => {
+          return x != 0 ? x + ' hPa/3h' : 'off';
+        },
+        onchange: x => save("drop3halarm", x)
+      },
+      "Raise alarm": {
+        value: settings.raise3halarm,
+        min: 0,
+        max: 10,
+        step: 1,
+        format: x => {
+          return x != 0 ? x + ' hPa/3h' : 'off';
+        },
+        onchange: x => save("raise3halarm", x)
+      },
+      "Show widget": {
+        value: settings.show,
+        format: x => {
+          return x ? 'Yes' : 'No';
+        },
+        onchange: x => save('show', x)
+      },
+      "Buzz on alarm": {
+        value: settings.buzz,
+        format: x => {
+          return x ? 'Yes' : 'No';
+        },
+        onchange: x => save('buzz', x)
+      },
+    };
+    E.showMenu(menu);
+  }
+
+  showMainMenu();
+});
diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js
new file mode 100644
index 000000000..2745db8ad
--- /dev/null
+++ b/apps/widbaroalarm/widget.js
@@ -0,0 +1,239 @@
+(function() {
+  let medianPressure;
+  let threeHourAvrPressure;
+  let currentPressures = [];
+
+  const LOG_FILE = "widbaroalarm.log.json";
+  const SETTINGS_FILE = "widbaroalarm.json";
+  const storage = require('Storage');
+
+  let settings;
+
+  function loadSettings() {
+    settings = Object.assign(
+      storage.readJSON("widbaroalarm.default.json", true) || {},
+      storage.readJSON(SETTINGS_FILE, true) || {}
+    );
+  }
+
+  loadSettings();
+
+
+  function setting(key) {
+    return settings[key];
+  }
+
+  function saveSetting(key, value) {
+    settings[key] = value;
+    storage.write(SETTINGS_FILE, settings);
+  }
+
+  const interval = setting("interval");
+
+  let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours
+
+  function showAlarm(body, title) {
+    if (body == undefined) return;
+
+    require("notify").show({
+      title: title || "Pressure",
+      body: body,
+      icon: require("heatshrink").decompress(atob("jEY4cA///gH4/++mkK30kiWC4H8x3BGDmSGgYDCgmSoEAg3bsAIDpAIFkmSpMAm3btgIFDQwIGNQpTYkAIJwAHEgMoCA0JgMEyBnBCAW3KoQQDhu3oAIH5JnDBAW24IIBEYm2EYwACBCIACA"))
+    });
+
+    if (setting("buzz") &&
+      !(storage.readJSON('setting.json', 1) || {}).quiet) {
+      Bangle.buzz();
+    }
+  }
+
+
+  function didWeAlreadyWarn(key) {
+    return setting(key) == undefined || setting(key) > 0;
+  }
+
+  function checkForAlarms(pressure) {
+    if (pressure == undefined || pressure <= 0) return;
+
+    let alreadyWarned = false;
+
+    const ts = Math.round(Date.now() / 1000); // seconds
+    const d = {
+      "ts": ts,
+      "p": pressure
+    };
+
+    // delete entries older than 3h
+    for (let i = 0; i < history3.length; i++) {
+      if (history3[i]["ts"] < ts - (3 * 60 * 60)) {
+        history3.shift();
+      }
+    }
+    // delete oldest entries until we have max 50
+    while (history3.length > 50) {
+      history3.shift();
+    }
+
+    if (setting("lowalarm")) {
+      // Is below the alarm threshold?
+      if (pressure <= setting("min")) {
+        if (!didWeAlreadyWarn("lastLowWarningTs")) {
+          showAlarm("Pressure low: " + Math.round(pressure) + " hPa");
+          saveSetting("lastLowWarningTs", ts);
+          alreadyWarned = true;
+        }
+      } else {
+        saveSetting("lastLowWarningTs", 0);
+      }
+    } else {
+      saveSetting("lastLowWarningTs", 0);
+    }
+
+    if (setting("highalarm")) {
+      // Is above the alarm threshold?
+      if (pressure >= setting("max")) {
+        if (!didWeAlreadyWarn("lastHighWarningTs")) {
+          showAlarm("Pressure high: " + Math.round(pressure) + " hPa");
+          saveSetting("lastHighWarningTs", ts);
+          alreadyWarned = true;
+        }
+      } else {
+        saveSetting("lastHighWarningTs", 0);
+      }
+    } else {
+      saveSetting("lastHighWarningTs", 0);
+    }
+
+    if (history3.length > 0 && !alreadyWarned) {
+      // 3h change detection
+      const drop3halarm = setting("drop3halarm");
+      const raise3halarm = setting("raise3halarm");
+      if (drop3halarm > 0 || raise3halarm > 0) {
+        // we need at least 30min of data for reliable detection
+        if (history3[0]["ts"] > ts - (30 * 60)) {
+          return;
+        }
+
+        // Get oldest entry:
+        const oldestPressure = history3[0]["p"];
+        if (oldestPressure != undefined && oldestPressure > 0) {
+          const diff = oldestPressure - pressure;
+
+          // drop alarm
+          if (drop3halarm > 0 && oldestPressure > pressure) {
+            if (Math.abs(diff) > drop3halarm) {
+              if (!didWeAlreadyWarn("lastDropWarningTs")) {
+                showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " +
+                  Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop");
+                saveSetting("lastDropWarningTs", ts);
+              }
+            } else {
+              saveSetting("lastDropWarningTs", 0);
+            }
+          } else {
+            saveSetting("lastDropWarningTs", 0);
+          }
+
+          // raise alarm
+          if (raise3halarm > 0 && oldestPressure < pressure) {
+            if (Math.abs(diff) > raise3halarm) {
+              if (!didWeAlreadyWarn("lastRaiseWarningTs")) {
+                showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " +
+                  Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise");
+                saveSetting("lastRaiseWarningTs", ts);
+              }
+            } else {
+              saveSetting("lastRaiseWarningTs", 0);
+            }
+          } else {
+            saveSetting("lastRaiseWarningTs", 0);
+          }
+        }
+      }
+    }
+
+    history3.push(d);
+    // write data to storage
+    storage.writeJSON(LOG_FILE, history3);
+
+    // calculate 3h average for widget
+    let sum = 0;
+    for (let i = 0; i < history3.length; i++) {
+      sum += history3[i]["p"];
+    }
+    threeHourAvrPressure = sum / history3.length;
+  }
+
+
+
+  function baroHandler(data) {
+    if (data) {
+      const pressure = Math.round(data.pressure);
+      if (pressure == undefined || pressure <= 0) return;
+      currentPressures.push(pressure);
+    }
+  }
+
+  /*
+   turn on barometer power
+   take 5 measurements
+   sort the results
+   take the middle one (median)
+   turn off barometer power
+  */
+  function check() {
+    Bangle.setBarometerPower(true, "widbaroalarm");
+    setTimeout(function() {
+      currentPressures = [];
+
+      Bangle.getPressure().then(baroHandler);
+      Bangle.getPressure().then(baroHandler);
+      Bangle.getPressure().then(baroHandler);
+      Bangle.getPressure().then(baroHandler);
+      Bangle.getPressure().then(baroHandler);
+
+      setTimeout(function() {
+        Bangle.setBarometerPower(false, "widbaroalarm");
+
+        currentPressures.sort();
+
+        // take median value
+        medianPressure = currentPressures[3];
+        checkForAlarms(medianPressure);
+      }, 1000);
+    }, 500);
+  }
+
+  function reload() {
+    check();
+  }
+
+  function draw() {
+    if (global.WIDGETS != undefined && typeof WIDGETS === "object") {
+      WIDGETS["baroalarm"] = {
+        width: setting("show") ? 24 : 0,
+        reload: reload,
+        area: "tr",
+        draw: draw
+      };
+    }
+
+    g.reset();
+    if (setting("show") && medianPressure != undefined) {
+      g.setFont("6x8", 1).setFontAlign(1, 0);
+      g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6);
+      if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) {
+        g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10);
+      }
+    }
+  }
+
+  // Let's delay the first check a bit
+  setTimeout(function() {
+    check();
+    if (interval > 0) {
+      setInterval(check, interval * 60000);
+    }
+  }, 1000);
+
+})();
diff --git a/apps/widbaroalarm/widget.png b/apps/widbaroalarm/widget.png
new file mode 100644
index 000000000..5be292143
Binary files /dev/null and b/apps/widbaroalarm/widget.png differ
diff --git a/apps/widbaroalarm/widget24.png b/apps/widbaroalarm/widget24.png
new file mode 100644
index 000000000..2f0d5e4ce
Binary files /dev/null and b/apps/widbaroalarm/widget24.png differ
diff --git a/apps/widbt_notify/ChangeLog b/apps/widbt_notify/ChangeLog
new file mode 100644
index 000000000..b5a50210e
--- /dev/null
+++ b/apps/widbt_notify/ChangeLog
@@ -0,0 +1,10 @@
+0.02: Tweaks for variable size widget system
+0.03: Ensure redrawing works with variable size widget system
+0.04: Fix automatic update of Bluetooth connection status
+0.05: Make Bluetooth widget thinner, and when on a bright theme use light grey for disabled color
+0.06: Tweaking colors for dark/light themes and low bpp screens
+0.07: Memory usage improvements
+0.08: Disable LCD on, on bluetooth status change
+0.09: Vibrate on connection loss
+0.10: Bug fix
+0.11: Avoid too many notifications. Change disconnected colour to red.
diff --git a/apps/widbt_notify/metadata.json b/apps/widbt_notify/metadata.json
new file mode 100644
index 000000000..0b795c2c8
--- /dev/null
+++ b/apps/widbt_notify/metadata.json
@@ -0,0 +1,13 @@
+{
+  "id": "widbt_notify",
+  "name": "Bluetooth Widget with Notification",
+  "version": "0.11",
+  "description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.",
+  "icon": "widget.png",
+  "type": "widget",
+  "tags": "widget,bluetooth",
+  "supports": ["BANGLEJS","BANGLEJS2"],
+  "storage": [
+    {"name":"widbt_notify.wid.js","url":"widget.js"}
+  ]
+}
diff --git a/apps/widbt_notify/widget.js b/apps/widbt_notify/widget.js
new file mode 100644
index 000000000..47765f3d0
--- /dev/null
+++ b/apps/widbt_notify/widget.js
@@ -0,0 +1,46 @@
+WIDGETS.bluetooth_notify = {
+    area: "tr",
+    width: 15,
+    warningEnabled: 1,
+    draw: function() {
+        g.reset();
+        if (NRF.getSecurityStatus().connected) {
+            g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
+        } else {
+            // g.setColor(g.theme.dark ? "#666" : "#999");
+            g.setColor("#f00"); // red is easier to distinguish from blue
+        }
+        g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
+    },
+    
+    redrawCurrentApp: function(){
+        if(typeof(draw)=='function'){
+            draw();
+        }else{
+            load(); // fallback. This might reset some variables
+        }
+    },
+    
+    connect: function() {
+        WIDGETS.bluetooth_notify.draw();
+    },
+    
+    disconnect: function() {
+        if(WIDGETS.bluetooth_notify.warningEnabled == 1){
+            E.showMessage(/*LANG*/'Connection\nlost.', 'Bluetooth');
+            setInterval(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
+            
+            WIDGETS.bluetooth_notify.warningEnabled = 0;
+            setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
+            
+            var quiet       = (require('Storage').readJSON('setting.json',1)||{}).quiet;
+            if(!quiet){
+                Bangle.buzz(700, 1); // buzz on connection loss
+            }
+        }
+        WIDGETS.bluetooth_notify.draw();
+    }
+};
+
+NRF.on('connect', WIDGETS.bluetooth_notify.connect);
+NRF.on('disconnect', WIDGETS.bluetooth_notify.disconnect);
diff --git a/apps/widbt_notify/widget.png b/apps/widbt_notify/widget.png
new file mode 100644
index 000000000..1a884a62c
Binary files /dev/null and b/apps/widbt_notify/widget.png differ
diff --git a/apps/widcal/ChangeLog b/apps/widcal/ChangeLog
index a4bc24d1a..07b8f7424 100644
--- a/apps/widcal/ChangeLog
+++ b/apps/widcal/ChangeLog
@@ -1 +1,2 @@
-0.01: First version
\ No newline at end of file
+0.01: First version
+0.02: Fix memory leak
\ No newline at end of file
diff --git a/apps/widcal/metadata.json b/apps/widcal/metadata.json
index 74ab6d488..fc7d6dd1d 100644
--- a/apps/widcal/metadata.json
+++ b/apps/widcal/metadata.json
@@ -1,7 +1,7 @@
 {
   "id": "widcal",
   "name": "Calendar Widget",
-  "version": "0.01",
+  "version": "0.02",
   "description": "Widget with the current date",
   "icon": "widget.png",
   "type": "widget",
diff --git a/apps/widcal/widget.js b/apps/widcal/widget.js
index 4214d280a..d4a4676a7 100644
--- a/apps/widcal/widget.js
+++ b/apps/widcal/widget.js
@@ -24,7 +24,8 @@
         ]);
       }
       // redraw when date changes
-      setTimeout(()=>WIDGETS["cal"].draw(), (86401 - Math.floor(date/1000) % 86400)*1000);
+      if (WIDGETS["cal"].to) clearTimeout(WIDGETS["cal"].to);
+      WIDGETS["cal"].to = setTimeout(()=>WIDGETS["cal"].draw(), (86401 - Math.floor(date/1000) % 86400)*1000);
     }
   };
 })();
diff --git a/apps/widclose/ChangeLog b/apps/widclose/ChangeLog
new file mode 100644
index 000000000..4be6afb16
--- /dev/null
+++ b/apps/widclose/ChangeLog
@@ -0,0 +1 @@
+0.01: New widget!
\ No newline at end of file
diff --git a/apps/widclose/README.md b/apps/widclose/README.md
new file mode 100644
index 000000000..55c8de483
--- /dev/null
+++ b/apps/widclose/README.md
@@ -0,0 +1,7 @@
+# Close Button
+
+Adds a ![X](preview.png) button to close the current app and go back to the clock.   
+(Widget is not visible on the clock screen)
+
+![Light theme screenshot](screenshot_light.png)
+![Dark theme screenshot](screenshot_dark.png)
\ No newline at end of file
diff --git a/apps/widclose/icon.png b/apps/widclose/icon.png
new file mode 100644
index 000000000..1d95ba0ce
Binary files /dev/null and b/apps/widclose/icon.png differ
diff --git a/apps/widclose/metadata.json b/apps/widclose/metadata.json
new file mode 100644
index 000000000..e044a2d39
--- /dev/null
+++ b/apps/widclose/metadata.json
@@ -0,0 +1,15 @@
+{
+  "id": "widclose",
+  "name": "Close Button",
+  "version": "0.01",
+  "description": "A button to close the current app",
+  "readme": "README.md",
+  "icon": "icon.png",
+  "type": "widget",
+  "tags": "widget,tools",
+  "supports": ["BANGLEJS2"],
+  "screenshots": [{"url":"screenshot_light.png"},{"url":"screenshot_dark.png"}],
+  "storage": [
+    {"name":"widclose.wid.js","url":"widget.js"}
+  ]
+}
diff --git a/apps/widclose/preview.png b/apps/widclose/preview.png
new file mode 100644
index 000000000..d90a3b4c5
Binary files /dev/null and b/apps/widclose/preview.png differ
diff --git a/apps/widclose/screenshot_dark.png b/apps/widclose/screenshot_dark.png
new file mode 100644
index 000000000..58067a3b9
Binary files /dev/null and b/apps/widclose/screenshot_dark.png differ
diff --git a/apps/widclose/screenshot_light.png b/apps/widclose/screenshot_light.png
new file mode 100644
index 000000000..32817ea8d
Binary files /dev/null and b/apps/widclose/screenshot_light.png differ
diff --git a/apps/widclose/widget.js b/apps/widclose/widget.js
new file mode 100644
index 000000000..3a354018b
--- /dev/null
+++ b/apps/widclose/widget.js
@@ -0,0 +1,14 @@
+if (!Bangle.CLOCK) WIDGETS.close = {
+  area: "tr", width: 24, sortorder: 10, // we want the right-most spot please
+  draw: function() {
+    Bangle.removeListener("touch", this.touch);
+    Bangle.on("touch", this.touch);
+    g.reset().setColor("#f00").drawImage(atob( // hardcoded red to match setUI back button
+      // b/w version of preview.png, 24x24
+      "GBgBABgAAf+AB//gD//wH//4P//8P//8fn5+fjx+fxj+f4H+/8P//8P/f4H+fxj+fjx+fn5+P//8P//8H//4D//wB//gAf+AABgA"
+    ), this.x, this.y);
+  }, touch: function(_, c) {
+    const w = WIDGETS.close;
+    if (w && c.x>=w.x && c.x<=w.x+24 && c.y>=w.y && c.y<=w.y+24) load();
+  }
+};
\ No newline at end of file
diff --git a/apps/widgps/ChangeLog b/apps/widgps/ChangeLog
index f68fc701c..0eb9e5692 100644
--- a/apps/widgps/ChangeLog
+++ b/apps/widgps/ChangeLog
@@ -3,3 +3,4 @@
 0.03: Fix positioning
 0.04: Show GPS fix status
 0.05: Don't poll for GPS status, override setGPSPower handler (fix #1456)
+0.06: Periodically update so the always on display does show current GPS fix
diff --git a/apps/widgps/metadata.json b/apps/widgps/metadata.json
index 39bff2fad..b135c77bd 100644
--- a/apps/widgps/metadata.json
+++ b/apps/widgps/metadata.json
@@ -1,7 +1,7 @@
 {
   "id": "widgps",
   "name": "GPS Widget",
-  "version": "0.05",
+  "version": "0.06",
   "description": "Tiny widget to show the power and fix status of the GPS",
   "icon": "widget.png",
   "type": "widget",
diff --git a/apps/widgps/widget.js b/apps/widgps/widget.js
index bfdb89d33..206096013 100644
--- a/apps/widgps/widget.js
+++ b/apps/widgps/widget.js
@@ -1,4 +1,6 @@
 (function(){
+  var interval;
+
   // override setGPSPower so we know if GPS is on or off
   var oldSetGPSPower = Bangle.setGPSPower;
   Bangle.setGPSPower = function(on,id) {
@@ -19,6 +21,16 @@
     } else {
       g.setColor("#888"); // off = grey
     }
+
+    // check if we need to update the widget periodically
+    if (Bangle.isGPSOn() && interval === undefined) {
+      interval = setInterval(function() {
+        WIDGETS.gps.draw(WIDGETS.gps);
+      }, 10*1000); // update every 10 seconds to show gps fix/no fix
+    } else if (!Bangle.isGPSOn() && interval !== undefined) {
+      clearInterval(interval);
+      interval = undefined;
+    }
     g.drawImage(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="), this.x, 2+this.y);
   }};
 })();
diff --git a/apps/widmnth/ChangeLog b/apps/widmnth/ChangeLog
new file mode 100644
index 000000000..370f41e8a
--- /dev/null
+++ b/apps/widmnth/ChangeLog
@@ -0,0 +1 @@
+0.01: Simple new widget!
diff --git a/apps/widmnth/README.md b/apps/widmnth/README.md
new file mode 100644
index 000000000..ef912c739
--- /dev/null
+++ b/apps/widmnth/README.md
@@ -0,0 +1,22 @@
+# Widget Name
+
+The days left in month widget is simple and just prints the number of days left in the month in the top left corner.
+The idea is to encourage people to keep track of time and keep goals they may have for the month.
+
+## Usage
+
+Hopefully you just have to Install it and it'll work. Customizing the location would just be changing tl to tr.
+
+## Features
+
+* Shows days left in month
+* Only updates at midnight.
+
+
+## Requests
+
+Complaints,compliments,problems,suggestions,annoyances,bugs, and all other feedback can be filed at [this repo](https://github.com/N-Onorato/BangleApps)
+
+## Creator
+
+Nick
diff --git a/apps/widmnth/metadata.json b/apps/widmnth/metadata.json
new file mode 100644
index 000000000..25f3a8126
--- /dev/null
+++ b/apps/widmnth/metadata.json
@@ -0,0 +1,14 @@
+{ "id": "widmnth",
+  "name": "Days left in month widget",
+  "shortName":"Month Countdown",
+  "version":"0.01",
+  "description": "A simple widget that displays the number of days left in the month.",
+  "icon": "widget.png",  
+  "type": "widget",
+  "tags": "widget,date,time,countdown,month",
+  "supports" : ["BANGLEJS","BANGLEJS2"],
+  "readme": "README.md",
+  "storage": [
+    {"name":"widmnth.wid.js","url":"widget.js"}
+  ]
+}
diff --git a/apps/widmnth/widget.js b/apps/widmnth/widget.js
new file mode 100644
index 000000000..c4eca155a
--- /dev/null
+++ b/apps/widmnth/widget.js
@@ -0,0 +1,42 @@
+
+(() => {
+  var days_left;
+  var clearCode;
+
+  function getDaysLeft(day) {
+    let year = day.getMonth() == 11 ? day.getFullYear() + 1 : day.getFullYear(); // rollover if december.
+    next_month = new Date(year, (day.getMonth() + 1) % 12, 1, 0, 0, 0);
+    let days_left = Math.floor((next_month - day) / 86400000); // ms left in month divided by ms in a day
+    return days_left;
+  }
+
+  function getTimeTilMidnight(now) {
+    let midnight = new Date(now.getTime());
+    midnight.setHours(23);
+    midnight.setMinutes(59);
+    midnight.setSeconds(59);
+    midnight.setMilliseconds(999);
+    return (midnight - now) + 1;
+  }
+
+  function update() {
+    let now = new Date();
+    days_left = getDaysLeft(now);
+    let ms_til_midnight = getTimeTilMidnight(now);
+    clearCode = setTimeout(update, ms_til_midnight);
+  }
+
+  function draw() {
+    g.reset();
+    g.setFont("4x6", 3);
+    if(!clearCode) update(); // On first run calculate days left and setup interval to update state.
+    g.drawString(days_left < 10 ? "0" + days_left : days_left.toString(), this.x + 2, this.y + 4);
+  }
+
+  // add your widget
+  WIDGETS.widmonthcountdown={
+    area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
+    width: 24,
+    draw:draw
+  };
+})();
\ No newline at end of file
diff --git a/apps/widmnth/widget.png b/apps/widmnth/widget.png
new file mode 100644
index 000000000..4a042ec05
Binary files /dev/null and b/apps/widmnth/widget.png differ
diff --git a/apps/widslimbat/metadata.json b/apps/widslimbat/metadata.json
new file mode 100644
index 000000000..a83046e90
--- /dev/null
+++ b/apps/widslimbat/metadata.json
@@ -0,0 +1,13 @@
+{ "id": "widslimbat",
+  "name": "Slim battery widget with cells",
+  "shortName":"Slim battery with cells",
+  "version":"0.01",
+  "description": "A small (13px wide) battery widget with cells",
+  "icon": "widget.png",
+  "type": "widget",
+  "tags": "widget",
+  "supports" : ["BANGLEJS2"],
+  "storage": [
+    {"name":"widslimbat.wid.js","url":"widget.js"}
+  ]
+}
diff --git a/apps/widslimbat/widget.js b/apps/widslimbat/widget.js
new file mode 100644
index 000000000..4a8bb3b5d
--- /dev/null
+++ b/apps/widslimbat/widget.js
@@ -0,0 +1,55 @@
+(() => {
+  const intervalLow = 60000; // update time when not charging
+  const intervalHigh = 2000; // update time when charging
+  const outline = atob("CRSBAD4AP/AYDAYDAYDAYDAYDAYDAYDAYD/w");
+
+  let COLORS = {
+    'black':    g.theme.dark ? "#fff" : "#000",
+    'charging': "#0f0",
+    'low':      "#f00",
+  };
+
+  function draw() {
+    var i;
+    var oCol = COLORS.low;
+    var cCol = COLORS.low;
+    var nCells = 0;
+
+    const bat = E.getBattery();
+    if (bat>5) {
+      oCol = COLORS.black;
+      nCells = 1 + Math.floor((bat-6)/19);
+    }
+    if (nCells>1)
+      cCol = COLORS.black;
+    if (Bangle.isCharging())
+      oCol = COLORS.charging;
+    g.reset();
+    g.setColor(oCol).drawImage(outline,this.x+2,this.y+2);
+    for (i=0;iWIDGETS["widslimbat"].draw(),intervalLow);
+
+  WIDGETS["widslimbat"]={
+    area:"tr",
+    width:13,
+    draw:draw
+  };
+})();
diff --git a/apps/widslimbat/widget.png b/apps/widslimbat/widget.png
new file mode 100644
index 000000000..a9c7d416d
Binary files /dev/null and b/apps/widslimbat/widget.png differ
diff --git a/apps/widstep/ChangeLog b/apps/widstep/ChangeLog
new file mode 100644
index 000000000..55cda0f21
--- /dev/null
+++ b/apps/widstep/ChangeLog
@@ -0,0 +1 @@
+0.01: New widget
diff --git a/apps/widstep/README.md b/apps/widstep/README.md
new file mode 100644
index 000000000..c41b025cd
--- /dev/null
+++ b/apps/widstep/README.md
@@ -0,0 +1,9 @@
+# Step counter widget
+This is my step counter. There are many like it, but this one is mine.
+
+A pedometer widget designed to be as narrow as possible, but still easy to read, by sacrificing accuracy and only showing to the nearest 100 steps (0.1k).
+Shows a subtle fill colour in the background for progress to the goal. The goal is picked up from the health tracker settings.
+
+
+![](widstep-light.png)
+![](widstep-dark.png)
diff --git a/apps/widstep/icons8-winter-boots-48.png b/apps/widstep/icons8-winter-boots-48.png
new file mode 100644
index 000000000..7dceceef0
Binary files /dev/null and b/apps/widstep/icons8-winter-boots-48.png differ
diff --git a/apps/widstep/metadata.json b/apps/widstep/metadata.json
new file mode 100644
index 000000000..ea108e0f1
--- /dev/null
+++ b/apps/widstep/metadata.json
@@ -0,0 +1,17 @@
+{
+    "id": "widstep",
+    "name": "Step counter widget",
+    "version": "0.01",
+    "description": "Step counter widget, narrow but clearly readable",
+    "readme": "README.md",
+    "icon": "icons8-winter-boots-48.png",
+    "screenshots": [{"url":"widstep-light.png"},{"url":"widstep-dark.png"}],
+    "type": "widget",
+    "tags": "widget,health",
+    "supports": ["BANGLEJS","BANGLEJS2"],
+    "dependencies" : {"health":"app"},
+    "allow_emulator":false,
+    "storage": [
+      {"name":"widstep.wid.js","url":"widstep.wid.js"}
+    ]
+  }
diff --git a/apps/widstep/widstep-dark.png b/apps/widstep/widstep-dark.png
new file mode 100644
index 000000000..c8e1a8065
Binary files /dev/null and b/apps/widstep/widstep-dark.png differ
diff --git a/apps/widstep/widstep-light.png b/apps/widstep/widstep-light.png
new file mode 100644
index 000000000..9cce1e7c2
Binary files /dev/null and b/apps/widstep/widstep-light.png differ
diff --git a/apps/widstep/widstep.wid.js b/apps/widstep/widstep.wid.js
new file mode 100644
index 000000000..6ad971af7
--- /dev/null
+++ b/apps/widstep/widstep.wid.js
@@ -0,0 +1,23 @@
+let wsSettingsGoal = (require('Storage').readJSON("health.json", 1) || {}).stepGoal || 10000;
+
+Bangle.on('step', function(s) { WIDGETS["widstep"].draw(); });
+Bangle.on('lcdPower', function(on) {
+  if (on) WIDGETS["widstep"].draw();
+});
+WIDGETS["widstep"]={area:"tl", sortorder:-1, width:28,
+  draw:function() {
+    if (!Bangle.isLCDOn()) return; // dont redraw if LCD is off
+    var steps = Bangle.getHealthStatus("day").steps;
+    g.reset();
+    g.setColor(g.theme.bg);
+    g.fillRect(this.x, this.y, this.x + this.width, this.y + 23);
+    g.setColor(g.theme.dark ? '#00f' : '#0ff');
+    var progress = this.width * Math.min(steps/wsSettingsGoal, 1);
+    g.fillRect(this.x+1, this.y+1, this.x + progress -1, this.y + 23);
+    g.setColor(g.theme.fg);
+    g.setFontAlign(0, -1);
+    var steps_k = (steps/1000).toFixed(1) + 'k';
+    g.setFont('6x15').drawString(steps_k, this.x+this.width/2, this.y + 10);
+    g.setFont('4x6').drawString('steps', this.x+this.width/2, this.y + 2);
+  }
+};
diff --git a/backup.js b/backup.js
new file mode 100644
index 000000000..8a894666e
--- /dev/null
+++ b/backup.js
@@ -0,0 +1,124 @@
+/* Code to handle Backup/Restore functionality */
+
+const BACKUP_STORAGEFILE_DIR = "storage-files";
+
+function bangleDownload() {
+  var zip = new JSZip();
+  Progress.show({title:"Scanning...",sticky:true});
+  var normalFiles, storageFiles;
+  console.log("Listing normal files...");
+  Comms.reset()
+  .then(() => Comms.showMessage("Backing up..."))
+  .then(() => Comms.listFiles({sf:false}))
+  .then(f => {
+    normalFiles = f;
+    console.log(" - "+f.join(","));
+    console.log("Listing StorageFiles...");
+    return Comms.listFiles({sf:true});
+  }).then(f => {
+    storageFiles = f;
+    console.log(" - "+f.join(","));
+    var fileCount = normalFiles.length + storageFiles.length;
+    var promise = Promise.resolve();
+    // Normal files
+    normalFiles.forEach((filename,n) => {
+      if (filename==".firmware") {
+        console.log("Ignoring .firmware file");
+        return;
+      }
+      promise = promise.then(() => {
+        Progress.hide({sticky: true});
+        var percent = n/fileCount;
+        Progress.show({title:`Download ${filename}`,sticky:true,min:percent,max:percent+(1/fileCount),percent:0});
+        return Comms.readFile(filename).then(data => zip.file(filename,data));
+      });
+    });
+    // Storage files
+    if (storageFiles.length) {
+      var zipStorageFiles = zip.folder(BACKUP_STORAGEFILE_DIR);
+      storageFiles.forEach((filename,n) => {
+        promise = promise.then(() => {
+          Progress.hide({sticky: true});
+          var percent = (normalFiles.length+n)/fileCount;
+          Progress.show({title:`Download ${filename}`,sticky:true,min:percent,max:percent+(1/fileCount),percent:0});
+          return Comms.readStorageFile(filename).then(data => zipStorageFiles.file(filename,data));
+        });
+      });
+    }
+    return promise;
+  }).then(() => {
+    return Comms.showMessage(Const.MESSAGE_RELOAD);
+  }).then(() => {
+    return zip.generateAsync({type:"binarystring"});
+  }).then(content => {
+    Progress.hide({ sticky: true });
+    showToast('Backup complete!', 'success');
+    Espruino.Core.Utils.fileSaveDialog(content, "Banglejs backup.zip");
+  }).catch(err => {
+    Progress.hide({ sticky: true });
+    showToast('Backup failed, ' + err, 'error');
+  });
+}
+
+function bangleUpload() {
+  Espruino.Core.Utils.fileOpenDialog({
+      id:"backup",
+      type:"arraybuffer",
+      mimeType:".zip,application/zip"}, function(data) {
+    if (data===undefined) return;
+    var promise = Promise.resolve();
+    var zip = new JSZip();
+    var cmds = "";
+    zip.loadAsync(data).then(function(zip) {
+      return showPrompt("Restore from ZIP","Are you sure? This will remove all existing apps");
+    }).then(()=>{
+      Progress.show({title:`Reading ZIP`});
+      zip.forEach(function (path, file){
+        console.log("path");
+        promise = promise
+        .then(() => file.async("string"))
+        .then(data => {
+          console.log("decoded", path);
+          if (data.length==0) { // https://github.com/espruino/BangleApps/issues/1593
+            console.log("Can't restore files of length 0, ignoring "+path);
+          } else if (path.startsWith(BACKUP_STORAGEFILE_DIR)) {
+            path = path.substr(BACKUP_STORAGEFILE_DIR.length+1);
+            cmds += AppInfo.getStorageFileUploadCommands(path, data)+"\n";
+          } else if (!path.includes("/")) {
+            cmds += AppInfo.getFileUploadCommands(path, data)+"\n";
+          } else console.log("Ignoring "+path);
+        });
+      });
+      return promise;
+    })
+    .then(() => {
+      Progress.hide({sticky:true});
+      Progress.show({title:`Erasing...`});
+      return Comms.removeAllApps(); })
+    .then(() => {
+      Progress.hide({sticky:true});
+      Progress.show({title:`Restoring...`, sticky:true});
+      return Comms.showMessage(`Restoring...`); })
+    .then(() => Comms.write("\x10"+Comms.getProgressCmd()+"\n"))
+    .then(() => Comms.uploadCommandList(cmds, 0, cmds.length))
+    .then(() => Comms.showMessage(Const.MESSAGE_RELOAD))
+    .then(() => {
+      Progress.hide({sticky:true});
+      showToast('Restore complete!', 'success');
+    })
+    .catch(err => {
+      Progress.hide({sticky:true});
+      showToast('Restore failed, ' + err, 'error');
+    });
+    return promise;
+  });
+}
+
+window.addEventListener('load', (event) => {
+  document.getElementById("downloadallapps").addEventListener("click",event=>{
+    bangleDownload();
+  });
+  document.getElementById("uploadallapps").addEventListener("click",event=>{
+    bangleUpload();
+  });
+});
diff --git a/bin/language_scan.js b/bin/language_scan.js
index 6385caf49..464d8f998 100755
--- a/bin/language_scan.js
+++ b/bin/language_scan.js
@@ -1,10 +1,74 @@
-#!/usr/bin/nodejs
+#!/usr/bin/env node
 /* Scans for strings that may be in English in each app, and
 outputs a list of strings that have been found.
 
 See https://github.com/espruino/BangleApps/issues/1311
 */
 
+var childProcess = require('child_process');
+
+let refresh = false;
+
+function handleCliParameters ()
+{
+    let usage = "USAGE: language_scan.js [options]";
+    let die = function (message) {
+        console.log(usage);
+        console.log(message);
+        process.exit(3);
+    };
+    let hadTURL = false,
+        hadDEEPL = false;
+    for(let i = 2; i < process.argv.length; i++)
+    {
+        const param = process.argv[i];
+        switch(param)
+        {
+            case '-r':
+            case '--refresh':
+                refresh = true;
+                break;
+            case '--deepl':
+                i++;
+                let KEY = process.argv[i];
+                if(KEY === '' || KEY === null || KEY === undefined)
+                {
+                    die('--deepl requires a parameter: the API key to use');
+                }
+                process.env.DEEPL = KEY;
+                hadDEEPL = true;
+                break;
+            case '--turl':
+                i++;
+                let URL = process.argv[i];
+                if(URL === '' || URL === null || URL === undefined)
+                {
+                    die('--turl requires a parameter: the URL to use');
+                }
+                process.env.TURL = URL;
+                hadTURL = true;
+                break;
+            case '-h':
+            case '--help':
+                console.log(usage+"\n");
+                console.log("Parameters:");
+                console.log("  -h, --help       Output this help text and exit");
+                console.log("  -r, --refresh    Auto-add new strings into lang/*.json");
+                console.log('      --deepl KEY  Enable DEEPL as auto-translation engine and');
+                console.log('                   use KEY as its API key. You also need to provide --turl');
+                console.log('      --turl URL   In combination with --deepl, use URL as the API base URL');
+                process.exit(0);
+            default:
+                die("Unknown parameter: "+param);
+        }
+    }
+    if((hadTURL !== false || hadDEEPL !== false) && hadTURL !== hadDEEPL)
+    {
+        die("Use of deepl requires both a --deepl API key and --turl URL");
+    }
+}
+handleCliParameters();
+
 let translate = false;
 if (process.env.DEEPL) {
   // Requires translate
@@ -64,6 +128,14 @@ try {
 } catch (e) {
   ERROR("apps.json not found");
 }
+if (appsFile.indexOf("---") === 0 && fs.existsSync(BASEDIR+"bin/create_apps_json.sh"))
+{
+    console.log("apps.json has not been generated, running bin/create_apps_json.sh to build it...");
+    childProcess.execFileSync(BASEDIR+'bin/create_apps_json.sh',[],{
+        stdio: 'inherit'
+    });
+  appsFile = fs.readFileSync(BASEDIR+"apps.json").toString();
+}
 try{
   apps = JSON.parse(appsFile);
 } catch (e) {
@@ -234,6 +306,11 @@ for (let language of languages) {
                 translations.GLOBAL[translationItem.str] = translation;
                 resolve()
             }))
+          } else if(refresh && !translate) {
+            translationPromises.push(new Promise(async (resolve) => {
+                translations.GLOBAL[translationItem.str] = translationItem.str;
+                resolve()
+            }))
           }
         }
       });
diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js
index 363e86922..850b793f4 100755
--- a/bin/sanitycheck.js
+++ b/bin/sanitycheck.js
@@ -1,4 +1,4 @@
-#!/usr/bin/nodejs
+#!/usr/bin/node
 /* Checks for any obvious problems in apps.json
 */
 
@@ -65,6 +65,7 @@ const APP_KEYS = [
 const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
 const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
 const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
+const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale"]; // values allowed for "type" field
 const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
 const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
 const GRANDFATHERED_ICONS = ["s7clk",  "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
@@ -94,6 +95,8 @@ apps.forEach((app,appIdx) => {
   if (!app.name) ERROR(`App ${app.id} has no name`);
   var isApp = !app.type || app.type=="app";
   if (app.name.length>20 && !app.shortName && isApp) ERROR(`App ${app.id} has a long name, but no shortName`);
+  if (app.type && !METADATA_TYPES.includes(app.type))
+    ERROR(`App ${app.id} 'type' is one one of `+METADATA_TYPES);
   if (!Array.isArray(app.supports)) ERROR(`App ${app.id} has no 'supports' field or it's not an array`);
   else {
     app.supports.forEach(dev => {
@@ -135,6 +138,9 @@ apps.forEach((app,appIdx) => {
       Object.keys(app.dependencies).forEach(dependency => {
         if (!["type","app"].includes(app.dependencies[dependency]))
           ERROR(`App ${app.id} 'dependencies' must all be tagged 'type' or 'app' right now`);
+        if (app.dependencies[dependency]=="type" && !METADATA_TYPES.includes(dependency))
+          ERROR(`App ${app.id} 'type' dependency must be one of `+METADATA_TYPES);
+          
       });
     } else
       ERROR(`App ${app.id} 'dependencies' must be an object`);
@@ -197,10 +203,11 @@ apps.forEach((app,appIdx) => {
     // warn if JS icon is the wrong size
     if (file.name == app.id+".img") {
         let icon;
-        let match = fileContents.match(/E\.toArrayBuffer\(atob\(\"([^"]*)\"\)\)/);
+        let match = fileContents.match(/^\s*E\.toArrayBuffer\(atob\(\"([^"]*)\"\)\)\s*$/);
+        if (match==null) match = fileContents.match(/^\s*atob\(\"([^"]*)\"\)\s*$/);
         if (match) icon = Buffer.from(match[1], 'base64');
         else {
-          match = fileContents.match(/require\(\"heatshrink\"\)\.decompress\(\s*atob\(\s*\"([^"]*)\"\s*\)\s*\)/);
+          match = fileContents.match(/^\s*require\(\"heatshrink\"\)\.decompress\(\s*atob\(\s*\"([^"]*)\"\s*\)\s*\)\s*$/);
           if (match) icon = heatshrink.decompress(Buffer.from(match[1], 'base64'));
           else ERROR(`JS icon ${file.name} does not match the pattern 'require("heatshrink").decompress(atob("..."))'`);
         }
diff --git a/core b/core
index a7a80a13f..32d01b5b3 160000
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit a7a80a13fa187a4ff5f89669992babca2d95812c
+Subproject commit 32d01b5b3d8e013ca0364671e2352b7b0dd48bb4
diff --git a/css/main.css b/css/main.css
index f4850babe..96a102119 100644
--- a/css/main.css
+++ b/css/main.css
@@ -19,7 +19,7 @@
 }
 
 .tile.column.col-6.col-sm-12.col-xs-12.app-tile {
-  border: solid 1px #fafafa;
+  border: solid 1px #dadee4;
   margin: 0;
   min-height: 150px;
   padding-top: 0.5rem;
diff --git a/gadgetbridge.js b/gadgetbridge.js
new file mode 100644
index 000000000..679fffc60
--- /dev/null
+++ b/gadgetbridge.js
@@ -0,0 +1,162 @@
+/* Detects if we're running under Gadgetbridge in a WebView, and if
+so it overwrites the 'Puck' library with a special one that calls back
+into Gadgetbridge to handle watch communications */
+
+/*// test code
+Android = {
+  bangleTx : function(data) {
+    console.log("TX : "+JSON.stringify(data));
+  }
+};*/
+
+if (typeof Android!=="undefined") {
+  console.log("Running under Gadgetbridge, overwrite Puck library");
+
+  var isBusy = false;
+  var queue = [];
+  var connection = {
+    cb : function(data) {},
+    write : function(data, writecb) {
+      Android.bangleTx(data);  
+      Puck.writeProgress(data.length, data.length);
+      if (writecb) setTimeout(writecb,10);
+    },
+    close : function() {},
+    received : "", 
+    hadData : false
+  }
+
+  function bangleRx(data) { 
+//    document.getElementById("status").innerText = "RX:"+data;
+    connection.received += data;
+    connection.hadData = true;
+    if (connection.cb)  connection.cb(data);
+  } 
+
+  function log(level, s) {
+    if (Puck.log) Puck.log(level, s);
+  }
+
+  function handleQueue() {
+    if (!queue.length) return;
+    var q = queue.shift();
+    log(3,"Executing "+JSON.stringify(q)+" from queue");
+    if (q.type == "write") Puck.write(q.data, q.callback, q.callbackNewline);
+    else log(1,"Unknown queue item "+JSON.stringify(q));
+  }
+
+ /* convenience function... Write data, call the callback with data:
+       callbackNewline = false => if no new data received for ~0.2 sec
+       callbackNewline = true => after a newline */
+  function write(data, callback, callbackNewline) {
+    let result;
+    /// If there wasn't a callback function, then promisify
+    if (typeof callback !== 'function') {
+      callbackNewline = callback;
+
+      result = new Promise((resolve, reject) => callback = (value, err) => {
+        if (err) reject(err);
+        else resolve(value);
+      });
+    }
+
+    if (isBusy) {
+      log(3, "Busy - adding Puck.write to queue");
+      queue.push({type:"write", data:data, callback:callback, callbackNewline:callbackNewline});
+      return result;
+    }
+
+    var cbTimeout;
+    function onWritten() {
+      if (callbackNewline) {
+        connection.cb = function(d) {
+          var newLineIdx = connection.received.indexOf("\n");
+          if (newLineIdx>=0) {
+            var l = connection.received.substr(0,newLineIdx);
+            connection.received = connection.received.substr(newLineIdx+1);
+            connection.cb = undefined;
+            if (cbTimeout) clearTimeout(cbTimeout);
+            cbTimeout = undefined;
+            if (callback)
+              callback(l);
+            isBusy = false;
+            handleQueue();
+          }
+        };
+      }
+      // wait for any received data if we have a callback...
+      var maxTime = 300; // 30 sec - Max time we wait in total, even if getting data
+      var dataWaitTime = callbackNewline ? 100/*10 sec if waiting for newline*/ : 3/*300ms*/;
+      var maxDataTime = dataWaitTime; // max time we wait after having received data
+      cbTimeout = setTimeout(function timeout() {
+        cbTimeout = undefined;
+        if (maxTime) maxTime--;
+        if (maxDataTime) maxDataTime--;
+        if (connection.hadData) maxDataTime=dataWaitTime;
+        if (maxDataTime && maxTime) {
+          cbTimeout = setTimeout(timeout, 100);
+        } else {
+          connection.cb = undefined;
+          if (callback)
+            callback(connection.received);
+          isBusy = false;
+          handleQueue();
+          connection.received = "";
+        }
+        connection.hadData = false;
+      }, 100);
+    }
+
+    if (!connection.txInProgress) connection.received = "";
+    isBusy = true;
+    connection.write(data, onWritten);
+    return result
+  }
+
+  // ----------------------------------------------------------
+
+  Puck = {
+    /// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all.
+    debug : Puck.debug,
+    /// Should we use flow control? Default is true
+    flowControl : true,
+    /// Used internally to write log information - you can replace this with your own function
+    log : function(level, s) { if (level <= this.debug) console.log(" "+s)},
+    /// Called with the current send progress or undefined when done - you can replace this with your own function
+    writeProgress : Puck.writeProgress,
+    connect : function(callback) {
+      setTimeout(callback, 10);
+    },
+    write : write,
+    eval : function(expr, cb) {
+      const response = write('\x10Bluetooth.println(JSON.stringify(' + expr + '))\n', true)
+        .then(function (d) {
+          try {
+            return JSON.parse(d);
+          } catch (e) {
+            log(1, "Unable to decode " + JSON.stringify(d) + ", got " + e.toString());
+            return Promise.reject(d);
+          }
+        });
+      if (cb) {
+        return void response.then(cb, (err) => cb(null, err));
+      } else {
+        return response;
+      }
+    },
+    isConnected : function() { return true;  },
+    getConnection : function() {  return connection; },
+    close : function() {
+      if (connection)
+        connection.close();
+    },
+  };
+  // no need for header 
+  document.getElementsByTagName("header")[0].style="display:none";
+  // force connection attempt automatically
+  setTimeout(function() {
+   getInstalledApps(true).catch(err => {
+      showToast("Device connection failed, "+err,"error");
+    });
+  }, 100);
+}
diff --git a/index.html b/index.html
index 6c9a21bf8..b141cffc9 100644
--- a/index.html
+++ b/index.html
@@ -75,11 +75,14 @@
         
+ + +