├── app ├── node_modules │ ├── objectid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── index.js │ │ ├── README.md │ │ ├── package.json │ │ └── test.js │ └── nconf │ │ ├── test │ │ ├── fixtures │ │ │ ├── malformed.json │ │ │ ├── hierarchy │ │ │ │ ├── hierarchical.json │ │ │ │ ├── user.json │ │ │ │ └── global.json │ │ │ ├── merge │ │ │ │ ├── file2.json │ │ │ │ └── file1.json │ │ │ ├── scripts │ │ │ │ ├── nconf-env.js │ │ │ │ ├── nconf-argv.js │ │ │ │ ├── nconf-nested-env.js │ │ │ │ ├── provider-argv.js │ │ │ │ ├── provider-env.js │ │ │ │ ├── nconf-change-argv.js │ │ │ │ ├── nconf-hierarchical-file-argv.js │ │ │ │ ├── nconf-hierarchical-load-merge.js │ │ │ │ └── nconf-hierarchical-load-save.js │ │ │ ├── bom.json │ │ │ ├── complete.json │ │ │ ├── no-bom.json │ │ │ ├── data.js │ │ │ └── secure.json │ │ ├── stores │ │ │ ├── argv-test.js │ │ │ ├── env-test.js │ │ │ ├── literal-test.js │ │ │ ├── memory-store-test.js │ │ │ └── file-store-test.js │ │ ├── common-test.js │ │ ├── mocks │ │ │ └── mock-store.js │ │ ├── provider-save-test.js │ │ ├── helpers.js │ │ ├── hierarchy-test.js │ │ ├── nconf-test.js │ │ ├── complete-test.js │ │ └── provider-test.js │ │ ├── .npmignore │ │ ├── .travis.yml │ │ ├── lib │ │ ├── nconf │ │ │ ├── formats.js │ │ │ ├── stores │ │ │ │ ├── literal.js │ │ │ │ ├── argv.js │ │ │ │ ├── env.js │ │ │ │ ├── memory.js │ │ │ │ └── file.js │ │ │ ├── common.js │ │ │ └── provider.js │ │ └── nconf.js │ │ ├── LICENSE │ │ ├── usage.js │ │ ├── package.json │ │ └── README.md ├── icons │ ├── icon.ico │ ├── icon.png │ ├── icon.icns │ ├── icon@2x.png │ └── installer.png ├── sign │ ├── dist │ │ ├── sign-icons.png │ │ ├── sign-icons@2.png │ │ └── sign.min.css │ ├── src │ │ ├── images │ │ │ ├── dida │ │ │ │ ├── sign-icons │ │ │ │ │ ├── qq.png │ │ │ │ │ ├── weibo.png │ │ │ │ │ └── wechat.png │ │ │ │ └── sign-icons@2 │ │ │ │ │ ├── qq.png │ │ │ │ │ ├── wechat.png │ │ │ │ │ └── weibo.png │ │ │ ├── common │ │ │ │ ├── sign-icons │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── email-blue.png │ │ │ │ │ ├── email-gray.png │ │ │ │ │ ├── lock-blue.png │ │ │ │ │ ├── lock-gray.png │ │ │ │ │ ├── name-blue.png │ │ │ │ │ └── name-gray.png │ │ │ │ └── sign-icons@2 │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── lock-blue.png │ │ │ │ │ ├── lock-gray.png │ │ │ │ │ ├── name-blue.png │ │ │ │ │ ├── name-gray.png │ │ │ │ │ ├── email-blue.png │ │ │ │ │ └── email-gray.png │ │ │ └── tick │ │ │ │ ├── sign-icons │ │ │ │ ├── facebook.png │ │ │ │ ├── google.png │ │ │ │ └── twitter.png │ │ │ │ └── sign-icons@2 │ │ │ │ ├── google.png │ │ │ │ ├── facebook.png │ │ │ │ └── twitter.png │ │ ├── styles │ │ │ ├── main.scss │ │ │ ├── _btns.scss │ │ │ ├── _reset.scss │ │ │ ├── _color.scss │ │ │ ├── _functions.scss │ │ │ ├── _mixins.scss │ │ │ ├── sign-icons.scss │ │ │ ├── sign-icons@2.scss │ │ │ ├── _global.scss │ │ │ ├── _sign.scss │ │ │ ├── _variables.scss │ │ │ └── _button.scss │ │ └── scripts │ │ │ └── sign.js │ ├── package.json │ ├── npm-debug.log │ ├── icon.mustache │ └── gulpfile.js ├── appest.js ├── appest-dida.js ├── main.css ├── appest-tick.js ├── package.json ├── index.html ├── user-config.js ├── index.js ├── main.js ├── menu.js └── signin.html ├── screenshot.png ├── .gitignore ├── .eslintrc.json ├── README.md ├── package.json └── gulpfile.js /app/node_modules/objectid/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/screenshot.png -------------------------------------------------------------------------------- /app/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/icons/icon.ico -------------------------------------------------------------------------------- /app/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/icons/icon.png -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/malformed.json: -------------------------------------------------------------------------------- 1 | { 2 | "literal": "bazz", 3 | } -------------------------------------------------------------------------------- /app/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/icons/icon.icns -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | !/app/node_modules 4 | npm-debug.log 5 | pkg 6 | -------------------------------------------------------------------------------- /app/icons/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/icons/icon@2x.png -------------------------------------------------------------------------------- /app/icons/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/icons/installer.png -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/hierarchy/hierarchical.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "empty" 3 | } -------------------------------------------------------------------------------- /app/sign/dist/sign-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/dist/sign-icons.png -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "plugins": [ 4 | "standard" 5 | ] 6 | } -------------------------------------------------------------------------------- /app/sign/dist/sign-icons@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/dist/sign-icons@2.png -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/hierarchy/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "My specific title", 3 | "color": "green" 4 | } -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons/qq.png -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons/weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons/weibo.png -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons@2/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons@2/qq.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/logo.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/logo.png -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons/wechat.png -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons@2/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons@2/wechat.png -------------------------------------------------------------------------------- /app/sign/src/images/dida/sign-icons@2/weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/dida/sign-icons@2/weibo.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons/facebook.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons/google.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons/twitter.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons@2/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons@2/google.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons@2/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons@2/facebook.png -------------------------------------------------------------------------------- /app/sign/src/images/tick/sign-icons@2/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/tick/sign-icons@2/twitter.png -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/hierarchy/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "My generic title", 3 | "color": "red", 4 | "movie": "Kill Bill" 5 | } -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/email-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/email-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/email-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/email-gray.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/lock-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/lock-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/lock-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/lock-gray.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/name-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/name-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons/name-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons/name-gray.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/lock-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/lock-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/lock-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/lock-gray.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/name-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/name-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/name-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/name-gray.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/email-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/email-blue.png -------------------------------------------------------------------------------- /app/sign/src/images/common/sign-icons@2/email-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwartz/dida/HEAD/app/sign/src/images/common/sign-icons@2/email-gray.png -------------------------------------------------------------------------------- /app/appest.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | user: { 3 | inboxId: '', 4 | username: '' 5 | }, 6 | domain: 'dida365.com', 7 | api_domain: 'dida365.com', 8 | protocol: 'https://', 9 | productName: '滴答清单' 10 | } 11 | -------------------------------------------------------------------------------- /app/appest-dida.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | user: { 3 | inboxId: '', 4 | username: '' 5 | }, 6 | domain: 'dida365.com', 7 | api_domain: 'dida365.com', 8 | protocol: 'https://', 9 | productName: '滴答清单' 10 | } 11 | -------------------------------------------------------------------------------- /app/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | overflow: hidden; 3 | } 4 | 5 | input { 6 | width: 600px; 7 | height: 40px; 8 | padding: 10px; 9 | font-size: 16px; 10 | box-sizing: border-box; 11 | } 12 | 13 | input:focus { 14 | outline: none; 15 | } 16 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/merge/file2.json: -------------------------------------------------------------------------------- 1 | { 2 | "candy": { 3 | "something": "file2", 4 | "something3": true, 5 | "something4": true 6 | }, 7 | "dates": true, 8 | "elderberries": true, 9 | "unicorn": null 10 | } 11 | -------------------------------------------------------------------------------- /app/appest-tick.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | user: { 3 | inboxId: '', 4 | username: '' 5 | }, 6 | domain: 'ticktick.com', 7 | api_domain: 'ticktick.com', 8 | protocol: 'https://', 9 | productName: 'TickTick' 10 | } 11 | -------------------------------------------------------------------------------- /app/sign/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // variables 4 | @import 5 | 'variables'; 6 | 7 | @import 8 | 'global'; 9 | 10 | @import 11 | 'button', 12 | 'btns'; 13 | 14 | @import 15 | 'sign-icons', 16 | 'reset', 17 | 'sign'; 18 | -------------------------------------------------------------------------------- /app/sign/src/styles/_btns.scss: -------------------------------------------------------------------------------- 1 | // buttons 2 | .btns { 3 | 4 | .btn-loading { 5 | display: none; 6 | } 7 | 8 | &.ing { 9 | .btn-initial { 10 | display: none; 11 | } 12 | 13 | .btn-loading { 14 | display: inline-block; 15 | } 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /app/node_modules/nconf/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | config.json 3 | test/fixtures/*.json 4 | !test/fixtures/complete.json 5 | !test/fixtures/malformed.json 6 | !test/fixtures/bom.json 7 | !test/fixtures/no-bom.json 8 | !test/fixtures/secure.json 9 | node_modules/ 10 | node_modules/* 11 | npm-debug.log 12 | coverage -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-env.js: Test fixture for using process.env defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').env(); 9 | 10 | process.stdout.write(nconf.get('SOMETHING')); -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * default-argv.js: Test fixture for using yargs defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').argv().env(); 9 | 10 | process.stdout.write(nconf.get('something')); 11 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-nested-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-nested-env.js: Test fixture for env with nested keys. 3 | * 4 | * (C) 2012, Charlie Robbins and the Contributors. 5 | * (C) 2012, Michael Hart 6 | * 7 | */ 8 | 9 | var nconf = require('../../../lib/nconf').env('_'); 10 | 11 | process.stdout.write(nconf.get('SOME:THING')); 12 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/provider-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-argv.js: Test fixture for using yargs defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf'); 9 | 10 | var provider = new (nconf.Provider)().argv(); 11 | 12 | process.stdout.write(provider.get('something')); 13 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/provider-env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-argv.js: Test fixture for using process.env defaults with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf'); 9 | 10 | var provider = new (nconf.Provider)().env(); 11 | 12 | process.stdout.write(provider.get('SOMETHING')); -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "Desktop App of 滴答清单(TickTick)", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "xwartz", 10 | "license": "MIT", 11 | "dependencies": { 12 | "nconf": "^0.8.4", 13 | "objectid": "^3.2.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/merge/file1.json: -------------------------------------------------------------------------------- 1 | { 2 | "apples": true, 3 | "bananas": true, 4 | "foo": { 5 | "bar": "boo" 6 | }, 7 | "candy": { 8 | "something": "file1", 9 | "something1": true, 10 | "something2": true, 11 | "something5": { 12 | "first": 1, 13 | "second": 2 14 | } 15 | }, 16 | "unicorn": { 17 | "exists": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/complete.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/no-bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "I've seen things": { 3 | "like": [ 4 | "carrots", 5 | "handbags", 6 | "cheese", 7 | "toilets", 8 | "russians", 9 | "planets", 10 | "hampsters", 11 | "weddings", 12 | "poets", 13 | "stalin", 14 | "kuala lumpur" 15 | ] 16 | }, 17 | "host": "weebls-stuff.com", 18 | "port": 78304 19 | } 20 | -------------------------------------------------------------------------------- /app/sign/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ticktick-sign", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "gulp": "^3.9.1", 6 | "gulp-compass": "^2.1.0", 7 | "gulp-minify-css": "^1.2.4", 8 | "gulp-notify": "^2.2.0", 9 | "gulp-plumber": "^1.1.0", 10 | "gulp-rename": "^1.2.2", 11 | "gulp-shell": "^0.5.2", 12 | "gulp-uncss": "^1.0.4", 13 | "gulp.spritesmith": "~1.1.1", 14 | "shelljs": "^0.6.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-change-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-change-argv.js: Test fixture for changing argv on the fly 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var nconf = require('../../../lib/nconf').argv(); 9 | 10 | // 11 | // Remove 'badValue', 'evenWorse' and 'OHNOEZ' 12 | // 13 | process.argv.splice(3, 3); 14 | nconf.stores['argv'].loadArgv(); 15 | process.stdout.write(nconf.get('something')); 16 | 17 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-file-argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-file-argv.js: Test fixture for using yargs defaults and a file store with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * (C) 2011, Sander Tolsma 6 | * 7 | */ 8 | 9 | var path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | nconf.argv(); 13 | nconf.add('file', { 14 | file: path.join(__dirname, '../hierarchy/hierarchical.json') 15 | }); 16 | 17 | process.stdout.write(nconf.get('something') || 'undefined'); 18 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-merge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-load-merge.js: Test fixture for loading and merging nested objects across stores. 3 | * 4 | * (C) 2012, Charlie Robbins and the Contributors. 5 | * (C) 2012, Michael Hart 6 | * 7 | */ 8 | 9 | var path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | nconf.argv() 13 | .file(path.join(__dirname, '..', 'merge', 'file1.json')); 14 | 15 | process.stdout.write(JSON.stringify({ 16 | apples: nconf.get('apples'), 17 | candy: nconf.get('candy') 18 | })); 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 滴答清单(TickTick) 2 | 3 | > Task Manager: [滴答清单](https://dida365.com/) && [TickTick](https://ticktick.com/) 4 | 5 | dida: Easily add task to 滴答清单(TickTick) by shortcut. 6 | 7 | 8 | 9 | ## Dev 10 | 11 | ``` 12 | $ npm i && cd app && npm i 13 | ``` 14 | 15 | ### Run 16 | 17 | ``` 18 | $ npm start 19 | ``` 20 | 21 | ## Build 22 | 23 | ``` 24 | $ npm run build:dida 25 | ``` 26 | or 27 | ``` 28 | $ npm run build:tick 29 | ``` 30 | 31 | ## Note 32 | 33 | ### Mirrors 34 | 35 | [China](https://npm.taobao.org/) 36 | 37 | ## License 38 | 39 | ©MIT 40 | -------------------------------------------------------------------------------- /app/node_modules/nconf/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | - "0.12" 6 | - "4.1" 7 | - "5" 8 | 9 | before_install: 10 | - travis_retry npm install npm -g 11 | 12 | before_install: 13 | - travis_retry npm install -g npm@2.5.1 14 | - travis_retry npm install 15 | 16 | script: 17 | - npm test 18 | 19 | after_script: 20 | - npm run cover 21 | - npm run coveralls 22 | 23 | matrix: 24 | allow_failures: 25 | - node_js: "0.10" 26 | 27 | notifications: 28 | email: 29 | - travis@nodejitsu.com 30 | irc: "irc.freenode.org#nodejitsu" 31 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * data.js: Simple data fixture for configuration test. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | exports.data = { 9 | isNull: null, 10 | literal: 'bazz', 11 | arr: ['one', 2, true, { value: 'foo' }], 12 | obj: { 13 | host: 'localhost', 14 | port: 5984, 15 | array: ['one', 2, true, { foo: 'bar' }], 16 | auth: { 17 | username: 'admin', 18 | password: 'password' 19 | } 20 | } 21 | }; 22 | 23 | exports.merge = { 24 | prop1: 1, 25 | prop2: [1, 2, 3], 26 | prop3: { 27 | foo: 'bar', 28 | bar: 'foo' 29 | } 30 | }; -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/formats.js: -------------------------------------------------------------------------------- 1 | /* 2 | * formats.js: Default formats supported by nconf 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var ini = require('ini'); 9 | 10 | var formats = exports; 11 | 12 | // 13 | // ### @json 14 | // Standard JSON format which pretty prints `.stringify()`. 15 | // 16 | formats.json = { 17 | stringify: function (obj, replacer, spacing) { 18 | return JSON.stringify(obj, replacer || null, spacing || 2) 19 | }, 20 | parse: JSON.parse 21 | }; 22 | 23 | // 24 | // ### @ini 25 | // Standard INI format supplied from the `ini` module 26 | // http://en.wikipedia.org/wiki/INI_file 27 | // 28 | formats.ini = ini; 29 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/stores/argv-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * argv-test.js: Tests for the nconf argv store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var vows = require('vows'), 9 | assert = require('assert'), 10 | helpers = require('../helpers'), 11 | nconf = require('../../lib/nconf'); 12 | 13 | vows.describe('nconf/stores/argv').addBatch({ 14 | "An instance of nconf.Argv": { 15 | topic: new nconf.Argv(), 16 | "should have the correct methods defined": function (argv) { 17 | assert.isFunction(argv.loadSync); 18 | assert.isFunction(argv.loadArgv); 19 | assert.isFalse(argv.options); 20 | } 21 | } 22 | }).export(module); -------------------------------------------------------------------------------- /app/node_modules/nconf/test/stores/env-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * env-test.js: Tests for the nconf env store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var vows = require('vows'), 9 | assert = require('assert'), 10 | helpers = require('../helpers'), 11 | nconf = require('../../lib/nconf'); 12 | 13 | vows.describe('nconf/stores/env').addBatch({ 14 | "An instance of nconf.Env": { 15 | topic: new nconf.Env(), 16 | "should have the correct methods defined": function (env) { 17 | assert.isFunction(env.loadSync); 18 | assert.isFunction(env.loadEnv); 19 | assert.isArray(env.whitelist); 20 | assert.lengthOf(env.whitelist, 0); 21 | assert.equal(env.separator, ''); 22 | } 23 | } 24 | }).export(module); 25 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/stores/literal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * literal.js: Simple literal Object store for nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | Memory = require('./memory').Memory 10 | 11 | var Literal = exports.Literal = function Literal (options) { 12 | Memory.call(this, options); 13 | 14 | options = options || {} 15 | this.type = 'literal'; 16 | this.readOnly = true; 17 | this.store = options.store || options; 18 | }; 19 | 20 | // Inherit from Memory store. 21 | util.inherits(Literal, Memory); 22 | 23 | // 24 | // ### function loadSync (callback) 25 | // Returns the data stored in `this.store` synchronously. 26 | // 27 | Literal.prototype.loadSync = function () { 28 | return this.store; 29 | }; -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/secure.json: -------------------------------------------------------------------------------- 1 | { 2 | "isNull": { 3 | "alg": "aes-256-ctr", 4 | "value": "af07fbcf" 5 | }, 6 | "literal": { 7 | "alg": "aes-256-ctr", 8 | "value": "e310f6d94f13" 9 | }, 10 | "arr": { 11 | "alg": "aes-256-ctr", 12 | "value": "9a78b783175e69bb8f3458042b1c098d8ed9613410fac185b3735099224f8fe4ece0f0da8decfddbbf0eab3b7c391c47772b5441" 13 | }, 14 | "obj": { 15 | "alg": "aes-256-ctr", 16 | "value": "ba78b783175968add93a680429424ae4cf957d2916ebcfa399730bb17200ddb0ecacb183c1b1ebcd950ced76726964062e74643c995c47372bfb1311bee8f65bbeb5a1d9426537a6d83635220ec7934e1d7cc187f7218cd4afadfa2f107fb42c232d80d95c160ee704fa8e922998b0b3e47ec579dd0baef7cae6d7dbaa203d732adb5cff22b80d810d7191237999cd8dc528d8f2201ae128a9f9e2df96d1a816aa73e3e6b8e6246cd98b454e453b36f43f9117cb4af8fa85429a92" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-save.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf-hierarchical-load-save.js: Test fixture for using yargs, envvars and a file store with nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | path = require('path'), 10 | nconf = require('../../../lib/nconf'); 11 | 12 | // 13 | // Setup nconf to use (in-order): 14 | // 1. Command-line arguments 15 | // 2. Environment variables 16 | // 3. A file located at 'path/to/config.json' 17 | // 18 | nconf.argv() 19 | .env() 20 | .file({ file: path.join(__dirname, '..', 'load-save.json') }); 21 | 22 | // 23 | // Set a few variables on `nconf`. 24 | // 25 | nconf.set('database:host', '127.0.0.1'); 26 | nconf.set('database:port', 5984); 27 | 28 | process.stdout.write(nconf.get('foo')); 29 | // 30 | // Save the configuration object to disk 31 | // 32 | nconf.save(); 33 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/stores/literal-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * literal-test.js: Tests for the nconf literal store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var vows = require('vows'), 9 | assert = require('assert'), 10 | helpers = require('../helpers'), 11 | nconf = require('../../lib/nconf'); 12 | 13 | vows.describe('nconf/stores/literal').addBatch({ 14 | "An instance of nconf.Literal": { 15 | topic: new nconf.Literal({ 16 | foo: 'bar', 17 | one: 2 18 | }), 19 | "should have the correct methods defined": function (literal) { 20 | assert.equal(literal.type, 'literal'); 21 | assert.isFunction(literal.get); 22 | assert.isFunction(literal.set); 23 | assert.isFunction(literal.merge); 24 | assert.isFunction(literal.loadSync); 25 | }, 26 | "should have the correct values in the store": function (literal) { 27 | assert.equal(literal.store.foo, 'bar'); 28 | assert.equal(literal.store.one, 2); 29 | } 30 | } 31 | }).export(module); -------------------------------------------------------------------------------- /app/node_modules/nconf/test/common-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * common.js: Tests for common utility function in nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | path = require('path'), 10 | vows = require('vows'), 11 | assert = require('assert'), 12 | helpers = require('./helpers'), 13 | nconf = require('../lib/nconf'); 14 | 15 | var mergeDir = path.join(__dirname, 'fixtures', 'merge'), 16 | files = fs.readdirSync(mergeDir).map(function (f) { return path.join(mergeDir, f) }); 17 | 18 | vows.describe('nconf/common').addBatch({ 19 | "Using nconf.common module": { 20 | "the loadFiles() method": { 21 | topic: function () { 22 | nconf.loadFiles(files, this.callback); 23 | }, 24 | "should merge the files correctly": helpers.assertMerged 25 | }, 26 | "the loadFilesSync() method": { 27 | "should merge the files correctly": function () { 28 | helpers.assertMerged(null, nconf.loadFilesSync(files)); 29 | } 30 | } 31 | } 32 | }).export(module); -------------------------------------------------------------------------------- /app/node_modules/objectid/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Agile Diagnosis, Inc. 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /app/node_modules/nconf/test/mocks/mock-store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * mock-store.js: Mock store for ensuring certain operations are actually called. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | events = require('events'), 10 | nconf = require('../../lib/nconf'); 11 | 12 | var Mock = nconf.Mock = function () { 13 | events.EventEmitter.call(this); 14 | this.type = 'mock'; 15 | }; 16 | 17 | // Inherit from Memory store. 18 | util.inherits(Mock, events.EventEmitter); 19 | 20 | // 21 | // ### function save (value, callback) 22 | // #### @value {Object} _Ignored_ Left here for consistency 23 | // #### @callback {function} Continuation to respond to when complete. 24 | // Waits `1000ms` and then calls the callback and emits the `save` event. 25 | // 26 | Mock.prototype.save = function (value, callback) { 27 | if (!callback && typeof value === 'function') { 28 | callback = value; 29 | value = null; 30 | } 31 | 32 | var self = this; 33 | 34 | setTimeout(function () { 35 | self.emit('save'); 36 | callback(); 37 | }, 1000); 38 | }; -------------------------------------------------------------------------------- /app/node_modules/nconf/test/provider-save-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-save-test.js: Ensures consistency for Provider `save` operations. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var assert = require('assert'), 9 | vows = require('vows'), 10 | nconf = require('../lib/nconf'); 11 | 12 | // 13 | // Expose `nconf.Mock` 14 | // 15 | require('./mocks/mock-store'); 16 | 17 | vows.describe('nconf/provider/save').addBatch({ 18 | "When using nconf": { 19 | "an instance of 'nconf.Provider'": { 20 | "with a Mock store": { 21 | topic: function () { 22 | return nconf.use('mock'); 23 | }, 24 | "the save() method": { 25 | topic: function () { 26 | var mock = nconf.stores.mock, 27 | that = this; 28 | 29 | mock.on('save', function () { that.saved = true }); 30 | nconf.save(this.callback); 31 | }, 32 | "should actually save before responding": function () { 33 | assert.isTrue(this.saved); 34 | } 35 | } 36 | } 37 | } 38 | } 39 | }).export(module); -------------------------------------------------------------------------------- /app/node_modules/nconf/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Charlie Robbins and the Contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dida", 3 | "version": "1.0.0", 4 | "description": "Desktop App of 滴答清单(TickTick)", 5 | "main": "app/main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "electron app/main.js", 9 | "build:dida": "gulp pkg:dida", 10 | "build:tick": "gulp pkg:tick" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/xwartz/dida.git" 15 | }, 16 | "keywords": [ 17 | "dida", 18 | "ticktick", 19 | "electron", 20 | "js", 21 | "es6", 22 | "desktop app" 23 | ], 24 | "author": "xwartz (https://github.com/xwartz/)", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/xwartz/dida/issues" 28 | }, 29 | "homepage": "https://github.com/xwartz/dida#readme", 30 | "devDependencies": { 31 | "del": "^2.2.0", 32 | "electron-prebuilt": "0.36.0", 33 | "electron-builder": "^2.8.3", 34 | "electron-packager": "^5.2.1", 35 | "eslint-config-standard": "^4.4.0", 36 | "eslint-plugin-standard": "^1.3.1", 37 | "gulp": "^3.9.1", 38 | "gulp-rename": "^1.2.2", 39 | "gulp-shell": "^0.5.2", 40 | "yargs": "^4.2.0" 41 | }, 42 | "dependencies": {} 43 | } 44 | -------------------------------------------------------------------------------- /app/user-config.js: -------------------------------------------------------------------------------- 1 | // 存储用户信息 2 | 3 | 'use strict' 4 | 5 | var nconf = require('nconf') 6 | var electron = require('electron') 7 | var app = electron.app 8 | 9 | var appest = require('./appest') 10 | 11 | // 不是主进程的话, 渲染进程只能用 remote 12 | if(!app) { 13 | var remote = electron.remote 14 | app = remote.app 15 | } 16 | 17 | nconf.file({ 18 | file: getUserHome() + '/' + appest.domain + '.json' 19 | }) 20 | 21 | function getUserHome() { 22 | return app.getPath('appData') + '/ticktick' 23 | } 24 | 25 | function _save (key, value) { 26 | nconf.set(key, value) 27 | nconf.save() 28 | } 29 | 30 | function readConfig(key) { 31 | nconf.load() 32 | return nconf.get(key) 33 | } 34 | 35 | function saveConfig(data) { 36 | var Appest = {} 37 | if(data.user) { 38 | Appest = Object.assign({}, data) 39 | } else { 40 | Appest = { 41 | user: { 42 | isGFS: data.pro, 43 | inboxId: data.inboxId, 44 | username: data.username 45 | }, 46 | end_date: data.proEndDate, 47 | } 48 | } 49 | _save('Appest', Appest) 50 | } 51 | 52 | function clearConfig () { 53 | saveConfig({ inboxId: '' }) 54 | } 55 | 56 | module.exports = { 57 | saveConfig: saveConfig, 58 | readConfig: readConfig, 59 | clearConfig: clearConfig 60 | } 61 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nconf.js: Top-level include for the nconf module 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | async = require('async'), 10 | common = require('./nconf/common'), 11 | Provider = require('./nconf/provider').Provider; 12 | 13 | // 14 | // `nconf` is by default an instance of `nconf.Provider`. 15 | // 16 | var nconf = module.exports = new Provider(); 17 | 18 | // 19 | // Expose the version from the package.json 20 | // 21 | nconf.version = require('../package.json').version; 22 | 23 | // 24 | // Setup all stores as lazy-loaded getters. 25 | // 26 | fs.readdirSync(__dirname + '/nconf/stores').forEach(function (file) { 27 | var store = file.replace('.js', ''), 28 | name = common.capitalize(store); 29 | 30 | nconf.__defineGetter__(name, function () { 31 | return require('./nconf/stores/' + store)[name]; 32 | }); 33 | }); 34 | 35 | // 36 | // Expose the various components included with nconf 37 | // 38 | nconf.key = common.key; 39 | nconf.path = common.path; 40 | nconf.loadFiles = common.loadFiles; 41 | nconf.loadFilesSync = common.loadFilesSync; 42 | nconf.formats = require('./nconf/formats'); 43 | nconf.Provider = Provider; 44 | 45 | -------------------------------------------------------------------------------- /app/sign/src/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | $s-base-divider: 36px; 2 | 3 | $s-input-placeholder-color: $base-font-color-holder; 4 | 5 | @mixin input-placeholder($color) { 6 | // style placeholder text cross browser 7 | ::-webkit-input-placeholder { 8 | color: $color; 9 | } 10 | 11 | :-moz-placeholder { /* Firefox 18- */ 12 | color: $color; 13 | } 14 | 15 | ::-moz-placeholder { /* Firefox 19+ */ 16 | color: $color; 17 | } 18 | 19 | :-ms-input-placeholder { 20 | color: $color !important; 21 | } 22 | } 23 | 24 | @include input-placeholder($s-input-placeholder-color); 25 | 26 | /*reset style*/ 27 | html, body, div, span, i, p, a, ul, li { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | outline: 0; 32 | font-size: 100%; 33 | font: inherit; 34 | } 35 | 36 | ol, ul { 37 | list-style: none; 38 | } 39 | 40 | a { 41 | color: $base-font-color-link; 42 | text-decoration: none; 43 | 44 | &:hover { 45 | color: $base-font-color-link; 46 | text-decoration: underline; 47 | } 48 | 49 | &:hover, &:active { 50 | outline: 0; 51 | } 52 | 53 | &:visited { 54 | color: $base-font-color-link; 55 | text-decoration: none; 56 | } 57 | 58 | } 59 | 60 | input, 61 | input:focus { 62 | outline: 0; 63 | box-shadow: none; 64 | } 65 | 66 | img { 67 | border-radius: 50%; 68 | } 69 | 70 | .button { 71 | cursor: pointer; 72 | -webkit-appearance: none; 73 | } -------------------------------------------------------------------------------- /app/node_modules/objectid/index.js: -------------------------------------------------------------------------------- 1 | var ObjectId = require('bson').ObjectId 2 | 3 | ObjectId.prototype.equals = function (oidB) { 4 | return equals(this, oidB) 5 | } 6 | 7 | var objIdPattern = /^[0-9a-fA-F]{24}$/; 8 | var isValid = function (alleged) { 9 | return (Boolean(alleged) && !Array.isArray(alleged) && objIdPattern.test(String(alleged))) 10 | } 11 | 12 | var equals = function (oidA, oidB) { 13 | // curried 14 | if (arguments.length === 1) { 15 | return function (oidB) { 16 | return equals(oidA, oidB) 17 | } 18 | } 19 | 20 | if (oidA === oidB) { return true; } 21 | if (!isValid(oidA) || !isValid(oidB)) { return false } 22 | return (String(oidA) === String(oidB)) 23 | return false; 24 | } 25 | 26 | var tryParse = function (oid, out, as) { 27 | if (!isValid(oid)) { return false } 28 | try { 29 | out[as] = Id(oid) 30 | return true 31 | } catch (e) { 32 | return false 33 | } 34 | } 35 | 36 | function Id(id) { 37 | if (id instanceof ObjectId) { return id } 38 | 39 | if (arguments.length === 0) { 40 | return new ObjectId() 41 | } 42 | 43 | id = String(id) 44 | 45 | if (isValid(id)) { 46 | return new ObjectId(id) 47 | 48 | } else { 49 | throw new Error('Invalid ObjectId: ' + id) 50 | } 51 | 52 | } 53 | 54 | module.exports = Id; 55 | module.exports.constructor = ObjectId; 56 | module.exports.tryParse = tryParse; 57 | module.exports.equals = equals; 58 | module.exports.isValid = isValid; 59 | -------------------------------------------------------------------------------- /app/node_modules/nconf/usage.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | nconf = require('./lib/nconf'); 4 | 5 | // 6 | // Configure the provider with a single store and 7 | // support for command-line arguments and environment 8 | // variables. 9 | // 10 | var single = new nconf.Provider({ 11 | env: true, 12 | argv: true, 13 | store: { 14 | type: 'file', 15 | file: path.join(__dirname, 'config.json') 16 | } 17 | }); 18 | 19 | // 20 | // Configure the provider with multiple hierarchical stores 21 | // representing `user` and `global` configuration values. 22 | // 23 | var multiple = new nconf.Provider({ 24 | stores: [ 25 | { name: 'user', type: 'file', file: path.join(__dirname, 'user-config.json') }, 26 | { name: 'global', type: 'global', file: path.join(__dirname, 'global-config.json') } 27 | ] 28 | }); 29 | 30 | // 31 | // Setup nconf to use the 'file' store and set a couple of values; 32 | // 33 | nconf.use('file', { file: path.join(__dirname, 'config.json') }); 34 | nconf.set('database:host', '127.0.0.1'); 35 | nconf.set('database:port', 5984); 36 | 37 | // 38 | // Get the entire database object from nconf 39 | // 40 | var database = nconf.get('database'); 41 | console.dir(database); 42 | 43 | // 44 | // Save the configuration object to disk 45 | // 46 | nconf.save(function (err) { 47 | fs.readFile(path.join(__dirname, 'config.json'), function (err, data) { 48 | console.dir(JSON.parse(data.toString())) 49 | }); 50 | }); -------------------------------------------------------------------------------- /app/sign/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ] 3 | 2 info using npm@3.5.2 4 | 3 info using node@v5.5.0 5 | 4 verbose stack Error: missing script: start 6 | 4 verbose stack at run (/usr/local/lib/node_modules/npm/lib/run-script.js:147:19) 7 | 4 verbose stack at /usr/local/lib/node_modules/npm/lib/run-script.js:57:5 8 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:345:5 9 | 4 verbose stack at checkBinReferences_ (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:309:45) 10 | 4 verbose stack at final (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:343:3) 11 | 4 verbose stack at then (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:113:5) 12 | 4 verbose stack at ReadFileContext. (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:284:20) 13 | 4 verbose stack at ReadFileContext.callback (/usr/local/lib/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:76:16) 14 | 4 verbose stack at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:324:13) 15 | 5 verbose cwd /Users/xwartz/github/pupa/sign 16 | 6 error Darwin 15.3.0 17 | 7 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "start" 18 | 8 error node v5.5.0 19 | 9 error npm v3.5.2 20 | 10 error missing script: start 21 | 11 error If you need help, you may report this error at: 22 | 11 error 23 | 12 verbose exit [ 1, true ] 24 | -------------------------------------------------------------------------------- /app/node_modules/objectid/README.md: -------------------------------------------------------------------------------- 1 | # objectid 2 | mongodb objectid utilities 3 | 4 | ## about 5 | 6 | 1.x is probably the version you want to use in a browser. 3.x focuses on compatibility with the npm `mongodb` driver. 7 | 8 | ## installation 9 | 10 | $ npm install objectid 11 | 12 | ## usage 13 | 14 | var objectid = require('objectid') 15 | 16 | var id = objectid() 17 | 18 | objectid.isValid(id) 19 | // => true 20 | 21 | objectid.isValid('4frsdef43wzx') 22 | // => false 23 | 24 | `objectid.isValid` returns true for `mongodb` native driver `ObjectID` objects, or any other representations with a `.toString` method which returns the hex string encoding of a valid objectid. 25 | 26 | Calling `objectid` with an existing objectid - whether a string, an object created by this module, an objectid created by another driver (such as the result of a query) - will cast the value to an instanceof this module. It will throw if the argument is not a valid ObjectId. 27 | 28 | ## Static Methods 29 | 30 | ### ObjectId.equals(oidA, oidB) => Boolean 31 | Curried to support creating equality predicates. 32 | 33 | ### ObjectId.tryParse(oid, out, as) => Boolean 34 | 35 | ### ObjectId.isValid(oid) => Boolean 36 | 37 | ## Instance Methods 38 | 39 | ### ObjectId#equals(oidB) => Boolean 40 | 41 | ### ObjectId#toString() => String 42 | 43 | ### ObjectId#toJSON() => String 44 | 45 | ## running the tests 46 | 47 | From package root: 48 | 49 | $ npm install 50 | $ npm test 51 | 52 | ## contributors 53 | 54 | jden @leJDen 55 | 56 | ## license 57 | 58 | MIT. (c) 2013 Agile Diagnosis See LICENSE.md 59 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let Objectid = require('objectid') 4 | 5 | 6 | let path = require('path'), 7 | userConfig = require('./user-config'), 8 | appest = require('./appest'), 9 | conf = userConfig.readConfig('Appest') 10 | 11 | const Appest = Object.assign({}, appest, conf) 12 | 13 | let App = { 14 | $el: document.getElementById('js-add'), 15 | createTask (model) { 16 | const api = Appest.protocol + Appest.api_domain + '/api/v2/task' 17 | 18 | fetch(api, { 19 | credentials: 'include', 20 | method: 'post', 21 | headers: { 22 | 'Accept': 'application/json', 23 | 'Content-Type': 'application/json' 24 | }, 25 | body: JSON.stringify(model) 26 | }).then((res) => { 27 | if(res.status >= 200 && res.status < 300) { 28 | this.$el.value = '' 29 | } 30 | }) 31 | }, 32 | 33 | initEvent () { 34 | this.$el.addEventListener('keyup', (event) => { 35 | if(event.keyCode === 13) { 36 | let model = { 37 | assignee: null, 38 | content: '', 39 | deleted: 0, 40 | dueDate: null, 41 | id: Objectid().toString(), 42 | isAllDay: null, 43 | items: [], 44 | local: true, 45 | priority: 0, 46 | projectId: Appest.user.inboxId, 47 | remindTime: null, 48 | reminder: null, 49 | reminders: null, 50 | status: 0, 51 | timeZone: 'Asia/Shanghai', 52 | title: this.$el.value, 53 | } 54 | this.createTask(model) 55 | } 56 | }, false) 57 | }, 58 | 59 | init () { 60 | this.initEvent() 61 | } 62 | 63 | } 64 | 65 | App.init() 66 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/stores/argv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * argv.js: Simple memory-based store for command-line arguments. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | Memory = require('./memory').Memory; 10 | 11 | // 12 | // ### function Argv (options) 13 | // #### @options {Object} Options for this instance. 14 | // Constructor function for the Argv nconf store, a simple abstraction 15 | // around the Memory store that can read command-line arguments. 16 | // 17 | var Argv = exports.Argv = function (options, usage) { 18 | Memory.call(this, options); 19 | 20 | this.type = 'argv'; 21 | this.readOnly = true; 22 | this.options = options || false; 23 | this.usage = usage; 24 | }; 25 | 26 | // Inherit from the Memory store 27 | util.inherits(Argv, Memory); 28 | 29 | // 30 | // ### function loadSync () 31 | // Loads the data passed in from `process.argv` into this instance. 32 | // 33 | Argv.prototype.loadSync = function () { 34 | this.loadArgv(); 35 | return this.store; 36 | }; 37 | 38 | // 39 | // ### function loadArgv () 40 | // Loads the data passed in from the command-line arguments 41 | // into this instance. 42 | // 43 | Argv.prototype.loadArgv = function () { 44 | var self = this, 45 | yargs, argv; 46 | 47 | yargs = typeof this.options === 'object' 48 | ? require('yargs')(process.argv.slice(2)).options(this.options) 49 | : require('yargs')(process.argv.slice(2)); 50 | 51 | if (typeof this.usage === 'string') { yargs.usage(this.usage) } 52 | 53 | argv = yargs.argv 54 | 55 | if (!argv) { 56 | return; 57 | } 58 | 59 | this.readOnly = false; 60 | Object.keys(argv).forEach(function (key) { 61 | if (typeof argv[key] !== 'undefined') { 62 | self.set(key, argv[key]); 63 | } 64 | }); 65 | 66 | this.showHelp = yargs.showHelp 67 | this.help = yargs.help 68 | 69 | this.readOnly = true; 70 | return this.store; 71 | }; 72 | -------------------------------------------------------------------------------- /app/node_modules/objectid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "objectid", 3 | "version": "3.2.1", 4 | "description": "mongodb objectid utilities", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node node_modules/mocha/bin/mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/AgileDiagnosis/objectid.git" 12 | }, 13 | "keywords": [ 14 | "mongodb", 15 | "objectid" 16 | ], 17 | "author": { 18 | "name": "Agile Diagnosis", 19 | "email": "hello@agilediagnosis.com" 20 | }, 21 | "contributors": [ 22 | { 23 | "name": "jden", 24 | "email": "jason@denizac.org" 25 | } 26 | ], 27 | "license": "MIT", 28 | "devDependencies": { 29 | "bson": "~0.1.8", 30 | "chai": "~1.5.0", 31 | "chai-interface": "~1.1.0", 32 | "mocha": "~1.8.2", 33 | "mongodb": "~1.2.12" 34 | }, 35 | "dependencies": { 36 | "bson": "^0.1.9" 37 | }, 38 | "gitHead": "5bbce7750dda1e82d01a7de57266d16e29fbb3ce", 39 | "bugs": { 40 | "url": "https://github.com/AgileDiagnosis/objectid/issues" 41 | }, 42 | "homepage": "https://github.com/AgileDiagnosis/objectid#readme", 43 | "_id": "objectid@3.2.1", 44 | "_shasum": "a3b7a2b4d65f3a8348ef8497f023069b5cb019de", 45 | "_from": "objectid@>=3.2.1 <4.0.0", 46 | "_npmVersion": "3.1.0", 47 | "_nodeVersion": "0.10.36", 48 | "_npmUser": { 49 | "name": "jden", 50 | "email": "jason@denizac.org" 51 | }, 52 | "dist": { 53 | "shasum": "a3b7a2b4d65f3a8348ef8497f023069b5cb019de", 54 | "size": 3402, 55 | "noattachment": false, 56 | "tarball": "http://registry.npm.taobao.org/objectid/download/objectid-3.2.1.tgz" 57 | }, 58 | "maintainers": [ 59 | { 60 | "name": "jden", 61 | "email": "jason@denizac.org" 62 | } 63 | ], 64 | "directories": {}, 65 | "publish_time": 1437500306619, 66 | "_cnpm_publish_time": 1437500306619, 67 | "_resolved": "https://registry.npm.taobao.org/objectid/download/objectid-3.2.1.tgz", 68 | "readme": "ERROR: No README data found!" 69 | } 70 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/helpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * helpers.js: Test helpers for nconf. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var assert = require('assert'), 9 | spawn = require('child_process').spawn, 10 | util = require('util'), 11 | fs = require('fs'), 12 | path = require('path'), 13 | nconf = require('../lib/nconf'); 14 | 15 | exports.assertMerged = function (err, merged) { 16 | merged = merged instanceof nconf.Provider 17 | ? merged.store.store 18 | : merged; 19 | 20 | assert.isNull(err); 21 | assert.isObject(merged); 22 | assert.isTrue(merged.apples); 23 | assert.isTrue(merged.bananas); 24 | assert.isObject(merged.candy); 25 | assert.isTrue(merged.candy.something1); 26 | assert.isTrue(merged.candy.something2); 27 | assert.isTrue(merged.candy.something3); 28 | assert.isTrue(merged.candy.something4); 29 | assert.isTrue(merged.dates); 30 | assert.isTrue(merged.elderberries); 31 | }; 32 | 33 | exports.assertSystemConf = function (options) { 34 | return { 35 | topic: function () { 36 | var env = null; 37 | 38 | if (options.env) { 39 | env = {} 40 | Object.keys(process.env).forEach(function (key) { 41 | env[key] = process.env[key]; 42 | }); 43 | 44 | Object.keys(options.env).forEach(function (key) { 45 | env[key] = options.env[key]; 46 | }); 47 | } 48 | 49 | var child = spawn('node', [options.script].concat(options.argv), { env: env }); 50 | child.stdout.once('data', this.callback.bind(this, null)); 51 | }, 52 | "should respond with the value passed into the script": function (_, data) { 53 | assert.equal(data.toString(), 'foobar'); 54 | } 55 | } 56 | } 57 | 58 | // copy a file 59 | exports.cp = function (from, to, callback) { 60 | fs.readFile(from, function (err, data) { 61 | if (err) return callback(err); 62 | fs.writeFile(to, data, callback); 63 | }); 64 | }; 65 | 66 | exports.fixture = function (file) { 67 | return path.join(__dirname, 'fixtures', file); 68 | }; 69 | -------------------------------------------------------------------------------- /app/sign/icon.mustache: -------------------------------------------------------------------------------- 1 | { 2 | 'functions': true 3 | } 4 | 5 | $sprite: ( 6 | {{#items}} 7 | {{name}}: ( 8 | offset-x: {{px.offset_x}}, 9 | offset-y: {{px.offset_y}}, 10 | width: {{px.width}}, 11 | height: {{px.height}}, 12 | total-width: {{px.total_width}}, 13 | total-height: {{px.total_height}}, 14 | image: '{{{escaped_image}}}' 15 | ), 16 | {{/items}} 17 | parts: ( 18 | total-width: {{items.0.px.total_width}}, 19 | total-height: {{items.0.px.total_height}}, 20 | image: '{{{items.0.escaped_image}}}' 21 | ) 22 | ); 23 | 24 | {{#options.functions}} 25 | 26 | @mixin sprite-size($image) { 27 | background-size: map-get(map-get($sprite, $image), 'total-width') map-get(map-get($sprite, $image), 'total-height'); 28 | } 29 | 30 | @mixin sprite-width($image) { 31 | width: map-get(map-get($sprite, $image), 'width'); 32 | } 33 | 34 | @mixin sprite-height($image) { 35 | height: map-get(map-get($sprite, $image), 'height'); 36 | } 37 | 38 | @mixin sprite-position($image) { 39 | background-position: map-get(map-get($sprite, $image), 'offset-x') map-get(map-get($sprite, $image), 'offset-y'); 40 | } 41 | 42 | @mixin sprite-image($image) { 43 | background-image: url(map-get(map-get($sprite, $image), 'image') + '.png'); 44 | } 45 | 46 | @mixin sprite-image-retina($image) { 47 | background-image: url(map-get(map-get($sprite, $image), 'image') + '@2.png'); 48 | } 49 | 50 | 51 | {{/options.functions}} 52 | 53 | 54 | {{#items}} 55 | .icon-{{name}} { 56 | @include sprite-width("{{name}}"); 57 | @include sprite-height("{{name}}"); 58 | @include sprite-position("{{name}}"); 59 | } 60 | {{/items}} 61 | 62 | 63 | .icon_sign { 64 | @include sprite-size('parts'); 65 | background-repeat: no-repeat; 66 | display: inline-block; 67 | @media 68 | only screen and (-webkit-min-device-pixel-ratio: 2), 69 | only screen and ( min--moz-device-pixel-ratio: 2), 70 | only screen and ( -o-min-device-pixel-ratio: 2/1), 71 | only screen and ( min-device-pixel-ratio: 2), 72 | only screen and ( min-resolution: 192dpi), 73 | only screen and ( min-resolution: 2dppx) { 74 | 75 | @include sprite-size('parts'); 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | shell = require('gulp-shell'), 3 | rename = require('gulp-rename'), 4 | del = require('del'), 5 | packager = require('electron-packager'), 6 | builder = require('electron-builder'), 7 | argv = require('yargs').argv, 8 | pjson = require('./package.json') 9 | 10 | 11 | 12 | gulp.task('config', function () { 13 | var name = argv.site === 'dida' ? 'dida' : 'tick' 14 | gulp.src(['./app/' + 'appest-' + name + '.js']) 15 | .pipe(rename(function(path) { 16 | path.basename = 'appest' 17 | })) 18 | .pipe(gulp.dest('./app')) 19 | }) 20 | 21 | 22 | gulp.task('pkg:packager', function () { 23 | var name = argv.site === 'dida' ? 'dida' : 'ticktick' 24 | var opts = { 25 | 'dir': './app', 26 | 'arch': 'x64', 27 | 'platform': 'darwin,win32', 28 | 'all': false, 29 | 'app-category-type': 'public.app-category.utilities', 30 | 'app-version': pjson.version, 31 | 'asar': false, 32 | 'icon': './app/icons/icon.png', 33 | // 'ignore': /node_modules/, 34 | 'name': name, 35 | 'out': './pkg', 36 | 'overwrite': true 37 | } 38 | packager(opts, function done (err, appPath) { 39 | console.log(appPath) 40 | }) 41 | }) 42 | 43 | gulp.task('pkg:clean', function () { 44 | del(['./pkg']) 45 | }) 46 | 47 | gulp.task('pkg:builder:dida', shell.task([ 48 | 'electron-builder \"pkg/dida-darwin-x64/dida.app\" --platform=osx --out=\"pkg/osx\" --config=builder.json', 49 | 'electron-builder \"pkg/dida-win32-x64\" --platform=win --out=\"pkg/win64\" --config=builder.json', 50 | // 'electron-builder \"pkg/dida-win32-ia32\" --platform=win --out=\"pkg/win32\" --config=builder.json' 51 | ])) 52 | 53 | gulp.task('pkg:builder:tick', shell.task([ 54 | 'electron-builder \"pkg/ticktick-darwin-x64/ticktick.app\" --platform=osx --out=\"pkg/osx\" --config=builder-tick.json', 55 | 'electron-builder \"pkg/ticktick-win32-x64\" --platform=win --out=\"pkg/win64\" --config=builder-tick.json', 56 | // 'electron-builder \"pkg/ticktick-win32-ia32\" --platform=win --out=\"pkg/win32\" --config=builder-tick.json' 57 | ])) 58 | 59 | gulp.task('pkg:dida', shell.task([ 60 | 'gulp pkg:clean', 61 | 'gulp config --site dida', 62 | 'gulp pkg:packager --site dida', 63 | 'gulp pkg:builder:dida' 64 | ])) 65 | 66 | gulp.task('pkg:tick', shell.task([ 67 | 'gulp pkg:clean', 68 | 'gulp config --site tick', 69 | 'gulp pkg:packager --site tick', 70 | 'gulp pkg:builder:tick' 71 | ])) 72 | -------------------------------------------------------------------------------- /app/node_modules/nconf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nconf", 3 | "description": "Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging.", 4 | "version": "0.8.4", 5 | "author": { 6 | "name": "Charlie Robbins", 7 | "email": "charlie.robbins@gmail.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/flatiron/nconf.git" 12 | }, 13 | "keywords": [ 14 | "configuration", 15 | "key value store", 16 | "plugabble" 17 | ], 18 | "dependencies": { 19 | "async": "^1.4.0", 20 | "ini": "^1.3.0", 21 | "secure-keys": "^1.0.0", 22 | "yargs": "^3.19.0" 23 | }, 24 | "devDependencies": { 25 | "coveralls": "^2.11.4", 26 | "istanbul": "^0.4.1", 27 | "vows": "0.8.x" 28 | }, 29 | "main": "./lib/nconf", 30 | "scripts": { 31 | "test": "vows test/*-test.js test/**/*-test.js --spec", 32 | "cover": "istanbul cover vows -- test/*-test.js test/**/*-test.js --spec", 33 | "coveralls": "cat coverage/lcov.info | coveralls" 34 | }, 35 | "engines": { 36 | "node": ">= 0.4.0" 37 | }, 38 | "license": "MIT", 39 | "gitHead": "3d4e58957878fab80fb3125784c04b615cf2f52e", 40 | "bugs": { 41 | "url": "https://github.com/flatiron/nconf/issues" 42 | }, 43 | "homepage": "https://github.com/flatiron/nconf#readme", 44 | "_id": "nconf@0.8.4", 45 | "_shasum": "9502234f7ad6238cab7f92d7c068c20434d3ff93", 46 | "_from": "nconf@>=0.8.4 <0.9.0", 47 | "_npmVersion": "2.14.7", 48 | "_nodeVersion": "4.2.3", 49 | "_npmUser": { 50 | "name": "jcrugzz", 51 | "email": "jcrugzz@gmail.com" 52 | }, 53 | "dist": { 54 | "shasum": "9502234f7ad6238cab7f92d7c068c20434d3ff93", 55 | "size": 36052, 56 | "noattachment": false, 57 | "tarball": "http://registry.npm.taobao.org/nconf/download/nconf-0.8.4.tgz" 58 | }, 59 | "maintainers": [ 60 | { 61 | "name": "indexzero", 62 | "email": "charlie.robbins@gmail.com" 63 | }, 64 | { 65 | "name": "jcrugzz", 66 | "email": "jcrugzz@gmail.com" 67 | } 68 | ], 69 | "_npmOperationalInternal": { 70 | "host": "packages-9-west.internal.npmjs.com", 71 | "tmp": "tmp/nconf-0.8.4.tgz_1454488758244_0.9802679584827274" 72 | }, 73 | "directories": {}, 74 | "publish_time": 1454488758997, 75 | "_cnpm_publish_time": 1454488758997, 76 | "_resolved": "https://registry.npm.taobao.org/nconf/download/nconf-0.8.4.tgz", 77 | "readme": "ERROR: No README data found!" 78 | } 79 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/stores/env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * env.js: Simple memory-based store for environment variables 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var util = require('util'), 9 | common = require('../common'), 10 | Memory = require('./memory').Memory; 11 | 12 | // 13 | // ### function Env (options) 14 | // #### @options {Object} Options for this instance. 15 | // Constructor function for the Env nconf store, a simple abstraction 16 | // around the Memory store that can read process environment variables. 17 | // 18 | var Env = exports.Env = function (options) { 19 | Memory.call(this, options); 20 | 21 | options = options || {}; 22 | this.type = 'env'; 23 | this.readOnly = true; 24 | this.whitelist = options.whitelist || []; 25 | this.separator = options.separator || ''; 26 | this.lowerCase = options.lowerCase || false; 27 | 28 | if (({}).toString.call(options.match) === '[object RegExp]' 29 | && typeof options !== 'string') { 30 | this.match = options.match; 31 | } 32 | 33 | if (options instanceof Array) { 34 | this.whitelist = options; 35 | } 36 | if (typeof(options) === 'string') { 37 | this.separator = options; 38 | } 39 | }; 40 | 41 | // Inherit from the Memory store 42 | util.inherits(Env, Memory); 43 | 44 | // 45 | // ### function loadSync () 46 | // Loads the data passed in from `process.env` into this instance. 47 | // 48 | Env.prototype.loadSync = function () { 49 | this.loadEnv(); 50 | return this.store; 51 | }; 52 | 53 | // 54 | // ### function loadEnv () 55 | // Loads the data passed in from `process.env` into this instance. 56 | // 57 | Env.prototype.loadEnv = function () { 58 | var self = this; 59 | 60 | var env = process.env; 61 | 62 | if (this.lowerCase) { 63 | Object.keys(env).forEach(function (key) { 64 | env[key.toLowerCase()] = env[key]; 65 | }); 66 | } 67 | 68 | this.readOnly = false; 69 | Object.keys(env).filter(function (key) { 70 | if (self.match && self.whitelist.length) { 71 | return key.match(self.match) || self.whitelist.indexOf(key) !== -1 72 | } 73 | else if (self.match) { 74 | return key.match(self.match); 75 | } 76 | else { 77 | return !self.whitelist.length || self.whitelist.indexOf(key) !== -1 78 | } 79 | }).forEach(function (key) { 80 | if (self.separator) { 81 | self.set(common.key.apply(common, key.split(self.separator)), env[key]); 82 | } 83 | else { 84 | self.set(key, env[key]); 85 | } 86 | }); 87 | 88 | this.readOnly = true; 89 | return this.store; 90 | }; 91 | 92 | -------------------------------------------------------------------------------- /app/sign/src/styles/_color.scss: -------------------------------------------------------------------------------- 1 | /* ========== Color Palettes ========== */ 2 | 3 | $white: #fff; 4 | $black: #000; 5 | $bistre: #333; 6 | $darkgrey: #737373; 7 | $manatee: #aaa; 8 | $red: #ff3180; 9 | $indigo: #617fde; 10 | $bluesky: #516bc1; 11 | $theme-red: #fd7897; 12 | $theme-green: #06ce90; 13 | 14 | // min-cal 15 | $bluegray: #5a76bc; 16 | $ballblue: #617fde; 17 | $cobalt: #9cb3ed; 18 | $vanilla: #ffc817; 19 | 20 | // tk-cal 21 | $paleorange: #fff2e2; 22 | $lightorange: #ffab3c; 23 | $lightgrayishblue: rgba(97, 127, 222, 0.2); 24 | $grayishblue: #eff2fb; 25 | 26 | 27 | $whitegray: #fdfdfd; 28 | 29 | $vwhitegray: #fcfcfc; 30 | 31 | $pink: #e86499; 32 | $tuftsblue: #8ea0cb; 33 | 34 | 35 | $brandeisblue: #6d8ee1; 36 | 37 | $steelblue: #5c7bc5; 38 | $ceruleanblue: #5a77bf; 39 | 40 | $orangered: #fd5d5f; 41 | $mediumseagreen: #3cc6cc; 42 | 43 | $mulberry: #e86499; 44 | $shamrock: #28e0bf; 45 | $springgreen: #49d498; 46 | $yellow: #ffefd8; 47 | 48 | $grey: #ddd; 49 | $cadetgrey: #ababab; 50 | $ashgrey: #f3f3f3; 51 | $platinum: #f7f7f7; 52 | $timberwolf: #ececec; 53 | $lavendergray: #c7c7c7; 54 | 55 | $whitelilac: #ebebeb; 56 | 57 | $disabled: #a8a8a8; 58 | 59 | $verylightgray: #fefefe; 60 | 61 | $lightwhite: #fafafa; 62 | 63 | $white4: rgba(225,225,225,0.4); 64 | $white6: rgba(225,225,225,0.6); 65 | $white8: rgba(225,225,225,0.8); 66 | 67 | $black05: rgba(0,0,0,.05); 68 | $black08: rgba(0,0,0,.08); 69 | $black1: rgba(0,0,0,.1); 70 | $black2: rgba(0,0,0,.2); 71 | $black3: rgba(0,0,0,.3); 72 | $black4: rgba(0,0,0,.4); 73 | 74 | $grey15: rgba(243,243,243,0.6); 75 | 76 | // list color 77 | $l-color-0: transparent; 78 | $l-color-1: #90ABD6; 79 | $l-color-2: #499FB4; 80 | $l-color-3: #3CB878; 81 | $l-color-4: #7FDD81; 82 | $l-color-5: #C4E365; 83 | $l-color-6: #E864A8; 84 | $l-color-7: #FD5E5E; 85 | $l-color-8: #F3BB4C; 86 | $l-color-9: #FFE954; 87 | 88 | // priority color 89 | $priority-high: #ff3180; 90 | $priority-medium: #ffc817; 91 | $priority-low: #617fde; 92 | 93 | 94 | 95 | // activity 96 | $limegreen: #18d791; 97 | $softred: rgba(241, 89, 100,0.8); 98 | $moderateblue: rgba(100,132,213,0.8); 99 | 100 | $lightblack: rgba(0,0,0,0.1); 101 | 102 | $lightgray: rgba(0, 0, 0, 0.15); 103 | 104 | $blackwhite: #d3dbf3; 105 | $blackblue: #5a7bcf; 106 | 107 | $opacityindigo: rgba(97,127,222,0.1); 108 | $opacitywhite: rgba(255,255,255,0.3); 109 | 110 | 111 | // dark theme 112 | $dark-deepgray: #313643; 113 | $dark-lightgray: #3f4658; 114 | $dark-lightgray1: rgba(49, 54, 67, 0.1); 115 | $dark-lightgray2: rgba(49, 54, 67, 0.2); 116 | -------------------------------------------------------------------------------- /app/sign/src/styles/_functions.scss: -------------------------------------------------------------------------------- 1 | // Foundation by ZURB 2 | // foundation.zurb.com 3 | // Licensed under MIT Open Source 4 | 5 | // This is the default html and body font-size for the base rem value. 6 | $rem-base: 16px !default; 7 | 8 | // IMPORT ONCE 9 | // We use this to prevent styles from being loaded multiple times for compenents that rely on other components. 10 | $modules: () !default; 11 | @mixin exports($name) { 12 | @if (index($modules, $name) == false or index($modules, $name) == null) { 13 | $modules: append($modules, $name); 14 | @content; 15 | } 16 | } 17 | 18 | // 19 | // @functions 20 | // 21 | 22 | 23 | // RANGES 24 | // We use these functions to define ranges for various things, like media queries. 25 | @function lower-bound($range){ 26 | @if length($range) <= 0 { 27 | @return 0; 28 | } 29 | @return nth($range,1); 30 | } 31 | 32 | @function upper-bound($range) { 33 | @if length($range) < 2 { 34 | @return 999999999999; 35 | } 36 | @return nth($range, 2); 37 | } 38 | 39 | // STRIP UNIT 40 | // It strips the unit of measure and returns it 41 | @function strip-unit($num) { 42 | @return $num / ($num * 0 + 1); 43 | } 44 | 45 | // CONVERT TO REM 46 | @function convert-to-rem($value, $base-value: $rem-base) { 47 | $value: strip-unit($value) / strip-unit($base-value) * 1rem; 48 | @if ($value == 0rem) { $value: 0; } // Turn 0rem into 0 49 | @return $value; 50 | } 51 | 52 | @function data($attr) { 53 | @if $namespace { 54 | @return '[data-' + $namespace + '-' + $attr + ']'; 55 | } 56 | 57 | @return '[data-' + $attr + ']'; 58 | } 59 | 60 | // REM CALC 61 | 62 | // New Syntax, allows to optionally calculate on a different base value to counter compounding effect of rem's. 63 | // Call with 1, 2, 3 or 4 parameters, 'px' is not required but supported: 64 | // 65 | // rem-calc(10 20 30px 40); 66 | // 67 | // Space delimited, if you want to delimit using comma's, wrap it in another pair of brackets 68 | // 69 | // rem-calc((10, 20, 30, 40px)); 70 | // 71 | // Optionally call with a different base (eg: 8px) to calculate rem. 72 | // 73 | // rem-calc(16px 32px 48px, 8px); 74 | // 75 | // If you require to comma separate your list 76 | // 77 | // rem-calc((16px, 32px, 48), 8px); 78 | 79 | @function rem-calc($values, $base-value: $rem-base) { 80 | //$max: length($values); 81 | 82 | //@if $max == 1 { @return convert-to-rem(nth($values, 1), $base-value); } 83 | 84 | //$remValues: (); 85 | //@for $i from 1 through $max { 86 | // $remValues: append($remValues, convert-to-rem(nth($values, $i), $base-value)); 87 | //} 88 | //@return $remValues; 89 | @return $values + 0px; 90 | } 91 | -------------------------------------------------------------------------------- /app/sign/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | spritesmith = require('gulp.spritesmith'), 3 | minifycss = require('gulp-cssnano'), 4 | rename = require('gulp-rename'), 5 | shell = require('gulp-shell'), 6 | del = require('del'), 7 | autoprefixer = require('gulp-autoprefixer'), 8 | sass = require('gulp-sass') 9 | 10 | 11 | var dirs = { 12 | template : 'icon.mustache', 13 | styles : './src/styles', 14 | dest : './dist', 15 | images: { 16 | dest: './src/images', 17 | tick_sign_icons : './src/images/tick/sign-icons', 18 | icons : './src/images/common/sign-icons' 19 | }, 20 | } 21 | 22 | var config = { 23 | 'sign' : { 24 | 'src' : [dirs.images.icons + '/*'], 25 | 'imgName' : 'sign-icons.png', 26 | 'cssName' : 'sign-icons.scss', 27 | 'imgDest' : dirs.dest, 28 | 'buildDest': dirs.build, 29 | 'cssDest' : dirs.styles, 30 | 'cssTemplate' : dirs.template, 31 | 'padding' : 0 32 | }, 33 | 34 | 'sign2' : { 35 | 'src' : [dirs.images.icons + '@2/*'], 36 | 'imgName' : 'sign-icons@2.png', 37 | 'cssName' : 'sign-icons@2.scss', 38 | 'imgDest' : dirs.dest, 39 | 'buildDest': dirs.build, 40 | 'cssDest' : dirs.styles, 41 | 'cssTemplate' : dirs.template, 42 | 'padding' : 0 43 | } 44 | } 45 | 46 | function sprite(cfg) { 47 | var sprite = gulp.src(cfg.src) 48 | .pipe(spritesmith({ 49 | imgName: cfg.imgName, 50 | cssName: cfg.cssName, 51 | algorithm: 'binary-tree', 52 | cssFormat: 'scss', 53 | cssTemplate: cfg.cssTemplate, 54 | engine: 'pngsmith', 55 | padding : cfg.padding 56 | })) 57 | 58 | sprite.img.pipe(gulp.dest(cfg.imgDest)) 59 | sprite.css.pipe(gulp.dest(cfg.cssDest)) 60 | } 61 | 62 | gulp.task('styles', function () { 63 | gulp.src(dirs.styles + '/main.scss') 64 | .pipe(sass()) 65 | .pipe(autoprefixer({ 66 | browsers: ['last 2 version', 'safari 5', 'ie 7', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'], 67 | cascade: false 68 | })) 69 | .pipe(rename({ 70 | basename: 'sign' 71 | })) 72 | .pipe(minifycss({ keepSpecialComments : 0 })) 73 | .pipe(rename({ 74 | suffix: '.min' 75 | })) 76 | .pipe(gulp.dest(dirs.dest)) 77 | }) 78 | 79 | gulp.task('sprite', function () { 80 | sprite(config.sign) 81 | sprite(config.sign2) 82 | }) 83 | 84 | gulp.task('clean', function() { 85 | del([dirs.dest], { 86 | force: true 87 | }) 88 | }) 89 | 90 | gulp.task('build', shell.task([ 91 | 'gulp clean', 92 | 'gulp sprite', 93 | 'gulp styles' 94 | ])) 95 | 96 | gulp.task('watch', function () { 97 | gulp.watch('./src/**/*', ['sprite','styles']) 98 | }) 99 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let path = require('path'), 4 | electron = require('electron') 5 | 6 | let app = electron.app 7 | 8 | let Menu = electron.Menu, 9 | BrowserWindow = electron.BrowserWindow, 10 | ipc = electron.ipcMain, 11 | globalShortcut = electron.globalShortcut 12 | 13 | let userConfig = require('./user-config'), 14 | menu = require('./menu'), 15 | appest = require('./appest') 16 | 17 | let setAppest = data => { 18 | userConfig.saveConfig(data) 19 | } 20 | 21 | // register shortcut 22 | let registerShortcut = () => { 23 | let ret = globalShortcut.register('Command+Shift+A', () => { 24 | if(indexWindow) { 25 | indexWindow.show() 26 | indexWindow.focus() 27 | } 28 | }) 29 | if (!ret) { 30 | console.log('registration failed') 31 | } 32 | } 33 | 34 | let unRegisterShortcut = () => { 35 | globalShortcut.unregisterAll() 36 | } 37 | 38 | // app.commandLine.appendSwitch('ignore-certificate-errors', 'true') 39 | 40 | // Report crashes to our server. 41 | electron.crashReporter.start() 42 | 43 | // Quit when all windows are closed. 44 | app.on('window-all-closed', () => { 45 | // On OS X it is common for applications and their menu bar 46 | // to stay active until the user quits explicitly with Cmd + Q 47 | // if (process.platform != 'darwin') { 48 | unRegisterShortcut() 49 | app.quit() 50 | // } 51 | }) 52 | 53 | let signin = data => { 54 | loginWindow && loginWindow.close() 55 | setAppest(data) 56 | if(indexWindow) { 57 | indexWindow.show() 58 | indexWindow.focus() 59 | } else { 60 | openIndex() 61 | } 62 | } 63 | 64 | // 登录 65 | ipc.on('signin', (event, data) => { 66 | signin(data) 67 | }) 68 | 69 | // 登出 70 | ipc.on('signout', (event, data) => { 71 | userConfig.clearConfig() 72 | indexWindow && indexWindow.close() 73 | if(loginWindow) { 74 | loginWindow.show() 75 | loginWindow.focus() 76 | } else { 77 | openLogin() 78 | } 79 | }) 80 | 81 | // This method will be called when Electron has finished 82 | // initialization and is ready to create browser windows. 83 | app.on('ready', () => { 84 | let config = userConfig.readConfig('Appest') 85 | if(config && config.user && config.user.inboxId) { 86 | openIndex() 87 | } else { 88 | openLogin() 89 | } 90 | Menu.setApplicationMenu(menu) 91 | registerShortcut() 92 | }) 93 | 94 | // 默认窗口状态 95 | let defaultWin = { 96 | width: 620, 97 | height: 600, 98 | minWidth: 600, 99 | resizable: false, 100 | title: appest.productName, 101 | icon: path.resolve('./icons/', 'icon.png') 102 | } 103 | 104 | // 登陆窗口 105 | let loginWindow 106 | let openLogin = () => { 107 | 108 | let win = Object.assign({}, defaultWin) 109 | 110 | loginWindow = new BrowserWindow(win) 111 | 112 | loginWindow.loadURL('file://' + __dirname + '/signin.html') 113 | 114 | // loginWindow.webContents.openDevTools() 115 | 116 | loginWindow.on('closed', () => { 117 | loginWindow.destroy() 118 | loginWindow = null 119 | }) 120 | } 121 | 122 | // 登录之后 123 | let indexWindow = null 124 | let openIndex = () => { 125 | if (indexWindow) 126 | return 127 | 128 | let win = Object.assign({}, defaultWin, { 129 | height: 55, 130 | transparent: false, 131 | frame: false 132 | }) 133 | 134 | indexWindow = new BrowserWindow(win) 135 | 136 | indexWindow.loadURL('file://' + __dirname + '/index.html') 137 | 138 | // indexWindow.webContents.openDevTools() 139 | 140 | indexWindow.on('closed', () => { 141 | indexWindow.destroy() 142 | indexWindow = null 143 | }) 144 | } 145 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.js: Utility functions for the nconf module. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | async = require('async'), 10 | formats = require('./formats'), 11 | Memory = require('./stores/memory').Memory; 12 | 13 | var common = exports; 14 | 15 | // 16 | // ### function path (key) 17 | // #### @key {string} The ':' delimited key to split 18 | // Returns a fully-qualified path to a nested nconf key. 19 | // If given null or undefined it should return an empty path. 20 | // '' should still be respected as a path. 21 | // 22 | common.path = function (key, separator) { 23 | separator = separator || ':'; 24 | return key == null ? [] : key.split(separator); 25 | }; 26 | 27 | // 28 | // ### function key (arguments) 29 | // Returns a `:` joined string from the `arguments`. 30 | // 31 | common.key = function () { 32 | return Array.prototype.slice.call(arguments).join(':'); 33 | }; 34 | 35 | // 36 | // ### function key (arguments) 37 | // Returns a joined string from the `arguments`, 38 | // first argument is the join delimiter. 39 | // 40 | common.keyed = function () { 41 | return Array.prototype.slice.call(arguments, 1).join(arguments[0]); 42 | }; 43 | 44 | // 45 | // ### function loadFiles (files, callback) 46 | // #### @files {Object|Array} List of files (or settings object) to load. 47 | // #### @callback {function} Continuation to respond to when complete. 48 | // Loads all the data in the specified `files`. 49 | // 50 | common.loadFiles = function (files, callback) { 51 | if (!files) { 52 | return callback(null, {}); 53 | } 54 | 55 | var options = Array.isArray(files) ? { files: files } : files; 56 | 57 | // 58 | // Set the default JSON format if not already 59 | // specified 60 | // 61 | options.format = options.format || formats.json; 62 | 63 | function parseFile (file, next) { 64 | fs.readFile(file, function (err, data) { 65 | return !err 66 | ? next(null, options.format.parse(data.toString())) 67 | : next(err); 68 | }); 69 | } 70 | 71 | async.map(options.files, parseFile, function (err, objs) { 72 | return err ? callback(err) : callback(null, common.merge(objs)); 73 | }); 74 | }; 75 | 76 | // 77 | // ### function loadFilesSync (files) 78 | // #### @files {Object|Array} List of files (or settings object) to load. 79 | // Loads all the data in the specified `files` synchronously. 80 | // 81 | common.loadFilesSync = function (files) { 82 | if (!files) { 83 | return; 84 | } 85 | 86 | // 87 | // Set the default JSON format if not already 88 | // specified 89 | // 90 | var options = Array.isArray(files) ? { files: files } : files; 91 | options.format = options.format || formats.json; 92 | 93 | return common.merge(options.files.map(function (file) { 94 | return options.format.parse(fs.readFileSync(file, 'utf8')); 95 | })); 96 | }; 97 | 98 | // 99 | // ### function merge (objs) 100 | // #### @objs {Array} Array of object literals to merge 101 | // Merges the specified `objs` using a temporary instance 102 | // of `stores.Memory`. 103 | // 104 | common.merge = function (objs) { 105 | var store = new Memory(); 106 | 107 | objs.forEach(function (obj) { 108 | Object.keys(obj).forEach(function (key) { 109 | store.merge(key, obj[key]); 110 | }); 111 | }); 112 | 113 | return store.store; 114 | }; 115 | 116 | // 117 | // ### function capitalize (str) 118 | // #### @str {string} String to capitalize 119 | // Capitalizes the specified `str`. 120 | // 121 | common.capitalize = function (str) { 122 | return str && str[0].toUpperCase() + str.slice(1); 123 | }; 124 | -------------------------------------------------------------------------------- /app/menu.js: -------------------------------------------------------------------------------- 1 | // menu 2 | 'use strict' 3 | 4 | let electron = require('electron') 5 | 6 | let app = electron.app, 7 | shell = electron.shell, 8 | Menu = electron.Menu, 9 | ipc = electron.ipcMain 10 | 11 | let appest = require('./appest') 12 | 13 | let template = [{ 14 | label: 'Edit', 15 | submenu: [{ 16 | label: 'Undo', 17 | accelerator: 'CmdOrCtrl+Z', 18 | role: 'undo' 19 | }, { 20 | label: 'Redo', 21 | accelerator: 'Shift+CmdOrCtrl+Z', 22 | role: 'redo' 23 | }, { 24 | type: 'separator' 25 | }, { 26 | label: 'Cut', 27 | accelerator: 'CmdOrCtrl+X', 28 | role: 'cut' 29 | }, { 30 | label: 'Copy', 31 | accelerator: 'CmdOrCtrl+C', 32 | role: 'copy' 33 | }, { 34 | label: 'Paste', 35 | accelerator: 'CmdOrCtrl+V', 36 | role: 'paste' 37 | }, { 38 | label: 'Select All', 39 | accelerator: 'CmdOrCtrl+A', 40 | role: 'selectall' 41 | }, ] 42 | }, { 43 | label: 'View', 44 | submenu: [{ 45 | label: 'Reload', 46 | accelerator: 'CmdOrCtrl+R', 47 | click (item, focusedWindow) { 48 | if (focusedWindow) 49 | focusedWindow.reload() 50 | } 51 | }, { 52 | label: 'Toggle Full Screen', 53 | accelerator: ( () => { 54 | if (process.platform === 'darwin') 55 | return 'Ctrl+Command+F' 56 | else 57 | return 'F11' 58 | })(), 59 | click (item, focusedWindow) { 60 | if (focusedWindow) 61 | focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) 62 | } 63 | }, 64 | { 65 | label: 'Toggle Developer Tools', 66 | accelerator: ( () => { 67 | if (process.platform === 'darwin') 68 | return 'Alt+Command+I' 69 | else 70 | return 'Ctrl+Shift+I' 71 | })(), 72 | click (item, focusedWindow) { 73 | if (focusedWindow) 74 | focusedWindow.toggleDevTools() 75 | } 76 | }, 77 | ] 78 | }, { 79 | label: 'Window', 80 | role: 'window', 81 | submenu: [{ 82 | label: 'Minimize', 83 | accelerator: 'CmdOrCtrl+M', 84 | role: 'minimize' 85 | }, { 86 | label: 'Close', 87 | accelerator: 'CmdOrCtrl+W', 88 | role: 'close' 89 | }, ] 90 | }, { 91 | label: 'Help', 92 | role: 'help', 93 | submenu: [{ 94 | label: 'Learn More', 95 | click () { 96 | shell.openExternal('https://help.' + appest.domain) 97 | } 98 | }, ] 99 | }, ] 100 | 101 | if (process.platform === 'darwin') { 102 | let name = appest.productName 103 | // let name = app.getName() 104 | template.unshift({ 105 | label: name, 106 | submenu: [{ 107 | label: 'About ' + name, 108 | role: 'about' 109 | }, { 110 | type: 'separator' 111 | }, { 112 | label: 'Services', 113 | role: 'services', 114 | submenu: [] 115 | }, { 116 | type: 'separator' 117 | }, { 118 | label: 'Hide ' + name, 119 | accelerator: 'Command+H', 120 | role: 'hide' 121 | }, { 122 | label: 'Hide Others', 123 | accelerator: 'Command+Alt+H', 124 | role: 'hideothers' 125 | }, { 126 | label: 'Show All', 127 | role: 'unhide' 128 | }, { 129 | type: 'separator' 130 | }, { 131 | label: 'Login Out', 132 | accelerator: 'Command+Shift+Q', 133 | click () { 134 | ipc._events.signout() 135 | } 136 | }, { 137 | type: 'separator' 138 | }, { 139 | label: 'Quit', 140 | accelerator: 'Command+Q', 141 | click () { 142 | app.quit() 143 | } 144 | }, ] 145 | }) 146 | // Window menu. 147 | template[3].submenu.push({ 148 | type: 'separator' 149 | }, { 150 | label: 'Bring All to Front', 151 | role: 'front' 152 | }) 153 | } 154 | 155 | let menu = Menu.buildFromTemplate(template) 156 | 157 | module.exports = menu 158 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/hierarchy-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * hierarchy-test.js: Basic tests for hierarchical file stores. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var assert = require('assert'), 9 | fs = require('fs'), 10 | path = require('path'), 11 | spawn = require('child_process').spawn, 12 | vows = require('vows'), 13 | nconf = require('../lib/nconf'); 14 | 15 | var configDir = path.join(__dirname, 'fixtures', 'hierarchy'), 16 | globalConfig = path.join(configDir, 'global.json'), 17 | userConfig = path.join(configDir, 'user.json'); 18 | 19 | vows.describe('nconf/hierarchy').addBatch({ 20 | "When using nconf": { 21 | "configured with two file stores": { 22 | topic: function () { 23 | nconf.add('user', { type: 'file', file: userConfig }); 24 | nconf.add('global', { type: 'file', file: globalConfig }); 25 | nconf.load(); 26 | return nconf; 27 | }, 28 | "should have the appropriate keys present": function () { 29 | assert.equal(nconf.get('title'), 'My specific title'); 30 | assert.equal(nconf.get('color'), 'green'); 31 | assert.equal(nconf.get('movie'), 'Kill Bill'); 32 | } 33 | }, 34 | "configured with two file stores using `file`": { 35 | topic: function () { 36 | nconf.file('user', userConfig); 37 | nconf.file('global', globalConfig); 38 | nconf.load(); 39 | return nconf; 40 | }, 41 | "should have the appropriate keys present": function () { 42 | assert.equal(nconf.get('title'), 'My specific title'); 43 | assert.equal(nconf.get('color'), 'green'); 44 | assert.equal(nconf.get('movie'), 'Kill Bill'); 45 | } 46 | }, 47 | "configured with .argv(), .env() and .file()": { 48 | topic: function () { 49 | var configFile = path.join(__dirname, 'fixtures', 'load-save.json'), 50 | script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-save.js'), 51 | argv = ['--foo', 'foo', '--bar', 'bar'], 52 | that = this, 53 | data = '', 54 | child; 55 | 56 | try { fs.unlinkSync(configFile) } 57 | catch (ex) { } 58 | 59 | child = spawn('node', [script].concat(argv)); 60 | 61 | child.stdout.on('data', function (d) { 62 | data += d; 63 | }); 64 | 65 | child.on('close', function () { 66 | fs.readFile(configFile, 'utf8', that.callback.bind(null, null, data)); 67 | }); 68 | }, 69 | "should not persist information passed in to process.env and process.argv to disk ": function (_, data, _, ondisk){ 70 | assert.equal(data, 'foo'); 71 | assert.deepEqual(JSON.parse(ondisk), { 72 | database: { 73 | host: '127.0.0.1', 74 | port: 5984 75 | } 76 | }); 77 | } 78 | }, 79 | "configured with .argv(), .file() and invoked with nested command line options": { 80 | topic: function () { 81 | var script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-merge.js'), 82 | argv = ['--candy:something', 'foo', '--candy:something5:second', 'bar'], 83 | that = this, 84 | data = '', 85 | child; 86 | 87 | child = spawn('node', [script].concat(argv)); 88 | 89 | child.stdout.on('data', function (d) { 90 | data += d; 91 | }); 92 | 93 | child.on('close', function() { 94 | that.callback(null, data); 95 | }); 96 | }, 97 | "should merge nested objects ": function (err, data) { 98 | assert.deepEqual(JSON.parse(data), { 99 | apples: true, 100 | candy: { 101 | something: 'foo', 102 | something1: true, 103 | something2: true, 104 | something5: { 105 | first: 1, 106 | second: 'bar' 107 | } 108 | } 109 | }); 110 | } 111 | } 112 | } 113 | }).export(module); 114 | -------------------------------------------------------------------------------- /app/sign/src/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | 2 | @mixin centerX { 3 | position: absolute; 4 | left: 50%; 5 | transform: translateX(-50%); 6 | } 7 | 8 | @mixin centerY { 9 | position: absolute; 10 | top: 50%; 11 | transform: translateY(-50%); 12 | } 13 | 14 | @mixin center { 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | transform: translate(-50%, -50%); 19 | } 20 | 21 | @mixin ellipsis { 22 | max-width: 100%; 23 | white-space: nowrap; 24 | text-overflow: ellipsis; 25 | overflow: hidden; 26 | } 27 | 28 | // fix inline-block ele bug 29 | @mixin inlineblockfix { 30 | font-size: 0; 31 | } 32 | 33 | //clear floats 34 | @mixin clearfix { 35 | &:before, &:after { content: " "; display: table; } 36 | &:after { clear: both; } 37 | } 38 | 39 | //user-select 40 | @mixin user-select($type: text) { 41 | -moz-user-select: $type; 42 | -webkit-user-select: $type; 43 | -ms-user-select: $type; 44 | } 45 | 46 | // 47 | @mixin liheight($height) { 48 | height: $height; 49 | line-height: $height; 50 | } 51 | 52 | @mixin minliheight($height) { 53 | min-height: $height; 54 | line-height: $height; 55 | } 56 | 57 | // 58 | @mixin single-transition($property:all, $speed:300ms, $ease:ease-out) { 59 | transition: $property $speed $ease; 60 | } 61 | 62 | // create isosceles triangles 63 | // $triangle-size - Used to set border-size. No default, set a px or em size. 64 | // $triangle-color - Used to set border-color which makes up triangle. No default 65 | // $triangle-direction - Used to determine which direction triangle points. Options: top, bottom, left, right 66 | @mixin css-triangle($triangle-size, $triangle-color, $triangle-direction) { 67 | border: inset $triangle-size; 68 | content: ""; 69 | display: block; 70 | height: 0; 71 | width: 0; 72 | @if ($triangle-direction == top) { 73 | border-color: $triangle-color transparent transparent transparent; 74 | border-top-style: solid; 75 | } 76 | @if ($triangle-direction == bottom) { 77 | border-color: transparent transparent $triangle-color transparent; 78 | border-bottom-style: solid; 79 | } 80 | @if ($triangle-direction == left) { 81 | border-color: transparent transparent transparent $triangle-color; 82 | border-left-style: solid; 83 | } 84 | @if ($triangle-direction == right) { 85 | border-color: transparent $triangle-color transparent transparent; 86 | border-right-style: solid; 87 | } 88 | } 89 | 90 | // create the icon with three lines aka the hamburger icon, the menu-icon or the navicon 91 | // $width - Width of hamburger icon in rem 92 | // $left - If false, icon will be centered horizontally || explicitly set value in rem 93 | // $top - If false, icon will be centered vertically || explicitly set value in rem 94 | // $thickness - thickness of lines in hamburger icon, set value in px 95 | // $gap - spacing between the lines in hamburger icon, set value in px 96 | // $color - icon color 97 | // $hover-color - icon color during hover 98 | // $offcanvas - Set to true of @include in offcanvas 99 | @mixin hamburger($width, $left, $top, $thickness, $gap, $color, $hover-color, $offcanvas) { 100 | span::after { 101 | content: ""; 102 | display: block; 103 | height: 0; 104 | position: absolute; 105 | 106 | @if $offcanvas { 107 | @if $top { 108 | top: $top; 109 | } 110 | @else { 111 | top: 50%; 112 | margin-top: (-$width/2); 113 | } 114 | @if $left { 115 | left: $left; 116 | } 117 | @else { 118 | left: (rem-calc(50) - $width)/2; 119 | } 120 | } 121 | @else { 122 | top: 50%; 123 | #{$opposite-direction}: 15px; 124 | } 125 | 126 | box-shadow: 127 | 0 0 0 $thickness $color, 128 | 0 $gap + $thickness 0 $thickness $color, 129 | 0 (2 * $gap + 2*$thickness) 0 $thickness $color; 130 | width: $width; 131 | } 132 | span:hover:after { 133 | box-shadow: 134 | 0 0 0 $thickness $hover-color, 135 | 0 $gap + $thickness 0 $thickness $hover-color, 136 | 0 (2 * $gap + 2*$thickness) 0 $thickness $hover-color; 137 | } 138 | } -------------------------------------------------------------------------------- /app/sign/src/styles/sign-icons.scss: -------------------------------------------------------------------------------- 1 | $sprite: ( 2 | email-blue: ( 3 | offset-x: -64px, 4 | offset-y: 0px, 5 | width: 24px, 6 | height: 24px, 7 | total-width: 112px, 8 | total-height: 92px, 9 | image: 'sign-icons.png' 10 | ), 11 | email-gray: ( 12 | offset-x: -64px, 13 | offset-y: -24px, 14 | width: 24px, 15 | height: 24px, 16 | total-width: 112px, 17 | total-height: 92px, 18 | image: 'sign-icons.png' 19 | ), 20 | lock-blue: ( 21 | offset-x: -88px, 22 | offset-y: 0px, 23 | width: 24px, 24 | height: 24px, 25 | total-width: 112px, 26 | total-height: 92px, 27 | image: 'sign-icons.png' 28 | ), 29 | lock-gray: ( 30 | offset-x: -88px, 31 | offset-y: -24px, 32 | width: 24px, 33 | height: 24px, 34 | total-width: 112px, 35 | total-height: 92px, 36 | image: 'sign-icons.png' 37 | ), 38 | logo: ( 39 | offset-x: 0px, 40 | offset-y: 0px, 41 | width: 64px, 42 | height: 68px, 43 | total-width: 112px, 44 | total-height: 92px, 45 | image: 'sign-icons.png' 46 | ), 47 | name-blue: ( 48 | offset-x: 0px, 49 | offset-y: -68px, 50 | width: 24px, 51 | height: 24px, 52 | total-width: 112px, 53 | total-height: 92px, 54 | image: 'sign-icons.png' 55 | ), 56 | name-gray: ( 57 | offset-x: -24px, 58 | offset-y: -68px, 59 | width: 24px, 60 | height: 24px, 61 | total-width: 112px, 62 | total-height: 92px, 63 | image: 'sign-icons.png' 64 | ), 65 | parts: ( 66 | total-width: 112px, 67 | total-height: 92px, 68 | image: 'sign-icons.png' 69 | ) 70 | ); 71 | 72 | 73 | @mixin sprite-size($image) { 74 | background-size: map-get(map-get($sprite, $image), 'total-width') map-get(map-get($sprite, $image), 'total-height'); 75 | } 76 | 77 | @mixin sprite-width($image) { 78 | width: map-get(map-get($sprite, $image), 'width'); 79 | } 80 | 81 | @mixin sprite-height($image) { 82 | height: map-get(map-get($sprite, $image), 'height'); 83 | } 84 | 85 | @mixin sprite-position($image) { 86 | background-position: map-get(map-get($sprite, $image), 'offset-x') map-get(map-get($sprite, $image), 'offset-y'); 87 | } 88 | 89 | @mixin sprite-image($image) { 90 | background-image: url(map-get(map-get($sprite, $image), 'image') + '.png'); 91 | } 92 | 93 | @mixin sprite-image-retina($image) { 94 | background-image: url(map-get(map-get($sprite, $image), 'image') + '@2.png'); 95 | } 96 | 97 | 98 | 99 | 100 | .icon-email-blue { 101 | @include sprite-width("email-blue"); 102 | @include sprite-height("email-blue"); 103 | @include sprite-position("email-blue"); 104 | } 105 | .icon-email-gray { 106 | @include sprite-width("email-gray"); 107 | @include sprite-height("email-gray"); 108 | @include sprite-position("email-gray"); 109 | } 110 | .icon-lock-blue { 111 | @include sprite-width("lock-blue"); 112 | @include sprite-height("lock-blue"); 113 | @include sprite-position("lock-blue"); 114 | } 115 | .icon-lock-gray { 116 | @include sprite-width("lock-gray"); 117 | @include sprite-height("lock-gray"); 118 | @include sprite-position("lock-gray"); 119 | } 120 | .icon-logo { 121 | @include sprite-width("logo"); 122 | @include sprite-height("logo"); 123 | @include sprite-position("logo"); 124 | } 125 | .icon-name-blue { 126 | @include sprite-width("name-blue"); 127 | @include sprite-height("name-blue"); 128 | @include sprite-position("name-blue"); 129 | } 130 | .icon-name-gray { 131 | @include sprite-width("name-gray"); 132 | @include sprite-height("name-gray"); 133 | @include sprite-position("name-gray"); 134 | } 135 | 136 | 137 | .icon_sign { 138 | @include sprite-size('parts'); 139 | background-repeat: no-repeat; 140 | display: inline-block; 141 | @media 142 | only screen and (-webkit-min-device-pixel-ratio: 2), 143 | only screen and ( min--moz-device-pixel-ratio: 2), 144 | only screen and ( -o-min-device-pixel-ratio: 2/1), 145 | only screen and ( min-device-pixel-ratio: 2), 146 | only screen and ( min-resolution: 192dpi), 147 | only screen and ( min-resolution: 2dppx) { 148 | 149 | @include sprite-size('parts'); 150 | 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/sign/src/styles/sign-icons@2.scss: -------------------------------------------------------------------------------- 1 | $sprite: ( 2 | email-blue: ( 3 | offset-x: -128px, 4 | offset-y: 0px, 5 | width: 48px, 6 | height: 48px, 7 | total-width: 224px, 8 | total-height: 184px, 9 | image: 'sign-icons@2.png' 10 | ), 11 | email-gray: ( 12 | offset-x: -128px, 13 | offset-y: -48px, 14 | width: 48px, 15 | height: 48px, 16 | total-width: 224px, 17 | total-height: 184px, 18 | image: 'sign-icons@2.png' 19 | ), 20 | lock-blue: ( 21 | offset-x: -176px, 22 | offset-y: 0px, 23 | width: 48px, 24 | height: 48px, 25 | total-width: 224px, 26 | total-height: 184px, 27 | image: 'sign-icons@2.png' 28 | ), 29 | lock-gray: ( 30 | offset-x: -176px, 31 | offset-y: -48px, 32 | width: 48px, 33 | height: 48px, 34 | total-width: 224px, 35 | total-height: 184px, 36 | image: 'sign-icons@2.png' 37 | ), 38 | logo: ( 39 | offset-x: 0px, 40 | offset-y: 0px, 41 | width: 128px, 42 | height: 136px, 43 | total-width: 224px, 44 | total-height: 184px, 45 | image: 'sign-icons@2.png' 46 | ), 47 | name-blue: ( 48 | offset-x: 0px, 49 | offset-y: -136px, 50 | width: 48px, 51 | height: 48px, 52 | total-width: 224px, 53 | total-height: 184px, 54 | image: 'sign-icons@2.png' 55 | ), 56 | name-gray: ( 57 | offset-x: -48px, 58 | offset-y: -136px, 59 | width: 48px, 60 | height: 48px, 61 | total-width: 224px, 62 | total-height: 184px, 63 | image: 'sign-icons@2.png' 64 | ), 65 | parts: ( 66 | total-width: 224px, 67 | total-height: 184px, 68 | image: 'sign-icons@2.png' 69 | ) 70 | ); 71 | 72 | 73 | @mixin sprite-size($image) { 74 | background-size: map-get(map-get($sprite, $image), 'total-width') map-get(map-get($sprite, $image), 'total-height'); 75 | } 76 | 77 | @mixin sprite-width($image) { 78 | width: map-get(map-get($sprite, $image), 'width'); 79 | } 80 | 81 | @mixin sprite-height($image) { 82 | height: map-get(map-get($sprite, $image), 'height'); 83 | } 84 | 85 | @mixin sprite-position($image) { 86 | background-position: map-get(map-get($sprite, $image), 'offset-x') map-get(map-get($sprite, $image), 'offset-y'); 87 | } 88 | 89 | @mixin sprite-image($image) { 90 | background-image: url(map-get(map-get($sprite, $image), 'image') + '.png'); 91 | } 92 | 93 | @mixin sprite-image-retina($image) { 94 | background-image: url(map-get(map-get($sprite, $image), 'image') + '@2.png'); 95 | } 96 | 97 | 98 | 99 | 100 | .icon-email-blue { 101 | @include sprite-width("email-blue"); 102 | @include sprite-height("email-blue"); 103 | @include sprite-position("email-blue"); 104 | } 105 | .icon-email-gray { 106 | @include sprite-width("email-gray"); 107 | @include sprite-height("email-gray"); 108 | @include sprite-position("email-gray"); 109 | } 110 | .icon-lock-blue { 111 | @include sprite-width("lock-blue"); 112 | @include sprite-height("lock-blue"); 113 | @include sprite-position("lock-blue"); 114 | } 115 | .icon-lock-gray { 116 | @include sprite-width("lock-gray"); 117 | @include sprite-height("lock-gray"); 118 | @include sprite-position("lock-gray"); 119 | } 120 | .icon-logo { 121 | @include sprite-width("logo"); 122 | @include sprite-height("logo"); 123 | @include sprite-position("logo"); 124 | } 125 | .icon-name-blue { 126 | @include sprite-width("name-blue"); 127 | @include sprite-height("name-blue"); 128 | @include sprite-position("name-blue"); 129 | } 130 | .icon-name-gray { 131 | @include sprite-width("name-gray"); 132 | @include sprite-height("name-gray"); 133 | @include sprite-position("name-gray"); 134 | } 135 | 136 | 137 | .icon_sign { 138 | @include sprite-size('parts'); 139 | background-repeat: no-repeat; 140 | display: inline-block; 141 | @media 142 | only screen and (-webkit-min-device-pixel-ratio: 2), 143 | only screen and ( min--moz-device-pixel-ratio: 2), 144 | only screen and ( -o-min-device-pixel-ratio: 2/1), 145 | only screen and ( min-device-pixel-ratio: 2), 146 | only screen and ( min-resolution: 192dpi), 147 | only screen and ( min-resolution: 2dppx) { 148 | 149 | @include sprite-size('parts'); 150 | 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/signin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sign In 6 | 7 | 8 | 9 | 24 | 25 | 26 | 27 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/sign/src/styles/_global.scss: -------------------------------------------------------------------------------- 1 | /* 定义一些全局的 class */ 2 | 3 | 4 | /* 大小规则 .xx-tny, .xx-sml, .xx-med, .xx-lrg 5 | * 6 | * button: .btn-tny 26, .btn-sml 30, .btn-med 36, .btn-lrg 7 | * text: .text-tny 12, .text-sml 13, .text-med 16, .text-lrg 22 8 | * icon tny 14 sml 18 med 32 lrg 40 9 | * line: .line-sml 8 10 | */ 11 | 12 | // font-size class 13 | .text-tny { 14 | font-size: $base-font-size-tny; 15 | } 16 | 17 | .text-sml { 18 | font-size: $base-font-size-sml; 19 | } 20 | 21 | .text-def { 22 | font-size: $base-font-size-def; 23 | } 24 | 25 | .text-med { 26 | font-size: $base-font-size-med; 27 | } 28 | 29 | .text-lrg { 30 | font-size: $base-font-size-lrg; 31 | } 32 | 33 | 34 | .left { float: left !important; } 35 | .right { float: right !important; } 36 | .clearfix { @include clearfix; } 37 | 38 | .hide { 39 | display: none !important; 40 | } 41 | 42 | .hidden { 43 | display: none; 44 | } 45 | 46 | .invisible { 47 | visibility: hidden; 48 | } 49 | 50 | // Font smoothing 51 | // Antialiased font smoothing works best for light text on a dark background. 52 | // Apply to single elements instead of globally to body. 53 | // Note this only applies to webkit-based desktop browsers and Firefox 25 (and later) on the Mac. 54 | .antialiased { 55 | -webkit-font-smoothing: antialiased; 56 | -moz-osx-font-smoothing: grayscale; 57 | } 58 | 59 | .divider { 60 | height: 1px; 61 | width: 100%; 62 | } 63 | 64 | .l-divider { 65 | height: 1px; 66 | margin: 10px 0; 67 | width: 100%; 68 | } 69 | 70 | // icon size tny 14 sml 18 med 24 71 | .i-tny { 72 | width: 14px; 73 | height: 14px; 74 | } 75 | .i-sml { 76 | width: 16px; 77 | height: 16px; 78 | } 79 | .i-med { 80 | width: 32px; 81 | height: 32px; 82 | } 83 | 84 | .i-lrg { 85 | width: 40px; 86 | height: 40px; 87 | } 88 | 89 | .i-lrg-x { 90 | width: 48px; 91 | height: 48px; 92 | } 93 | 94 | .i-xl { 95 | width: 100px; 96 | height: 100px; 97 | } 98 | 99 | .i-xxl { 100 | width: 180px; 101 | height: 180px; 102 | } 103 | 104 | .i-xxxl { 105 | width: 192px; 106 | height: 192px; 107 | } 108 | 109 | .i-redeem { 110 | width: 156px; 111 | height: 96px; 112 | } 113 | 114 | // adaptive line 115 | .line-right, 116 | .line-left { 117 | position: relative; 118 | } 119 | .line-right { 120 | z-index: 1; 121 | float: right; 122 | } 123 | 124 | .b-h { 125 | height: 100%; 126 | } 127 | 128 | 129 | 130 | .drag { 131 | visibility: hidden; 132 | position: absolute; 133 | cursor: move; 134 | } 135 | 136 | .shadow { 137 | border: $base-border; 138 | box-shadow: $base-box-shadow; 139 | border-radius: $base-border-radius; 140 | } 141 | 142 | .pop-shadow { 143 | border: $base-border; 144 | box-shadow: $base-pop-shadow; 145 | border-radius: $base-border-radius; 146 | } 147 | 148 | .dropdown-toggle { 149 | cursor: pointer; 150 | } 151 | 152 | img.round { 153 | border-radius: 50%; 154 | overflow: hidden; 155 | } 156 | 157 | .btns { 158 | line-height: 1; 159 | } 160 | 161 | .nowrap { 162 | white-space: nowrap; 163 | } 164 | 165 | body > svg { 166 | display: none; 167 | } 168 | 169 | /* text align */ 170 | .text-center { 171 | text-align: center; 172 | } 173 | 174 | .text-left { 175 | text-align: left; 176 | } 177 | 178 | .text-right { 179 | text-align: right; 180 | } 181 | /* text align end */ 182 | 183 | // sort 184 | .drag-help { 185 | width: rem-calc(360); 186 | max-width: rem-calc(360); 187 | opacity: 0.9; 188 | } 189 | 190 | // 行高 设置 191 | .line-sml { 192 | line-height: rem-calc(21); 193 | } 194 | 195 | .file-wrap { 196 | overflow: hidden; 197 | } 198 | 199 | // 最好用 select-disabled 200 | .select-disabled *, 201 | .can-not-select * { 202 | @include user-select(none); 203 | } 204 | 205 | .select-enabled, 206 | .select-enabled *, 207 | .can-select, 208 | .can-select * { 209 | @include user-select(text); 210 | } 211 | 212 | .select-disabled, 213 | .can-not-select { 214 | input, 215 | textarea, 216 | *[contenteditable] { 217 | @include user-select(text); 218 | } 219 | } 220 | 221 | .tl-body { 222 | .antiscroll-wrap { 223 | position: static; // 为了拖出去 224 | } 225 | } 226 | 227 | .loading-view { 228 | width: 100%; 229 | height: 100%; 230 | background-color: $white; 231 | position: absolute; 232 | top: 0; 233 | z-index: $zindex-loadview; 234 | text-align: center; 235 | 236 | .logo { 237 | position: relative; 238 | height: 100%; 239 | top: 50%; 240 | margin-top: rem-calc(-100); 241 | } 242 | } 243 | 244 | .avoid-event { 245 | svg { 246 | pointer-events: none; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /app/sign/src/styles/_sign.scss: -------------------------------------------------------------------------------- 1 | /*sign style*/ 2 | 3 | body { 4 | line-height: 1; 5 | font-family: $font-family; 6 | background: $whitegray; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | .sign { 12 | max-width: 400px; 13 | min-width: 300px; 14 | margin: 48px auto; 15 | color: $base-font-color; 16 | } 17 | 18 | .card { 19 | position: relative; 20 | margin: 0 auto; 21 | } 22 | 23 | .logo { 24 | text-align: center; 25 | margin-bottom: $s-base-divider; 26 | } 27 | 28 | .card-section { 29 | background-color: $white; 30 | border: $base-border; 31 | border-radius: 2px; 32 | padding: 40px 50px; 33 | } 34 | 35 | .form { 36 | position: relative; 37 | } 38 | 39 | .sign { 40 | .row { 41 | position: relative; 42 | border-bottom: 1px solid $base-border-color; 43 | border-radius: 1px; 44 | transition: border-color ease-in-out .15s; 45 | margin-bottom: 28px; 46 | 47 | &.active { 48 | // border-bottom: 1px solid $base-focus-border-color; 49 | transition: border-color ease-in-out .15s; 50 | } 51 | 52 | &.name input, 53 | &.email input, 54 | &.password input { 55 | border: 0 none; 56 | font-size: 16px; 57 | padding: 10px 10px 8px 8px; 58 | width: calc(100% - 70px); 59 | } 60 | 61 | &.submit, 62 | &.forgot-password { 63 | border-bottom: none; 64 | } 65 | 66 | &.submit { 67 | margin: 0 auto; 68 | margin-bottom: 12px; 69 | } 70 | 71 | &.forgot-password { 72 | margin: 0; 73 | text-align: left; 74 | font-size: 13px; 75 | } 76 | 77 | } 78 | 79 | } 80 | 81 | .input-bar { 82 | position: relative; 83 | display: block; 84 | width: 100%; 85 | height: 1px; 86 | 87 | &:before, 88 | &:after { 89 | content: ''; 90 | height: 1px; 91 | width: 0; 92 | bottom: -1px; 93 | position: absolute; 94 | background-color: $base-focus-border-color; 95 | border-radius: 1px; 96 | transition: all .28s cubic-bezier(.4,0,.2,1); 97 | } 98 | 99 | &:before { 100 | left: 50%; 101 | border-top-right-radius: 0; 102 | border-bottom-right-radius: 0; 103 | } 104 | 105 | &:after { 106 | right: 50%; 107 | border-top-left-radius: 0; 108 | border-bottom-left-radius: 0; 109 | } 110 | } 111 | 112 | 113 | 114 | input:focus ~ .input-bar:before { 115 | left: 0%; 116 | width: 51%; 117 | } 118 | 119 | input:focus ~ .input-bar:after { 120 | right: 0%; 121 | width: 50%; 122 | } 123 | 124 | /*插件*/ 125 | .switch-site { 126 | display: none; 127 | } 128 | .chrome-extension .forgot-password .link { 129 | float: right; 130 | } 131 | 132 | .chrome-extension .forgot-password .switch-site { 133 | display: inline; 134 | float: left; 135 | } 136 | 137 | .sign .row .icon { 138 | position: relative; 139 | top: 6px; 140 | } 141 | 142 | .middle-line { 143 | overflow: hidden; 144 | text-align: center; 145 | } 146 | 147 | .middle-line:before, 148 | .middle-line:after { 149 | background-color: $base-border-color; 150 | content: ""; 151 | display: inline-block; 152 | height: 1px; 153 | position: relative; 154 | vertical-align: middle; 155 | width: 50%; 156 | } 157 | .middle-line:before { 158 | right: 0.5em; 159 | margin-left: -50%; 160 | } 161 | .middle-line:after { 162 | left: 0.5em; 163 | margin-right: -50%; 164 | } 165 | 166 | .sns-title { 167 | color: $lavendergray; 168 | font-size: 13px; 169 | padding: 30px 0 36px; 170 | } 171 | 172 | .signin-sns ul { 173 | text-align: center; 174 | 175 | li { 176 | display: inline-block; 177 | 178 | &.second { 179 | margin: 0 42px; 180 | } 181 | 182 | &:hover { 183 | opacity: .9; 184 | } 185 | } 186 | } 187 | 188 | 189 | .link.signup, 190 | .link.signin, 191 | .link.sign-link { 192 | text-align: center; 193 | padding-top: 35px; 194 | font-size: 14px; 195 | } 196 | 197 | .link { 198 | &.message { 199 | line-height: 25px; 200 | padding: 40px 0 0; 201 | } 202 | 203 | &.success { 204 | padding: 40px 0; 205 | } 206 | } 207 | 208 | .button { 209 | font-size: 16px; 210 | width: 100%; 211 | } 212 | 213 | .error { 214 | position: absolute; 215 | right: 0; 216 | z-index: 2; 217 | 218 | .warn { 219 | display: none; 220 | color: $base-font-color-alert; 221 | font-size: 13px; 222 | margin-top: 6px; 223 | line-height: 1.1; 224 | } 225 | 226 | &.long { 227 | position: relative; 228 | } 229 | } 230 | 231 | @media #{$small-only} { 232 | .sign { 233 | width: 95%; 234 | 235 | .signin-sns ul li:nth-child(2) { 236 | margin: 0 10px; 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /app/sign/src/scripts/sign.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let ipc = require('electron').ipcRenderer 4 | let Appest = require(__dirname + '/appest') 5 | 6 | let homeUrl = Appest.protocol + Appest.domain 7 | let emailRegexp = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,9}$/i 8 | 9 | let View = { 10 | password: '', 11 | email: '', 12 | isPasswordOK: false, 13 | isEmailValid: false, 14 | 15 | initDom () { 16 | this.$empty = $('#login_length_warn_empty') 17 | this.$warn = $('#login_length_warn') 18 | this.$ewarn = $('#email_invalid_warn') 19 | this.$blank = $('#not_blank') 20 | this.$ps = $('#password') 21 | this.$eps = $('#error_username_password') 22 | this.$spt = $('.submit input') 23 | this.$submit = $('.submit') 24 | this.$name = $('#username') 25 | }, 26 | 27 | init () { 28 | let _this = this 29 | this.initDom() 30 | 31 | this.$name.change(() => { 32 | _this.checkEmailFormat() 33 | }).keyup(() => { 34 | _this.checkingEmailFormat() 35 | }) 36 | 37 | this.$ps.keyup(() => { 38 | _this.checkingPassword1() 39 | }) 40 | 41 | $('#submit-btn').click(() => { 42 | event.preventDefault() 43 | _this.submit() 44 | }) 45 | 46 | $('input').keydown(() => { 47 | if (event.keyCode === 13) { 48 | _this.submit() 49 | } 50 | }) 51 | 52 | $('.required').focus(() => { 53 | let 54 | parent = $(event.target).parent(), 55 | element = parent.find('i').first(), 56 | iconClass = 'icon-' + element.attr('icon') + '-gray', 57 | activeIcon = 'icon-' + element.attr('icon') + '-blue' 58 | 59 | parent.addClass('active') 60 | element.removeClass(iconClass) 61 | element.addClass(activeIcon) 62 | }).blur(() => { 63 | let 64 | parent = $(event.target).parent(), 65 | element = parent.find('i').first(), 66 | iconClass = 'icon-' + element.attr('icon') + '-gray', 67 | activeIcon = 'icon-' + element.attr('icon') + '-blue' 68 | 69 | parent.removeClass('active') 70 | element.addClass(iconClass) 71 | element.removeClass(activeIcon) 72 | }) 73 | }, 74 | 75 | submit () { 76 | this.checkEmailFormat() 77 | this.checkPassword1() 78 | this.postLogin() 79 | }, 80 | 81 | checkEmailFormat () { 82 | this.isEmailValid = false 83 | this.email = this.$name.val() 84 | $('#error_no_user_info').fadeOut(200) 85 | let isMatch = this.email.match(emailRegexp) 86 | if (isMatch) { 87 | this.$ewarn.fadeOut(200) 88 | this.$blank.fadeOut(200) 89 | this.isEmailValid = true 90 | } else { 91 | if (this.email.length > 0) { 92 | this.$blank.fadeOut(200) 93 | this.$ewarn.fadeIn(200) 94 | } else { 95 | this.$blank.fadeIn(200) 96 | } 97 | } 98 | }, 99 | 100 | checkingEmailFormat () { 101 | this.email = this.$name.val() 102 | let isMatch = this.email.match(emailRegexp) 103 | this.$ewarn.fadeOut(200) 104 | if (this.email.length > 0) { 105 | this.$blank.fadeOut(200) 106 | } 107 | return isMatch 108 | }, 109 | 110 | checkPassword1 () { 111 | this.password = this.$ps.val() 112 | this.isPasswordOK = false 113 | 114 | let isMatch = this.password.length >= 6 115 | 116 | if (isMatch) { 117 | this.$empty.fadeOut(200) 118 | this.$warn.fadeOut(200) 119 | this.isPasswordOK = true 120 | } else { 121 | if (this.password.length > 0) { 122 | this.$empty.fadeOut(200) 123 | this.$warn.fadeIn(200) 124 | } else { 125 | this.$empty.fadeIn(200) 126 | } 127 | } 128 | }, 129 | 130 | checkingPassword1 () { 131 | this.$eps.fadeOut(200) 132 | this.password = this.$ps.val() 133 | if ((this.password.length >= 6)) { 134 | this.$warn.fadeOut(200) 135 | } else if (this.password.length > 0) { 136 | this.$empty.fadeOut(200) 137 | } 138 | }, 139 | 140 | postLogin () { 141 | if (!this.isPasswordOK || !this.isEmailValid) 142 | return 143 | 144 | this.$spt.attr('disabled') 145 | this.$submit.addClass('waiting ing') 146 | 147 | let url = homeUrl + '/api/v2/user/signon?wc=true&remember=true' 148 | 149 | let _this = this 150 | $.ajax({ 151 | type: 'POST', 152 | url: url, 153 | contentType: 'application/json', 154 | data: JSON.stringify({ 155 | username: this.email, 156 | password: this.password 157 | }), 158 | success (data) { 159 | ipc.send('signin', data) 160 | }, 161 | error (err) { 162 | _this.loginError(err) 163 | } 164 | }) 165 | }, 166 | 167 | loginError (err) { 168 | this.$spt.removeAttr('disabled') 169 | this.$submit.removeClass('waiting ing') 170 | let obj = $.parseJSON(err.responseText) 171 | if (obj && obj.errorCode === 'username_password_not_match') { 172 | this.$eps.fadeIn(200) 173 | } else { 174 | $('#login_server_error').fadeIn(200) 175 | } 176 | } 177 | } 178 | 179 | View.init() 180 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/nconf-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file-store-test.js: Tests for the nconf File store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | path = require('path'), 10 | vows = require('vows'), 11 | assert = require('assert'), 12 | nconf = require('../lib/nconf'), 13 | data = require('./fixtures/data').data; 14 | 15 | vows.describe('nconf').addBatch({ 16 | "When using the nconf": { 17 | "should have the correct methods set": function () { 18 | assert.isFunction(nconf.key); 19 | assert.isFunction(nconf.path); 20 | assert.isFunction(nconf.use); 21 | assert.isFunction(nconf.get); 22 | assert.isFunction(nconf.set); 23 | assert.isFunction(nconf.clear); 24 | assert.isFunction(nconf.load); 25 | assert.isFunction(nconf.save); 26 | assert.isFunction(nconf.reset); 27 | assert.isFunction(nconf.required); 28 | }, 29 | "the use() method": { 30 | "should instaniate the correct store": function () { 31 | nconf.use('memory'); 32 | nconf.load(); 33 | assert.instanceOf(nconf.stores['memory'], nconf.Memory); 34 | } 35 | }, 36 | "it should": { 37 | topic: function () { 38 | fs.readFile(path.join(__dirname, '..', 'package.json'), this.callback); 39 | }, 40 | "have the correct version set": function (err, data) { 41 | assert.isNull(err); 42 | data = JSON.parse(data.toString()); 43 | assert.equal(nconf.version, data.version); 44 | } 45 | }, 46 | "the required() method": { 47 | "should throw error with missing keys": function() { 48 | nconf.set('foo:bar:bazz', 'buzz'); 49 | assert.throws(nconf.required.bind(nconf, ['missing', 'foo:bar:bazz']), Error); 50 | }, 51 | "should return true if all required keys exist": function() { 52 | nconf.set('foo:bar:bazz', 'buzz'); 53 | assert.isTrue(nconf.required(['foo:bar:bazz'])); 54 | } 55 | } 56 | } 57 | }).addBatch({ 58 | "When using the nconf": { 59 | "with the memory store": { 60 | "the set() method": { 61 | "should respond with true": function () { 62 | assert.isTrue(nconf.set('foo:bar:bazz', 'buzz')); 63 | } 64 | }, 65 | "the get() method": { 66 | "without a callback": { 67 | "should respond with the correct value": function () { 68 | assert.equal(nconf.get('foo:bar:bazz'), 'buzz'); 69 | } 70 | }, 71 | "with a callback": { 72 | topic: function () { 73 | nconf.get('foo:bar:bazz', this.callback); 74 | }, 75 | "should respond with the correct value": function (err, value) { 76 | assert.equal(value, 'buzz'); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | }).addBatch({ 83 | "When using the nconf": { 84 | "with the memory store": { 85 | "the get() method": { 86 | "should respond allow access to the root": function () { 87 | assert(nconf.get(null)); 88 | assert(nconf.get(undefined)); 89 | assert(nconf.get()); 90 | } 91 | }, 92 | "the set() method": { 93 | "should respond allow access to the root and complain about non-objects": function () { 94 | assert(!nconf.set(null, null)); 95 | assert(!nconf.set(null, undefined)); 96 | assert(!nconf.set(null)); 97 | assert(!nconf.set(null, '')); 98 | assert(!nconf.set(null, 1)); 99 | var original = nconf.get(); 100 | assert(nconf.set(null, nconf.get())); 101 | assert.notEqual(nconf.get(), original); 102 | assert.deepEqual(nconf.get(), original) 103 | } 104 | } 105 | } 106 | } 107 | }).addBatch({ 108 | "When using nconf": { 109 | "with the memory store": { 110 | "the clear() method": { 111 | "should respond with the true": function () { 112 | assert.equal(nconf.get('foo:bar:bazz'), 'buzz'); 113 | assert.isTrue(nconf.clear('foo:bar:bazz')); 114 | assert.isTrue(typeof nconf.get('foo:bar:bazz') === 'undefined'); 115 | } 116 | }, 117 | "the load() method": { 118 | "without a callback": { 119 | "should respond with the merged store": function () { 120 | assert.deepEqual(nconf.load(), { 121 | title: 'My specific title', 122 | color: 'green', 123 | movie: 'Kill Bill' 124 | }); 125 | } 126 | }, 127 | "with a callback": { 128 | topic: function () { 129 | nconf.load(this.callback.bind(null, null)); 130 | }, 131 | "should respond with the merged store": function (ign, err, store) { 132 | assert.isNull(err); 133 | assert.deepEqual(store, { 134 | title: 'My specific title', 135 | color: 'green', 136 | movie: 'Kill Bill' 137 | }); 138 | } 139 | } 140 | } 141 | } 142 | } 143 | }).export(module); 144 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/stores/memory-store-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * memory-store-test.js: Tests for the nconf Memory store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var vows = require('vows'), 9 | assert = require('assert'), 10 | nconf = require('../../lib/nconf'), 11 | merge = require('../fixtures/data').merge; 12 | 13 | vows.describe('nconf/stores/memory').addBatch({ 14 | "When using the nconf memory store": { 15 | topic: new nconf.Memory(), 16 | "the set() method": { 17 | "should respond with true": function (store) { 18 | assert.isTrue(store.set('foo:bar:bazz', 'buzz')); 19 | assert.isTrue(store.set('falsy:number', 0)); 20 | assert.isTrue(store.set('falsy:string:empty', '')); 21 | assert.isTrue(store.set('falsy:string:value', 'value')); 22 | assert.isTrue(store.set('falsy:boolean', false)); 23 | assert.isTrue(store.set('falsy:object', null)); 24 | } 25 | }, 26 | "the get() method": { 27 | "should respond with the correct value": function (store) { 28 | assert.equal(store.get('foo:bar:bazz'), 'buzz'); 29 | assert.equal(store.get('falsy:number'), 0); 30 | assert.equal(store.get('falsy:string:empty'), ''); 31 | assert.equal(store.get('falsy:string:value'), 'value'); 32 | assert.equal(store.get('falsy:boolean'), false); 33 | assert.equal(store.get('falsy:object'), null); 34 | }, 35 | "should not fail when retrieving non-existent keys": { 36 | "at the root level": function (store) { 37 | assert.doesNotThrow(function() { 38 | assert.equal(store.get('this:key:does:not:exist'), undefined); 39 | }, TypeError); 40 | }, 41 | "within numbers": function (store) { 42 | assert.doesNotThrow(function() { 43 | assert.equal(store.get('falsy:number:not:exist'), undefined); 44 | }, TypeError); 45 | }, 46 | "within booleans": function (store) { 47 | assert.doesNotThrow(function() { 48 | assert.equal(store.get('falsy:boolean:not:exist'), undefined); 49 | }, TypeError); 50 | }, 51 | "within objects": function (store) { 52 | assert.doesNotThrow(function() { 53 | assert.equal(store.get('falsy:object:not:exist'), undefined); 54 | }, TypeError); 55 | }, 56 | "within empty strings": function (store) { 57 | assert.doesNotThrow(function() { 58 | assert.equal(store.get('falsy:string:empty:not:exist'), undefined); 59 | }, TypeError); 60 | }, 61 | "within non-empty strings": function (store) { 62 | assert.doesNotThrow(function() { 63 | assert.equal(store.get('falsy:string:value:not:exist'), undefined); 64 | }, TypeError); 65 | } 66 | } 67 | }, 68 | "the clear() method": { 69 | "should respond with the true": function (store) { 70 | assert.equal(store.get('foo:bar:bazz'), 'buzz'); 71 | assert.isTrue(store.clear('foo:bar:bazz')); 72 | assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined'); 73 | } 74 | }, 75 | "the merge() method": { 76 | "when overriding an existing literal value": function (store) { 77 | store.set('merge:literal', 'string-value'); 78 | store.merge('merge:literal', merge); 79 | assert.deepEqual(store.get('merge:literal'), merge); 80 | }, 81 | "when overriding an existing Array value": function (store) { 82 | store.set('merge:array', [1,2,3,4]); 83 | store.merge('merge:array', merge); 84 | assert.deepEqual(store.get('merge:literal'), merge); 85 | }, 86 | "when merging into an existing Object value": function (store) { 87 | store.set('merge:object', { 88 | prop1: 2, 89 | prop2: 'prop2', 90 | prop3: { 91 | bazz: 'bazz' 92 | }, 93 | prop4: ['foo', 'bar'] 94 | }); 95 | store.merge('merge:object', merge); 96 | 97 | assert.equal(store.get('merge:object:prop1'), 1); 98 | assert.equal(store.get('merge:object:prop2').length, 3); 99 | assert.deepEqual(store.get('merge:object:prop3'), { 100 | foo: 'bar', 101 | bar: 'foo', 102 | bazz: 'bazz' 103 | }); 104 | assert.equal(store.get('merge:object:prop4').length, 2); 105 | } 106 | } 107 | }, 108 | "When using the nconf memory store with different logical separator": { 109 | topic: new nconf.Memory({logicalSeparator: '||' }), 110 | "when storing with : (colon)": { 111 | "should store the config atomicly": function (store) { 112 | store.set('foo:bar:bazz', 'buzz'); 113 | assert.isTrue(typeof store.get('foo:bar') === 'undefined'); 114 | assert.equal(store.get('foo:bar:bazz'), 'buzz'); 115 | } 116 | }, 117 | "when storing with separator": { 118 | "should be able to read the object": function (store) { 119 | store.set('foo||bar||bazz', 'buzz'); 120 | assert.equal(store.get('foo||bar').bazz, 'buzz'); 121 | assert.equal(store.get('foo').bar.bazz, 'buzz'); 122 | } 123 | } 124 | } 125 | }).export(module); 126 | -------------------------------------------------------------------------------- /app/node_modules/nconf/test/complete-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * complete-test.js: Complete test for multiple stores. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | path = require('path'), 10 | vows = require('vows'), 11 | assert = require('assert'), 12 | nconf = require('../lib/nconf'), 13 | data = require('./fixtures/data').data, 14 | helpers = require('./helpers'); 15 | 16 | var completeTest = helpers.fixture('complete-test.json'), 17 | complete = helpers.fixture('complete.json'); 18 | 19 | // prime the process.env 20 | process.env['NCONF_foo'] = 'bar'; 21 | process.env.FOO = 'bar'; 22 | process.env.BAR = 'zalgo'; 23 | process.env.NODE_ENV = 'debug'; 24 | process.env.FOOBAR = 'should not load'; 25 | 26 | vows.describe('nconf/multiple-stores').addBatch({ 27 | "When using the nconf with multiple providers": { 28 | topic: function () { 29 | var that = this; 30 | helpers.cp(complete, completeTest, function () { 31 | nconf.env({ 32 | // separator: '__', 33 | match: /^NCONF_/, 34 | whitelist: ['NODE_ENV', 'FOO', 'BAR'] 35 | }); 36 | nconf.file({ file: completeTest }); 37 | nconf.use('argv', { type: 'literal', store: data }); 38 | that.callback(); 39 | }); 40 | }, 41 | "should have the correct `stores`": function () { 42 | assert.isObject(nconf.stores.env); 43 | assert.isObject(nconf.stores.argv); 44 | assert.isObject(nconf.stores.file); 45 | }, 46 | "env vars": { 47 | "are present": function () { 48 | ['NODE_ENV', 'FOO', 'BAR', 'NCONF_foo'].forEach(function (key) { 49 | assert.equal(nconf.get(key), process.env[key]); 50 | }); 51 | } 52 | }, 53 | "json vars": { 54 | topic: function () { 55 | fs.readFile(complete, 'utf8', this.callback); 56 | }, 57 | "are present": function (err, data) { 58 | assert.isNull(err); 59 | data = JSON.parse(data); 60 | Object.keys(data).forEach(function (key) { 61 | assert.deepEqual(nconf.get(key), data[key]); 62 | }); 63 | } 64 | }, 65 | "literal vars": { 66 | "are present": function () { 67 | Object.keys(data).forEach(function (key) { 68 | assert.deepEqual(nconf.get(key), data[key]); 69 | }); 70 | } 71 | }, 72 | "and saving *synchronously*": { 73 | topic: function () { 74 | nconf.set('weebls', 'stuff'); 75 | return nconf.save(); 76 | }, 77 | "correct return value": function (topic) { 78 | Object.keys(topic).forEach(function (key) { 79 | assert.deepEqual(topic[key], nconf.get(key)); 80 | }); 81 | }, 82 | "the file": { 83 | topic: function () { 84 | fs.readFile(completeTest, 'utf8', this.callback); 85 | }, 86 | "saved correctly": function (err, data) { 87 | data = JSON.parse(data); 88 | Object.keys(data).forEach(function (key) { 89 | assert.deepEqual(data[key], nconf.get(key)); 90 | }); 91 | assert.equal(nconf.get('weebls'), 'stuff'); 92 | } 93 | } 94 | }, 95 | teardown: function () { 96 | // remove the file so that we can test saving it async 97 | fs.unlinkSync(completeTest); 98 | } 99 | } 100 | }).addBatch({ 101 | // Threw this in it's own batch to make sure it's run separately from the 102 | // sync check 103 | "When using the nconf with multiple providers": { 104 | "and saving *asynchronously*": { 105 | topic: function () { 106 | nconf.set('weebls', 'crap'); 107 | nconf.save(this.callback); 108 | }, 109 | "correct return value": function (err, data) { 110 | assert.isNull(err); 111 | Object.keys(data).forEach(function (key) { 112 | assert.deepEqual(data[key], nconf.get(key)); 113 | }); 114 | }, 115 | "the file": { 116 | topic: function () { 117 | fs.readFile(completeTest, 'utf8', this.callback); 118 | }, 119 | "saved correctly": function (err, data) { 120 | assert.isNull(err); 121 | data = JSON.parse(data); 122 | Object.keys(data).forEach(function (key) { 123 | assert.deepEqual(nconf.get(key), data[key]); 124 | }); 125 | assert.equal(nconf.get('weebls'), 'crap'); 126 | } 127 | } 128 | }, 129 | teardown: function () { 130 | fs.unlinkSync(completeTest); 131 | nconf.remove('file'); 132 | nconf.remove('memory'); 133 | nconf.remove('argv'); 134 | nconf.remove('env'); 135 | } 136 | } 137 | }).addBatch({ 138 | // Threw this in it's own batch to make sure it's run separately from the 139 | // sync check 140 | "When using env with lowerCase:true": { 141 | topic: function () { 142 | var that = this; 143 | helpers.cp(complete, completeTest, function () { 144 | nconf.env({ lowerCase: true }); 145 | that.callback(); 146 | }); 147 | }, 148 | "env vars": { 149 | "keys also available as lower case": function () { 150 | Object.keys(process.env).forEach(function (key) { 151 | assert.equal(nconf.get(key.toLowerCase()), process.env[key]); 152 | }); 153 | } 154 | }, 155 | teardown: function () { 156 | nconf.remove('env'); 157 | } 158 | } 159 | }).export(module); -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/stores/memory.js: -------------------------------------------------------------------------------- 1 | /* 2 | * memory.js: Simple memory storage engine for nconf configuration(s) 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var common = require('../common'); 9 | 10 | // 11 | // ### function Memory (options) 12 | // #### @options {Object} Options for this instance 13 | // Constructor function for the Memory nconf store which maintains 14 | // a nested json structure based on key delimiters `:`. 15 | // 16 | // e.g. `my:nested:key` ==> `{ my: { nested: { key: } } }` 17 | // 18 | var Memory = exports.Memory = function (options) { 19 | options = options || {}; 20 | this.type = 'memory'; 21 | this.store = {}; 22 | this.mtimes = {}; 23 | this.readOnly = false; 24 | this.loadFrom = options.loadFrom || null; 25 | this.logicalSeparator = options.logicalSeparator || ':'; 26 | 27 | if (this.loadFrom) { 28 | this.store = common.loadFilesSync(this.loadFrom); 29 | } 30 | }; 31 | 32 | // 33 | // ### function get (key) 34 | // #### @key {string} Key to retrieve for this instance. 35 | // Retrieves the value for the specified key (if any). 36 | // 37 | Memory.prototype.get = function (key) { 38 | var target = this.store, 39 | path = common.path(key, this.logicalSeparator); 40 | 41 | // 42 | // Scope into the object to get the appropriate nested context 43 | // 44 | while (path.length > 0) { 45 | key = path.shift(); 46 | if (target && target.hasOwnProperty(key)) { 47 | target = target[key]; 48 | continue; 49 | } 50 | return undefined; 51 | } 52 | 53 | return target; 54 | }; 55 | 56 | // 57 | // ### function set (key, value) 58 | // #### @key {string} Key to set in this instance 59 | // #### @value {literal|Object} Value for the specified key 60 | // Sets the `value` for the specified `key` in this instance. 61 | // 62 | Memory.prototype.set = function (key, value) { 63 | if (this.readOnly) { 64 | return false; 65 | } 66 | 67 | var target = this.store, 68 | path = common.path(key, this.logicalSeparator); 69 | 70 | if (path.length === 0) { 71 | // 72 | // Root must be an object 73 | // 74 | if (!value || typeof value !== 'object') { 75 | return false; 76 | } 77 | else { 78 | this.reset(); 79 | this.store = value; 80 | return true; 81 | } 82 | } 83 | 84 | // 85 | // Update the `mtime` (modified time) of the key 86 | // 87 | this.mtimes[key] = Date.now(); 88 | 89 | // 90 | // Scope into the object to get the appropriate nested context 91 | // 92 | while (path.length > 1) { 93 | key = path.shift(); 94 | if (!target[key] || typeof target[key] !== 'object') { 95 | target[key] = {}; 96 | } 97 | 98 | target = target[key]; 99 | } 100 | 101 | // Set the specified value in the nested JSON structure 102 | key = path.shift(); 103 | target[key] = value; 104 | return true; 105 | }; 106 | 107 | // 108 | // ### function clear (key) 109 | // #### @key {string} Key to remove from this instance 110 | // Removes the value for the specified `key` from this instance. 111 | // 112 | Memory.prototype.clear = function (key) { 113 | if (this.readOnly) { 114 | return false; 115 | } 116 | 117 | var target = this.store, 118 | value = target, 119 | path = common.path(key, this.logicalSeparator); 120 | 121 | // 122 | // Remove the key from the set of `mtimes` (modified times) 123 | // 124 | delete this.mtimes[key]; 125 | 126 | // 127 | // Scope into the object to get the appropriate nested context 128 | // 129 | for (var i = 0; i < path.length - 1; i++) { 130 | key = path[i]; 131 | value = target[key]; 132 | if (typeof value !== 'function' && typeof value !== 'object') { 133 | return false; 134 | } 135 | target = value; 136 | } 137 | 138 | // Delete the key from the nested JSON structure 139 | key = path[i]; 140 | delete target[key]; 141 | return true; 142 | }; 143 | 144 | // 145 | // ### function merge (key, value) 146 | // #### @key {string} Key to merge the value into 147 | // #### @value {literal|Object} Value to merge into the key 148 | // Merges the properties in `value` into the existing object value 149 | // at `key`. If the existing value `key` is not an Object, it will be 150 | // completely overwritten. 151 | // 152 | Memory.prototype.merge = function (key, value) { 153 | if (this.readOnly) { 154 | return false; 155 | } 156 | 157 | // 158 | // If the key is not an `Object` or is an `Array`, 159 | // then simply set it. Merging is for Objects. 160 | // 161 | if (typeof value !== 'object' || Array.isArray(value) || value === null) { 162 | return this.set(key, value); 163 | } 164 | 165 | var self = this, 166 | target = this.store, 167 | path = common.path(key, this.logicalSeparator), 168 | fullKey = key; 169 | 170 | // 171 | // Update the `mtime` (modified time) of the key 172 | // 173 | this.mtimes[key] = Date.now(); 174 | 175 | // 176 | // Scope into the object to get the appropriate nested context 177 | // 178 | while (path.length > 1) { 179 | key = path.shift(); 180 | if (!target[key]) { 181 | target[key] = {}; 182 | } 183 | 184 | target = target[key]; 185 | } 186 | 187 | // Set the specified value in the nested JSON structure 188 | key = path.shift(); 189 | 190 | // 191 | // If the current value at the key target is not an `Object`, 192 | // or is an `Array` then simply override it because the new value 193 | // is an Object. 194 | // 195 | if (typeof target[key] !== 'object' || Array.isArray(target[key])) { 196 | target[key] = value; 197 | return true; 198 | } 199 | 200 | return Object.keys(value).every(function (nested) { 201 | return self.merge(common.keyed(self.logicalSeparator, fullKey, nested), value[nested]); 202 | }); 203 | }; 204 | 205 | // 206 | // ### function reset (callback) 207 | // Clears all keys associated with this instance. 208 | // 209 | Memory.prototype.reset = function () { 210 | if (this.readOnly) { 211 | return false; 212 | } 213 | 214 | this.mtimes = {}; 215 | this.store = {}; 216 | return true; 217 | }; 218 | 219 | // 220 | // ### function loadSync 221 | // Returns the store managed by this instance 222 | // 223 | Memory.prototype.loadSync = function () { 224 | return this.store || {}; 225 | }; 226 | -------------------------------------------------------------------------------- /app/node_modules/objectid/test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | chai.should() 3 | var expect = chai.expect 4 | chai.use(require('chai-interface')) 5 | 6 | var ObjectId = require('./index') 7 | var NativeObjectId = require('mongodb').ObjectID 8 | var bson = require('bson').BSONPure 9 | 10 | var testOid = '511083bb08ce6b1b00000003' 11 | 12 | describe('objectid', function () { 13 | it ('has interface', function () { 14 | // constructor 15 | ObjectId.should.be.a('function') 16 | 17 | // static 18 | ObjectId.should.have.interface({ 19 | equals: Function, 20 | tryParse: Function, 21 | isValid: Function 22 | }) 23 | 24 | // instance 25 | ObjectId().should.be.an('object') 26 | 27 | }) 28 | 29 | it('has a bsontype to work with the `bson` module', function () { 30 | var id = ObjectId() 31 | id._bsontype.should.equal('ObjectID'); 32 | }) 33 | 34 | it('serializes the same as a bson ObjectID', function () { 35 | var id1 = ObjectId(testOid) 36 | var id2 = bson.ObjectID(testOid) 37 | 38 | var b1 = bson.BSON.serialize(id1) 39 | var b2 = bson.BSON.serialize(id2) 40 | 41 | bson.BSON.deserialize(b1).id.should.equal(bson.BSON.deserialize(b2).id) 42 | 43 | }) 44 | 45 | describe('#toJSON', function () { 46 | it('JSON serializes to its id string', function () { 47 | var id = ObjectId(testOid) 48 | JSON.stringify(id).should.equal('"' + testOid + '"') 49 | }) 50 | }) 51 | 52 | it('casts native driver ObjectIds', function () { 53 | var nativeOid = new NativeObjectId() 54 | var oid = ObjectId(nativeOid) 55 | oid.should.be.instanceof(ObjectId.constructor) 56 | oid.toString().should.equal(nativeOid.toString()) 57 | }) 58 | 59 | it('generates a new objectId', function () { 60 | var oid = ObjectId() 61 | oid.should.not.equal(undefined) 62 | }) 63 | 64 | 65 | it('casts itself to itself', function () { 66 | var oid = ObjectId() 67 | var oid2 = ObjectId(oid) 68 | oid2.should.be.instanceof(ObjectId.constructor) 69 | oid.toString().should.equal(oid2.toString()) 70 | }) 71 | 72 | it('returns the same object if casting an ObjectId', function () { 73 | var oid = ObjectId() 74 | var oid2 = ObjectId(oid) 75 | expect(oid === oid2).to.equal(true) 76 | }) 77 | 78 | it('casts strings to ObjectIds', function () { 79 | var oid = '511083bb08ce6b1b00000003' 80 | var oid2 = ObjectId(oid) 81 | oid.should.equal(oid2.toString()) 82 | }) 83 | 84 | it('throws if called as a cast of an invalid objectid', function () { 85 | expect(function () { 86 | var oid = ObjectId('fsodfisohj') 87 | }).to.throw(/invalid/i) 88 | }) 89 | 90 | it('throws when casting falsy values', function () { 91 | expect(function () { 92 | var id 93 | ObjectId(id) 94 | }).to.throw(/invalid/i) 95 | expect(function () { 96 | var id = false 97 | ObjectId(id) 98 | }).to.throw(/invalid/i) 99 | expect(function () { 100 | var id = null 101 | ObjectId(id) 102 | }).to.throw(/invalid/i) 103 | expect(function () { 104 | var id = '' 105 | ObjectId(id) 106 | }).to.throw(/invalid/i) 107 | expect(function () { 108 | var id = 0 109 | ObjectId(id) 110 | }).to.throw(/invalid/i) 111 | }) 112 | 113 | describe('.isValid', function () { 114 | it('validates ObjectIds as 24 character hex strings', function () { 115 | ObjectId.isValid('foo').should.equal(false) 116 | ObjectId.isValid('511083bb08ce6b1b00000003').should.equal(true) 117 | ObjectId.isValid('sdf').should.equal(false) 118 | ObjectId.isValid(123).should.equal(false) 119 | ObjectId.isValid(null).should.equal(false) 120 | ObjectId.isValid({}).should.equal(false) 121 | ObjectId.isValid(['foo']).should.equal(false) 122 | }) 123 | 124 | it('validates itself', function () { 125 | ObjectId.isValid(ObjectId()).should.equal(true) 126 | }) 127 | 128 | it('validates mongo native driver ObjectIds', function () { 129 | ObjectId.isValid(new NativeObjectId).should.equal(true) 130 | }) 131 | 132 | it('false for 1-element array of ObjectId', function () { 133 | ObjectId.isValid([ObjectId()]).should.equal(false) 134 | }) 135 | 136 | }) 137 | 138 | describe('.equals', function () { 139 | it('returns true if oidA and oidB are the same instance', function () { 140 | var oidA = ObjectId() 141 | var oidB = oidA 142 | ObjectId.equals(oidA, oidB).should.equal(true) 143 | }) 144 | it('returns true if oidA and oidB are stringwise equal', function () { 145 | var oidA = ObjectId('511083bb08ce6b1b00000003') 146 | var oidB = '511083bb08ce6b1b00000003' 147 | ObjectId.equals(oidA, oidB).should.equal(true) 148 | }) 149 | it('returns false if A or B are not ObjectIds', function () { 150 | ObjectId.equals('sdfsdfsdfsdf', testOid).should.equal(false) 151 | ObjectId.equals('511083bb08ce6b1b00000003', ['511083bb08ce6b1b00000003']).should.equal(false) 152 | }) 153 | it('is also on ObjectId.prototype', function () { 154 | var oidA = ObjectId() 155 | var oidB = oidA 156 | oidA.equals(oidB).should.equal(true) 157 | }) 158 | it('curries arguments', function () { 159 | var oidA = ObjectId() 160 | var eq = ObjectId.equals(oidA) 161 | eq.should.be.a('function') 162 | eq('sdfsdf').should.equal(false) 163 | eq(oidA).should.equal(true) 164 | }) 165 | }) 166 | 167 | describe('.tryParse', function () { 168 | it('returns false if the first argument cannot be cast to an objectId', function () { 169 | var out = {} 170 | ObjectId.tryParse('sdfsdf', out, 'oid').should.equal(false) 171 | }) 172 | it('returns true and casts the oid to the out object if possible', function () { 173 | var out = {} 174 | ObjectId.tryParse('511083bb08ce6b1b00000003', out, 'oid').should.equal(true) 175 | out.oid.should.be.instanceof(ObjectId.constructor) 176 | out.oid.toString().should.equal('511083bb08ce6b1b00000003') 177 | }) 178 | it('returns false if trying to parse an empty objectId', function () { 179 | var out = {} 180 | ObjectId.tryParse(undefined, out, 'oid').should.equal(false) 181 | }) 182 | it('returns true for a native ObjectId', function () { 183 | var out = {} 184 | var oid = new NativeObjectId() 185 | ObjectId.tryParse(oid, out, 'oid').should.equal(true) 186 | 187 | }) 188 | }) 189 | 190 | }) -------------------------------------------------------------------------------- /app/node_modules/nconf/test/provider-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider-test.js: Tests for the nconf Provider object. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var assert = require('assert'), 9 | fs = require('fs'), 10 | path = require('path'), 11 | spawn = require('child_process').spawn, 12 | vows = require('vows'), 13 | helpers = require('./helpers'), 14 | nconf = require('../lib/nconf'); 15 | 16 | var fixturesDir = path.join(__dirname, 'fixtures'), 17 | mergeFixtures = path.join(fixturesDir, 'merge'), 18 | files = [path.join(mergeFixtures, 'file1.json'), path.join(mergeFixtures, 'file2.json')], 19 | override = JSON.parse(fs.readFileSync(files[0]), 'utf8'); 20 | 21 | function assertProvider(test) { 22 | return { 23 | topic: new nconf.Provider(), 24 | "should use the correct File store": test 25 | }; 26 | } 27 | 28 | vows.describe('nconf/provider').addBatch({ 29 | "When using nconf": { 30 | "an instance of 'nconf.Provider'": { 31 | "calling the use() method with the same store type and different options": { 32 | topic: new nconf.Provider().use('file', { file: files[0] }), 33 | "should use a new instance of the store type": function (provider) { 34 | var old = provider.stores['file']; 35 | 36 | assert.equal(provider.stores.file.file, files[0]); 37 | provider.use('file', { file: files[1] }); 38 | 39 | assert.notStrictEqual(old, provider.stores.file); 40 | assert.equal(provider.stores.file.file, files[1]); 41 | } 42 | }, 43 | "when 'argv' is true": helpers.assertSystemConf({ 44 | script: path.join(fixturesDir, 'scripts', 'provider-argv.js'), 45 | argv: ['--something', 'foobar'] 46 | }), 47 | "when 'env' is true": helpers.assertSystemConf({ 48 | script: path.join(fixturesDir, 'scripts', 'provider-env.js'), 49 | env: { SOMETHING: 'foobar' } 50 | }) 51 | }, 52 | "the default nconf provider": { 53 | "when 'argv' is set to true": helpers.assertSystemConf({ 54 | script: path.join(fixturesDir, 'scripts', 'nconf-argv.js'), 55 | argv: ['--something', 'foobar'], 56 | env: { SOMETHING: true } 57 | }), 58 | "when 'env' is set to true": helpers.assertSystemConf({ 59 | script: path.join(fixturesDir, 'scripts', 'nconf-env.js'), 60 | env: { SOMETHING: 'foobar' } 61 | }), 62 | "when 'argv' is set to true and process.argv is modified": helpers.assertSystemConf({ 63 | script: path.join(fixturesDir, 'scripts', 'nconf-change-argv.js'), 64 | argv: ['--something', 'badValue', 'evenWorse', 'OHNOEZ', 'foobar'] 65 | }), 66 | "when hierarchical 'argv' get": helpers.assertSystemConf({ 67 | script: path.join(fixturesDir, 'scripts', 'nconf-hierarchical-file-argv.js'), 68 | argv: ['--something', 'foobar'], 69 | env: { SOMETHING: true } 70 | }), 71 | "when 'env' is set to true with a nested separator": helpers.assertSystemConf({ 72 | script: path.join(fixturesDir, 'scripts', 'nconf-nested-env.js'), 73 | env: { SOME_THING: 'foobar' } 74 | }) 75 | } 76 | } 77 | }).addBatch({ 78 | "When using nconf": { 79 | "an instance of 'nconf.Provider'": { 80 | "the merge() method": { 81 | topic: new nconf.Provider().use('file', { file: files[1] }), 82 | "should have the result merged in": function (provider) { 83 | provider.load(); 84 | provider.merge(override); 85 | helpers.assertMerged(null, provider.stores.file.store); 86 | assert.equal(provider.stores.file.store.candy.something, 'file1'); 87 | }, 88 | "should merge Objects over null": function (provider) { 89 | provider.load(); 90 | provider.merge(override); 91 | assert.equal(provider.stores.file.store.unicorn.exists, true); 92 | } 93 | } 94 | } 95 | } 96 | }).addBatch({ 97 | "When using nconf": { 98 | "an instance of 'nconf.Provider'": { 99 | "the load() method": { 100 | "when sources are passed in": { 101 | topic: new nconf.Provider({ 102 | sources: { 103 | user: { 104 | type: 'file', 105 | file: files[0] 106 | }, 107 | global: { 108 | type: 'file', 109 | file: files[1] 110 | } 111 | } 112 | }), 113 | "should respect the hierarchy ": function (provider) { 114 | var merged = provider.load(); 115 | 116 | helpers.assertMerged(null, merged); 117 | assert.equal(merged.candy.something, 'file1'); 118 | } 119 | }, 120 | "when multiple stores are used": { 121 | topic: new nconf.Provider().overrides({foo: {bar: 'baz'}}) 122 | .add('file1', {type: 'file', file: files[0]}) 123 | .add('file2', {type: 'file', file: files[1]}), 124 | "should respect the hierarchy": function(provider) { 125 | var merged = provider.load(); 126 | 127 | helpers.assertMerged(null, merged); 128 | assert.equal(merged.foo.bar, 'baz'); 129 | assert.equal(merged.candy.something, 'file1'); 130 | } 131 | } 132 | } 133 | } 134 | } 135 | }).addBatch({ 136 | "When using nconf": { 137 | "an instance of 'nconf.Provider'": { 138 | "the .file() method": { 139 | "with a single filepath": assertProvider(function (provider) { 140 | provider.file(helpers.fixture('store.json')); 141 | assert.isObject(provider.stores.file); 142 | }), 143 | "with a name and a filepath": assertProvider(function (provider) { 144 | provider.file('custom', helpers.fixture('store.json')); 145 | assert.isObject(provider.stores.custom); 146 | }), 147 | "with a single object": assertProvider(function (provider) { 148 | provider.file({ 149 | dir: helpers.fixture(''), 150 | file: 'store.json', 151 | search: true 152 | }); 153 | 154 | assert.isObject(provider.stores.file); 155 | assert.equal(provider.stores.file.file, helpers.fixture('store.json')); 156 | }), 157 | "with a name and an object": assertProvider(function (provider) { 158 | provider.file('custom', { 159 | dir: helpers.fixture(''), 160 | file: 'store.json', 161 | search: true 162 | }); 163 | 164 | assert.isObject(provider.stores.custom); 165 | assert.equal(provider.stores.custom.file, helpers.fixture('store.json')); 166 | }) 167 | } 168 | } 169 | } 170 | }).export(module); 171 | -------------------------------------------------------------------------------- /app/sign/src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import "functions"; 4 | @import "mixins"; 5 | @import "color"; 6 | 7 | 8 | 9 | /* ========== background-color ========= */ 10 | // base bg 11 | $base-bg: $white; 12 | 13 | // active bg color 14 | $base-active-bg: $indigo; 15 | $base-active-light-bg: $ashgrey; 16 | $base-alert-bg: $red; 17 | $base-disabled-bg: $disabled; 18 | 19 | // divider 20 | $base-divider-bg: $grey; 21 | 22 | 23 | 24 | /* ========== font ========== */ 25 | 26 | // defined font size 27 | $body-font-size: rem-calc(14); 28 | $base-font-size-tny: rem-calc(12); 29 | $base-font-size-sml: rem-calc(13); 30 | $base-font-size-def: $body-font-size; 31 | $base-font-size-med: rem-calc(16); 32 | $base-font-size-mid: rem-calc(20); 33 | $base-font-size-lrg: rem-calc(22); 34 | 35 | // defined font color 36 | $base-font-color: $bistre; // 333 正常 37 | $base-font-color-primary: $bistre; //333 重要 38 | $base-font-color-secondary: $darkgrey; // 737373 次要 39 | $base-font-color-alert: $red; // f15964 警告 提醒 40 | $base-font-color-info: $manatee; //aaa 消息信息 41 | $base-font-color-link: $indigo; 42 | $base-font-color-deepbg: $white; // fff 白色 43 | $base-font-color-title: $indigo; 44 | 45 | $base-font-color-info-title: $manatee; // aaa 46 | 47 | $base-font-color-holder: $lavendergray;// c7c7c7 48 | 49 | 50 | $font-family: -apple-system,".SFNSText-Regular",'Helvetica Neue','Hiragino Sans GB','WenQuanYi Micro Hei','Microsoft Yahei',sans-serif; 51 | 52 | 53 | $font-weight-normal: normal !default; 54 | $font-weight-bold: bold !default; 55 | 56 | $body-bg: $white !default; 57 | $body-font-color: $base-font-color; 58 | $body-font-family: $font-family !default; 59 | $body-font-weight: $font-weight-normal !default; 60 | $body-font-style: normal !default; 61 | 62 | $font-smoothing-webkit: antialiased !default; 63 | $font-smoothing-moz: grayscale !default; 64 | 65 | $base-font-size: 100% !default; 66 | $base-line-height: 1.5 !default; 67 | 68 | 69 | 70 | /* ========== Media Query Ranges ========== */ 71 | 72 | $small-breakpoint: 460px !default; 73 | 74 | $small-col-breakpoint: 560px !default; 75 | 76 | $medium-breakpoint: 920px !default; 77 | $large-breakpoint: 1020px !default; 78 | $xlarge-breakpoint: 1920px !default; 79 | $popup-breakpoint: 610px !default; 80 | 81 | $small-range: (0, $small-breakpoint) !default; 82 | $small-col-range: ($small-breakpoint + 1px, $small-col-breakpoint) !default; 83 | $medium-range: ($small-breakpoint + 1px, $medium-breakpoint) !default; 84 | $large-range: ($medium-breakpoint + 1px, $large-breakpoint) !default; 85 | $xlarge-range: ($large-breakpoint + 1px, $xlarge-breakpoint) !default; 86 | $popup-range: (0, $popup-breakpoint) !default; 87 | 88 | $screen: "only screen" !default; 89 | 90 | $popup-low: "#{$screen} and (max-width:#{upper-bound($popup-range)})" !default; 91 | 92 | $landscape: "#{$screen} and (orientation: landscape)" !default; 93 | $portrait: "#{$screen} and (orientation: portrait)" !default; 94 | 95 | $small-up: $screen !default; 96 | $small-only: "#{$screen} and (max-width: #{upper-bound($small-range)})" !default; 97 | 98 | $small-col-only: "#{$screen} and (min-width: #{lower-bound($small-col-range)}) and (max-width:#{upper-bound($small-col-range)})" !default; 99 | 100 | $medium-up: "#{$screen} and (min-width:#{lower-bound($medium-range)})" !default; 101 | $medium-only: "#{$screen} and (min-width:#{lower-bound($medium-range)}) and (max-width:#{upper-bound($medium-range)})" !default; 102 | $medium-low: "#{$screen} and (max-width:#{upper-bound($medium-range)})" !default; 103 | 104 | $large-up: "#{$screen} and (min-width:#{lower-bound($large-range)})" !default; 105 | $large-only: "#{$screen} and (min-width:#{lower-bound($large-range)}) and (max-width:#{upper-bound($large-range)})" !default; 106 | $large-low: "#{$screen} and (max-width:#{upper-bound($large-range)})" !default; 107 | 108 | $xlarge-up: "#{$screen} and (min-width:#{lower-bound($xlarge-range)})" !default; 109 | $xlarge-only: "#{$screen} and (min-width:#{lower-bound($xlarge-range)}) and (max-width:#{upper-bound($xlarge-range)})" !default; 110 | 111 | $xxlarge-up: "#{$screen} and (min-width:#{lower-bound($xlarge-range)})" !default; 112 | 113 | $retina: ( 114 | "#{$screen} and (min-device-pixel-ratio: 2)", 115 | "#{$screen} and (min-resolution: 192dpi)", 116 | "#{$screen} and (min-resolution: 2dppx)" 117 | ); 118 | 119 | /* ========== /Media Query Ranges ========== */ 120 | 121 | // grid 122 | $grid-left-width: rem-calc(260); 123 | // $grid-right-width: rem-calc(460); 124 | $grid-right-width: 36%; 125 | 126 | $grid-right-width-col-2: 45%; 127 | 128 | $grid-right-width-col-1: 70%; 129 | 130 | $grid-left-bg: $base-active-bg; 131 | 132 | 133 | //We use this as cursors values for enabling the option of having custom cursors in the whole site's stylesheet 134 | $cursor-auto-value: auto !default; 135 | $cursor-crosshair-value: crosshair !default; 136 | $cursor-default-value: default !default; 137 | $cursor-disabled-value: not-allowed !default; 138 | $cursor-pointer-value: pointer !default; 139 | $cursor-help-value: help !default; 140 | $cursor-text-value: text !default; 141 | 142 | // border 143 | $base-border-color: $timberwolf; 144 | 145 | $base-focus-border-color: $indigo; 146 | $base-focus-border: 1px solid $base-focus-border-color; 147 | 148 | $base-border-style: 1px solid; 149 | 150 | $base-border: 1px solid $base-border-color; 151 | $base-border-radius: rem-calc(4); 152 | $global-radius: $base-border-radius; 153 | $list-border-width: $base-border-radius; 154 | 155 | $input-base-border-color: $grey; 156 | 157 | 158 | // box-shadow 159 | $base-box-shadow: 0 6px 20px rgba(0,0,0,.15) !default; 160 | $base-pop-shadow: 0 12px 40px rgba(0,0,0,.3) !default; 161 | $base-input-box-shadow: 0 0 8px rgba(0, 0, 0, 0.06) !default; 162 | 163 | $pop-box-shdow: 0 7px 18px rgba(0, 0, 0, 0.12); 164 | $pop-down-box-shdow: 0 -7px 18px rgba(0, 0, 0, 0.12); 165 | 166 | // float 167 | $default-float: left !default; 168 | $opposite-direction: right !default; 169 | 170 | 171 | // main padding 172 | $col-padding: rem-calc(20); 173 | $row-padding: rem-calc(10); 174 | 175 | // z-index master list 176 | $zindex-1: 1 !default; 177 | $zindex-2: 2 !default; 178 | $zindex-3: 3 !default; 179 | 180 | $zindex-navbar: 1000 !default; 181 | $zindex-dropdown: 1000 !default; 182 | $zindex-popover: 1060 !default; 183 | $zindex-tooltip: 1070 !default; 184 | $zindex-navbar-fixed: 1030 !default; 185 | $zindex-modal-background: 1040 !default; 186 | $zindex-loadview: 1045 !default; 187 | $zindex-modal: 1050 !default; 188 | $zindex-drag: 1080 !default; 189 | 190 | // icon size 191 | $base-icon-size: rem-calc(24); 192 | 193 | $small-icon-size: rem-calc(14); 194 | 195 | $li-line-height: rem-calc(36) !default; 196 | 197 | // avatar size 198 | $avatar-size-sml: rem-calc(16); 199 | $avatar-size: rem-calc(36); 200 | $avatar-size-lrg: rem-calc(60); 201 | 202 | 203 | $base-transition-duration: .2s; 204 | $base-transition-timing-function: ease; 205 | 206 | 207 | $base-list-padding-t-b: rem-calc(5); 208 | // select list 209 | $base-list-padding: $base-list-padding-t-b 0; 210 | 211 | $base-modal-padding: rem-calc(24); 212 | -------------------------------------------------------------------------------- /app/sign/src/styles/_button.scss: -------------------------------------------------------------------------------- 1 | // 2 | // @variables 3 | 4 | $primary-color: $indigo !default; 5 | $secondary-color: $whitelilac !default; 6 | $alert-color: $red !default; 7 | $normal-color: $white; 8 | // $success-color: #43AC6A !default; 9 | // $warning-color: #f08a24 !default; 10 | // $info-color: #a0d3e8 !default; 11 | // 12 | 13 | $button-transition-duration: .2s; 14 | $button-transition-timing-function: ease; 15 | 16 | // all buttons min width 17 | $button-min-width: rem-calc(100) !default; 18 | 19 | // all buttons padding right and left 20 | $button-padding-rl: rem-calc(12) !default; 21 | 22 | // padding top and bottom for buttons. 23 | $button-tny: rem-calc(7) !default; 24 | $button-sml: rem-calc(8) !default; 25 | $button-med: rem-calc(11) !default; 26 | $button-lrg: rem-calc(13) !default; 27 | 28 | // the display property. 29 | $button-display: inline-block !default; 30 | $button-margin-bottom: rem-calc(0) !default; 31 | 32 | // button text styles. 33 | $button-font-family: $body-font-family !default; 34 | $button-font-color: $white !default; 35 | 36 | $button-font-tny: $base-font-size-sml !default; 37 | $button-font-sml: $body-font-size !default; 38 | $button-font-med: $body-font-size !default; 39 | $button-font-lrg: $body-font-size !default; 40 | $button-font-weight: $font-weight-normal !default; 41 | $button-font-align: center !default; 42 | 43 | // hover effects. 44 | $button-function-factor: -5% !default; 45 | 46 | // active / keypress effects 47 | $button-active-function-factor: -20% !default; 48 | 49 | // We use these to control button border styles. 50 | $button-border-width: 0 !default; 51 | $button-border-style: solid !default; 52 | 53 | $button-bg-color: $primary-color !default; 54 | $button-bg-hover: scale-color($button-bg-color, $lightness: $button-function-factor) !default; 55 | $button-bg-active: scale-color($button-bg-color, $lightness: $button-active-function-factor) !default; 56 | 57 | 58 | $secondary-button-bg-color: $secondary-color !default; 59 | $secondary-button-bg-hover: scale-color($secondary-color, $lightness: $button-function-factor) !default; 60 | $secondary-button-bg-active: scale-color($secondary-color, $lightness: $button-active-function-factor) !default; 61 | $secondary-button-font-color: $bistre !default; 62 | 63 | $alert-button-bg-color: $alert-color !default; 64 | $alert-button-bg-hover: scale-color($alert-color, $lightness: $button-function-factor) !default; 65 | $alert-button-bg-active: scale-color($alert-color, $lightness: $button-active-function-factor) !default; 66 | 67 | $normal-button-bg-color: $normal-color; 68 | $normal-button-bg-hover: scale-color($normal-button-bg-color, $lightness: $button-function-factor) !default; 69 | $normal-button-bg-active: scale-color($normal-button-bg-color, $lightness: $button-active-function-factor) !default; 70 | $normal-button-font-color: $base-font-color-info; 71 | 72 | 73 | // the default radius used throughout the core. 74 | $button-radius: $global-radius !default; 75 | $button-round: $global-radius !default; 76 | 77 | // set default opacity and cursor for disabled buttons. 78 | $button-disabled-opacity: .7 !default; 79 | $button-disabled-cursor: $cursor-default-value !default; 80 | 81 | 82 | // 83 | // @MIXIN 84 | // 85 | // We use this mixin to create a default button base. 86 | // 87 | // $style - Sets base styles. Can be set to false. Default: true. 88 | // $display - Used to control display property. Default: $button-display || inline-block 89 | 90 | @mixin button-base($display:$button-display) { 91 | -webkit-appearance: none; 92 | -moz-appearance: none; 93 | box-shadow: none; 94 | border-radius:$button-radius; 95 | border-style: $button-border-style; 96 | border-width: $button-border-width; 97 | padding-left: $button-padding-rl; 98 | padding-right: $button-padding-rl; 99 | min-width: $button-min-width; 100 | cursor: $cursor-pointer-value; 101 | font-family: $button-font-family; 102 | font-weight: $button-font-weight; 103 | line-height: 1; 104 | margin: 0 0 $button-margin-bottom; 105 | position: relative; 106 | text-align: $button-font-align; 107 | text-decoration: none; 108 | outline: none; 109 | color: $button-font-color; 110 | @if $display { display: $display; } 111 | } 112 | 113 | // @MIXIN 114 | // 115 | // We use this mixin to add button size styles 116 | // 117 | // $padding - Used to build padding for buttons Default: $button-med ||= rem-calc(12) 118 | 119 | @mixin button-size($padding:$button-med) { 120 | 121 | // We control which padding styles come through 122 | padding-top: $padding; 123 | padding-bottom: $padding; 124 | // We control the font-size based on mixin input. 125 | @if $padding == $button-med { font-size: $button-font-med;} 126 | @else if $padding == $button-tny { font-size: $button-font-tny; } 127 | @else if $padding == $button-sml { font-size: $button-font-sml; } 128 | @else if $padding == $button-lrg { font-size: $button-font-lrg; } 129 | 130 | } 131 | 132 | // @MIXIN 133 | // 134 | // we use this mixin to create the button hover and border colors 135 | 136 | // @MIXIN 137 | // 138 | // We use this mixin to add button color styles 139 | // 140 | // $bg - Background color. We can set $bg:false for a transparent background. Default: $primary-color. 141 | // $radius - If true, set to button radius which is $button-radius || explicitly set radius amount in px (ex. $radius:10px). Default: false 142 | // $disabled - We can set $disabled:true to create a disabled transparent button. Default: false 143 | // $bg-hover - Button Hover Background Color. Default: $button-bg-hover 144 | @mixin button-style($bg:$button-bg-color, $disabled:false, $bg-hover:$button-bg-hover, $bg-active:$button-bg-active, $color:$button-font-color) { 145 | 146 | color: $color; 147 | // We control which background styles are used, 148 | // these can be removed by setting $bg:false 149 | 150 | background-color: $bg; 151 | 152 | @if $disabled == false { 153 | &:hover, 154 | &:focus { background-color: $bg-hover; } 155 | &:active { background-color: $bg-active; } 156 | } 157 | 158 | // We can set $disabled:true to create a disabled transparent button. 159 | @if $disabled { 160 | cursor: $button-disabled-cursor; 161 | opacity: $button-disabled-opacity; 162 | } 163 | 164 | } 165 | 166 | 167 | // class 168 | button, .button, .btn { 169 | @include button-base; 170 | @include button-size; 171 | @include button-style; 172 | 173 | @include single-transition(background-color, $button-transition-duration, $button-transition-timing-function); 174 | 175 | &.btn-primary { @include button-style($bg:$button-bg-color, $bg-hover:$button-bg-hover, $bg-active: $button-bg-active); } 176 | &.btn-secondary, 177 | &.btn-cancel { @include button-style($bg:$secondary-button-bg-color, $bg-hover:$secondary-button-bg-hover, $bg-active: $secondary-button-bg-active, $color: $secondary-button-font-color); } 178 | &.btn-alert { @include button-style($bg:$alert-button-bg-color, $bg-hover:$alert-button-bg-hover, $bg-active: $alert-button-bg-active); } 179 | 180 | &.btn-normal { @include button-style($bg:$normal-button-bg-color, $bg-hover:$normal-button-bg-hover, $bg-active: $normal-button-bg-active, $color: $normal-button-font-color); } 181 | 182 | &.btn-tny { @include button-size($padding:$button-tny); } 183 | &.btn-sml { @include button-size($padding:$button-sml); } 184 | &.btn-med { @include button-size($padding:$button-med); } 185 | &.btn-lrg { @include button-size($padding:$button-lrg); } 186 | 187 | &.disabled, &[disabled] { @include button-style($bg:$button-bg-color, $disabled:true, $bg-hover:$button-bg-hover); 188 | &.btn-primary { @include button-style($bg:$button-bg-color, $disabled:true, $bg-hover:$button-bg-hover); } 189 | &.btn-cancel { @include button-style($bg:$secondary-button-bg-color, $disabled:true, $color: $secondary-button-font-color); } 190 | &.btn-alert { @include button-style($bg:$alert-button-bg-color, $disabled:true, $bg-hover:$alert-button-bg-hover); } 191 | } 192 | } 193 | 194 | //firefox 2px fix 195 | button::-moz-focus-inner {border:0; padding:0;} 196 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/stores/file.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file.js: Simple file storage engine for nconf files 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var crypto = require('crypto'), 9 | fs = require('fs'), 10 | path = require('path'), 11 | util = require('util'), 12 | Secure = require('secure-keys'), 13 | formats = require('../formats'), 14 | Memory = require('./memory').Memory, 15 | exists = fs.exists || path.exists, 16 | existsSync = fs.existsSync || path.existsSync; 17 | 18 | // 19 | // ### function File (options) 20 | // #### @options {Object} Options for this instance 21 | // Constructor function for the File nconf store, a simple abstraction 22 | // around the Memory store that can persist configuration to disk. 23 | // 24 | var File = exports.File = function (options) { 25 | if (!options || !options.file) { 26 | throw new Error ('Missing required option `file`'); 27 | } 28 | 29 | Memory.call(this, options); 30 | 31 | this.type = 'file'; 32 | this.file = options.file; 33 | this.dir = options.dir || process.cwd(); 34 | this.format = options.format || formats.json; 35 | this.secure = options.secure; 36 | this.spacing = options.json_spacing 37 | || options.spacing 38 | || 2; 39 | 40 | if (this.secure) { 41 | this.secure = Buffer.isBuffer(this.secure) || typeof this.secure === 'string' 42 | ? { secret: this.secure.toString() } 43 | : this.secure; 44 | 45 | this.secure.alg = this.secure.alg || 'aes-256-ctr'; 46 | if (this.secure.secretPath) { 47 | this.secure.secret = fs.readFileSync(this.secure.secretPath, 'utf8'); 48 | } 49 | 50 | if (!this.secure.secret) { 51 | throw new Error('secure.secret option is required'); 52 | } 53 | 54 | this.keys = new Secure({ 55 | secret: this.secure.secret, 56 | alg: this.secure.alg, 57 | format: this.format 58 | }); 59 | } 60 | 61 | if (options.search) { 62 | this.search(this.dir); 63 | } 64 | }; 65 | 66 | // Inherit from the Memory store 67 | util.inherits(File, Memory); 68 | 69 | // 70 | // ### function save (value, callback) 71 | // #### @value {Object} _Ignored_ Left here for consistency 72 | // #### @callback {function} Continuation to respond to when complete. 73 | // Saves the current configuration object to disk at `this.file` 74 | // using the format specified by `this.format`. 75 | // 76 | File.prototype.save = function (value, callback) { 77 | if (!callback) { 78 | callback = value; 79 | value = null; 80 | } 81 | 82 | fs.writeFile(this.file, this.stringify(), callback); 83 | }; 84 | 85 | // 86 | // ### function saveSync (value, callback) 87 | // #### @value {Object} _Ignored_ Left here for consistency 88 | // #### @callback {function} **Optional** Continuation to respond to when complete. 89 | // Saves the current configuration object to disk at `this.file` 90 | // using the format specified by `this.format` synchronously. 91 | // 92 | File.prototype.saveSync = function (value) { 93 | fs.writeFileSync(this.file, this.stringify()); 94 | return this.store; 95 | }; 96 | 97 | // 98 | // ### function load (callback) 99 | // #### @callback {function} Continuation to respond to when complete. 100 | // Responds with an Object representing all keys associated in this instance. 101 | // 102 | File.prototype.load = function (callback) { 103 | var self = this; 104 | 105 | exists(self.file, function (exists) { 106 | if (!exists) { 107 | return callback(null, {}); 108 | } 109 | 110 | // 111 | // Else, the path exists, read it from disk 112 | // 113 | fs.readFile(self.file, function (err, data) { 114 | if (err) { 115 | return callback(err); 116 | } 117 | 118 | try { 119 | // Deals with string that include BOM 120 | var stringData = data.toString(); 121 | if (stringData.charAt(0) === '\uFEFF') { 122 | stringData = stringData.substr(1); 123 | } 124 | 125 | self.store = self.parse(stringData); 126 | } 127 | catch (ex) { 128 | return callback(new Error("Error parsing your configuration file: [" + self.file + ']: ' + ex.message)); 129 | } 130 | 131 | callback(null, self.store); 132 | }); 133 | }); 134 | }; 135 | 136 | // 137 | // ### function loadSync (callback) 138 | // Attempts to load the data stored in `this.file` synchronously 139 | // and responds appropriately. 140 | // 141 | File.prototype.loadSync = function () { 142 | if (!existsSync(this.file)) { 143 | this.store = {}; 144 | return this.store; 145 | } 146 | 147 | // 148 | // Else, the path exists, read it from disk 149 | // 150 | try { 151 | // Deals with file that include BOM 152 | var fileData = fs.readFileSync(this.file, 'utf8'); 153 | if (fileData.charAt(0) === '\uFEFF') { 154 | fileData = fileData.substr(1); 155 | } 156 | 157 | this.store = this.parse(fileData); 158 | } 159 | catch (ex) { 160 | throw new Error("Error parsing your configuration file: [" + this.file + ']: ' + ex.message); 161 | } 162 | 163 | return this.store; 164 | }; 165 | 166 | // 167 | // ### function stringify () 168 | // Returns an encrypted version of the contents IIF 169 | // `this.secure` is enabled 170 | // 171 | File.prototype.stringify = function () { 172 | var data = this.store, 173 | self = this; 174 | 175 | if (this.secure) { 176 | data = this.keys.encrypt(data); 177 | } 178 | 179 | return this.format.stringify(data, null, this.spacing); 180 | }; 181 | 182 | // 183 | // ### function parse (contents) 184 | // Returns a decrypted version of the contents IFF 185 | // `this.secure` is enabled. 186 | // 187 | File.prototype.parse = function (contents) { 188 | var parsed = this.format.parse(contents), 189 | self = this; 190 | 191 | if (!this.secure) { 192 | return parsed; 193 | } 194 | 195 | return this.keys.decrypt(parsed); 196 | 197 | }; 198 | 199 | 200 | // 201 | // ### function search (base) 202 | // #### @base {string} Base directory (or file) to begin searching for the target file. 203 | // Attempts to find `this.file` by iteratively searching up the 204 | // directory structure 205 | // 206 | File.prototype.search = function (base) { 207 | var looking = true, 208 | fullpath, 209 | previous, 210 | stats; 211 | 212 | base = base || process.cwd(); 213 | 214 | if (this.file[0] === '/') { 215 | // 216 | // If filename for this instance is a fully qualified path 217 | // (i.e. it starts with a `'/'`) then check if it exists 218 | // 219 | try { 220 | stats = fs.statSync(fs.realpathSync(this.file)); 221 | if (stats.isFile()) { 222 | fullpath = this.file; 223 | looking = false; 224 | } 225 | } 226 | catch (ex) { 227 | // 228 | // Ignore errors 229 | // 230 | } 231 | } 232 | 233 | if (looking && base) { 234 | // 235 | // Attempt to stat the realpath located at `base` 236 | // if the directory does not exist then return false. 237 | // 238 | try { 239 | var stat = fs.statSync(fs.realpathSync(base)); 240 | looking = stat.isDirectory(); 241 | } 242 | catch (ex) { 243 | return false; 244 | } 245 | } 246 | 247 | while (looking) { 248 | // 249 | // Iteratively look up the directory structure from `base` 250 | // 251 | try { 252 | stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file))); 253 | looking = stats.isDirectory(); 254 | } 255 | catch (ex) { 256 | previous = base; 257 | base = path.dirname(base); 258 | 259 | if (previous === base) { 260 | // 261 | // If we've reached the top of the directory structure then simply use 262 | // the default file path. 263 | // 264 | try { 265 | stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file))); 266 | if (stats.isDirectory()) { 267 | fullpath = undefined; 268 | } 269 | } 270 | catch (ex) { 271 | // 272 | // Ignore errors 273 | // 274 | } 275 | 276 | looking = false; 277 | } 278 | } 279 | } 280 | 281 | // 282 | // Set the file for this instance to the fullpath 283 | // that we have found during the search. In the event that 284 | // the search was unsuccessful use the original value for `this.file`. 285 | // 286 | this.file = fullpath || this.file; 287 | 288 | return fullpath; 289 | }; 290 | -------------------------------------------------------------------------------- /app/sign/dist/sign.min.css: -------------------------------------------------------------------------------- 1 | .text-tny{font-size:12px}.text-sml{font-size:13px}.text-def{font-size:14px}.text-med{font-size:16px}.text-lrg{font-size:22px}.left{float:left!important}.right{float:right!important}.clearfix:after,.clearfix:before{content:" ";display:table}.clearfix:after{clear:both}.hide{display:none!important}.hidden{display:none}.invisible{visibility:hidden}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.divider,.l-divider{height:1px;width:100%}.l-divider{margin:10px 0}.i-tny{width:14px;height:14px}.i-sml{width:16px;height:16px}.i-med{width:32px;height:32px}.i-lrg{width:40px;height:40px}.i-lrg-x{width:48px;height:48px}.i-xl{width:100px;height:100px}.i-xxl{width:180px;height:180px}.i-xxxl{width:192px;height:192px}.i-redeem{width:156px;height:96px}.line-left,.line-right{position:relative}.line-right{z-index:1;float:right}.b-h{height:100%}.drag{visibility:hidden;position:absolute;cursor:move}.shadow{box-shadow:0 6px 20px rgba(0,0,0,.15)}.pop-shadow,.shadow{border:1px solid #ececec;border-radius:4px}.pop-shadow{box-shadow:0 12px 40px rgba(0,0,0,.3)}.dropdown-toggle{cursor:pointer}img.round{border-radius:50%;overflow:hidden}.btns{line-height:1}.nowrap{white-space:nowrap}body>svg{display:none}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.drag-help{width:360px;max-width:360px;opacity:.9}.line-sml{line-height:21px}.file-wrap{overflow:hidden}.can-not-select *,.select-disabled *{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.can-not-select [contenteditable],.can-not-select input,.can-not-select textarea,.can-select,.can-select *,.select-disabled [contenteditable],.select-disabled input,.select-disabled textarea,.select-enabled,.select-enabled *{-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text}.tl-body .antiscroll-wrap{position:static}.loading-view{width:100%;height:100%;background-color:#fff;position:absolute;top:0;z-index:3;text-align:center}.loading-view .logo{position:relative;height:100%;top:50%;margin-top:-100px}.avoid-event svg{pointer-events:none}.btn,.button,button{-webkit-appearance:none;-moz-appearance:none;box-shadow:none;border-radius:4px;border-style:solid;border-width:0;padding:11px 12px;min-width:100px;cursor:pointer;font-family:-apple-system,\.SFNSText-Regular,Helvetica Neue,Hiragino Sans GB,WenQuanYi Micro Hei,Microsoft Yahei,sans-serif;font-weight:400;line-height:1;margin:0;position:relative;text-align:center;text-decoration:none;outline:none;display:inline-block;font-size:14px;color:#fff;background-color:#617fde;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}.btn:focus,.btn:hover,.button:focus,.button:hover,button:focus,button:hover{background-color:#5474db}.btn:active,.button:active,button:active{background-color:#2c54d3}.btn.btn-primary,.button.btn-primary,button.btn-primary{color:#fff;background-color:#617fde}.btn.btn-primary:focus,.btn.btn-primary:hover,.button.btn-primary:focus,.button.btn-primary:hover,button.btn-primary:focus,button.btn-primary:hover{background-color:#5474db}.btn.btn-primary:active,.button.btn-primary:active,button.btn-primary:active{background-color:#2c54d3}.btn.btn-cancel,.btn.btn-secondary,.button.btn-cancel,.button.btn-secondary,button.btn-cancel,button.btn-secondary{color:#333;background-color:#ebebeb}.btn.btn-cancel:focus,.btn.btn-cancel:hover,.btn.btn-secondary:focus,.btn.btn-secondary:hover,.button.btn-cancel:focus,.button.btn-cancel:hover,.button.btn-secondary:focus,.button.btn-secondary:hover,button.btn-cancel:focus,button.btn-cancel:hover,button.btn-secondary:focus,button.btn-secondary:hover{background-color:#dfdfdf}.btn.btn-cancel:active,.btn.btn-secondary:active,.button.btn-cancel:active,.button.btn-secondary:active,button.btn-cancel:active,button.btn-secondary:active{background-color:#bcbcbc}.btn.btn-alert,.button.btn-alert,button.btn-alert{color:#fff;background-color:#ff3180}.btn.btn-alert:focus,.btn.btn-alert:hover,.button.btn-alert:focus,.button.btn-alert:hover,button.btn-alert:focus,button.btn-alert:hover{background-color:#f27}.btn.btn-alert:active,.button.btn-alert:active,button.btn-alert:active{background-color:#f3005d}.btn.btn-normal,.button.btn-normal,button.btn-normal{color:#aaa;background-color:#fff}.btn.btn-normal:focus,.btn.btn-normal:hover,.button.btn-normal:focus,.button.btn-normal:hover,button.btn-normal:focus,button.btn-normal:hover{background-color:#f2f2f2}.btn.btn-normal:active,.button.btn-normal:active,button.btn-normal:active{background-color:#ccc}.btn.btn-tny,.button.btn-tny,button.btn-tny{padding-top:7px;padding-bottom:7px;font-size:13px}.btn.btn-sml,.button.btn-sml,button.btn-sml{padding-top:8px;padding-bottom:8px;font-size:14px}.btn.btn-med,.button.btn-med,button.btn-med{padding-top:11px;padding-bottom:11px;font-size:14px}.btn.btn-lrg,.button.btn-lrg,button.btn-lrg{padding-top:13px;padding-bottom:13px;font-size:14px}.btn.disabled,.btn.disabled.btn-primary,.btn[disabled],.btn[disabled].btn-primary,.button.disabled,.button.disabled.btn-primary,.button[disabled],.button[disabled].btn-primary,button.disabled,button.disabled.btn-primary,button[disabled],button[disabled].btn-primary{color:#fff;background-color:#617fde;cursor:default;opacity:.7}.btn.disabled.btn-cancel,.btn[disabled].btn-cancel,.button.disabled.btn-cancel,.button[disabled].btn-cancel,button.disabled.btn-cancel,button[disabled].btn-cancel{color:#333;background-color:#ebebeb;cursor:default;opacity:.7}.btn.disabled.btn-alert,.btn[disabled].btn-alert,.button.disabled.btn-alert,.button[disabled].btn-alert,button.disabled.btn-alert,button[disabled].btn-alert{color:#fff;background-color:#ff3180;cursor:default;opacity:.7}button::-moz-focus-inner{border:0;padding:0}.btns .btn-loading,.btns.ing .btn-initial{display:none}.btns.ing .btn-loading{display:inline-block}.icon-email-blue{width:24px;height:24px;background-position:-64px 0}.icon-email-gray{width:24px;height:24px;background-position:-64px -24px}.icon-lock-blue{width:24px;height:24px;background-position:-88px 0}.icon-lock-gray{width:24px;height:24px;background-position:-88px -24px}.icon-logo{width:64px;height:68px;background-position:0 0}.icon-name-blue{width:24px;height:24px;background-position:0 -68px}.icon-name-gray{width:24px;height:24px;background-position:-24px -68px}.icon_sign{background-size:112px 92px;background-repeat:no-repeat;display:inline-block}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:2dppx),only screen and (min-resolution:192dpi){.icon_sign{background-size:112px 92px}}::-webkit-input-placeholder{color:#c7c7c7}:-moz-placeholder,::-moz-placeholder{color:#c7c7c7}:-ms-input-placeholder{color:#c7c7c7!important}a,body,div,html,i,li,p,span,ul{margin:0;padding:0;border:0;outline:0;font-size:100%;font:inherit}ol,ul{list-style:none}a{text-decoration:none}a,a:hover{color:#617fde}a:hover{text-decoration:underline}a:active,a:hover{outline:0}a:visited{color:#617fde;text-decoration:none}input,input:focus{outline:0;box-shadow:none}img{border-radius:50%}.button{cursor:pointer;-webkit-appearance:none}body{line-height:1;font-family:-apple-system,\.SFNSText-Regular,Helvetica Neue,Hiragino Sans GB,WenQuanYi Micro Hei,Microsoft Yahei,sans-serif;background:#fdfdfd;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sign{max-width:400px;min-width:300px;margin:48px auto;color:#333}.card{position:relative;margin:0 auto}.logo{text-align:center;margin-bottom:36px}.card-section{background-color:#fff;border:1px solid #ececec;border-radius:2px;padding:40px 50px}.form{position:relative}.sign .row{position:relative;border-bottom:1px solid #ececec;border-radius:1px;margin-bottom:28px}.sign .row,.sign .row.active{-webkit-transition:border-color ease-in-out .15s;transition:border-color ease-in-out .15s}.sign .row.email input,.sign .row.name input,.sign .row.password input{border:0 none;font-size:16px;padding:10px 10px 8px 8px;width:calc(100% - 70px)}.sign .row.forgot-password,.sign .row.submit{border-bottom:none}.sign .row.submit{margin:0 auto;margin-bottom:12px}.sign .row.forgot-password{margin:0;text-align:left;font-size:13px}.input-bar{position:relative;display:block;width:100%;height:1px}.input-bar:after,.input-bar:before{content:'';height:1px;width:0;bottom:-1px;position:absolute;background-color:#617fde;border-radius:1px;-webkit-transition:all .28s cubic-bezier(.4,0,.2,1);transition:all .28s cubic-bezier(.4,0,.2,1)}.input-bar:before{left:50%;border-top-right-radius:0;border-bottom-right-radius:0}.input-bar:after{right:50%;border-top-left-radius:0;border-bottom-left-radius:0}input:focus~.input-bar:before{left:0;width:51%}input:focus~.input-bar:after{right:0;width:50%}.switch-site{display:none}.chrome-extension .forgot-password .link{float:right}.chrome-extension .forgot-password .switch-site{display:inline;float:left}.sign .row .icon{position:relative;top:6px}.middle-line{overflow:hidden;text-align:center}.middle-line:after,.middle-line:before{background-color:#ececec;content:"";display:inline-block;height:1px;position:relative;vertical-align:middle;width:50%}.middle-line:before{right:.5em;margin-left:-50%}.middle-line:after{left:.5em;margin-right:-50%}.sns-title{color:#c7c7c7;font-size:13px;padding:30px 0 36px}.signin-sns ul{text-align:center}.signin-sns ul li{display:inline-block}.signin-sns ul li.second{margin:0 42px}.signin-sns ul li:hover{opacity:.9}.link.sign-link,.link.signin,.link.signup{text-align:center;padding-top:35px;font-size:14px}.link.message{line-height:25px;padding:40px 0 0}.link.success{padding:40px 0}.button{font-size:16px;width:100%}.error{position:absolute;right:0;z-index:2}.error .warn{display:none;color:#ff3180;font-size:13px;margin-top:6px;line-height:1.1}.error.long{position:relative}@media only screen and (max-width:460px){.sign{width:95%}.sign .signin-sns ul li:nth-child(2){margin:0 10px}} -------------------------------------------------------------------------------- /app/node_modules/nconf/test/stores/file-store-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file-store-test.js: Tests for the nconf File store. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var fs = require('fs'), 9 | path = require('path'), 10 | vows = require('vows'), 11 | assert = require('assert'), 12 | nconf = require('../../lib/nconf'), 13 | data = require('../fixtures/data').data, 14 | store; 15 | 16 | vows.describe('nconf/stores/file').addBatch({ 17 | "When using the nconf file store": { 18 | "with a valid JSON file": { 19 | topic: function () { 20 | var filePath = path.join(__dirname, '..', 'fixtures', 'store.json'); 21 | fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); 22 | this.store = store = new nconf.File({ file: filePath }); 23 | return null; 24 | }, 25 | "the load() method": { 26 | topic: function () { 27 | this.store.load(this.callback); 28 | }, 29 | "should load the data correctly": function (err, data) { 30 | assert.isNull(err); 31 | assert.deepEqual(data, this.store.store); 32 | } 33 | } 34 | }, 35 | "with a malformed JSON file": { 36 | topic: function () { 37 | var filePath = path.join(__dirname, '..', 'fixtures', 'malformed.json'); 38 | this.store = new nconf.File({ file: filePath }); 39 | return null; 40 | }, 41 | "the load() method with a malformed JSON config file": { 42 | topic: function () { 43 | this.store.load(this.callback.bind(null, null)); 44 | }, 45 | "should respond with an error and indicate file name": function (_, err) { 46 | assert.isTrue(!!err); 47 | assert.match(err, /malformed\.json/); 48 | } 49 | } 50 | }, 51 | "with a valid UTF8 JSON file that contains a BOM": { 52 | topic: function () { 53 | var filePath = path.join(__dirname, '..', 'fixtures', 'bom.json'); 54 | this.store = store = new nconf.File({ file: filePath }); 55 | return null; 56 | }, 57 | "the load() method": { 58 | topic: function () { 59 | this.store.load(this.callback); 60 | }, 61 | "should load the data correctly": function (err, data) { 62 | assert.isNull(err); 63 | assert.deepEqual(data, this.store.store); 64 | } 65 | }, 66 | "the loadSync() method": { 67 | topic: function () { 68 | var data = this.store.loadSync(); 69 | return data; 70 | }, 71 | "should load the data correctly": function (result) { 72 | assert.deepEqual(result, this.store.store); 73 | } 74 | } 75 | }, 76 | "with a valid UTF8 JSON file that contains no BOM": { 77 | topic: function () { 78 | var filePath = path.join(__dirname, '..', 'fixtures', 'no-bom.json'); 79 | this.store = store = new nconf.File({ file: filePath }); 80 | return null; 81 | }, 82 | "the load() method": { 83 | topic: function () { 84 | this.store.load(this.callback); 85 | }, 86 | "should load the data correctly": function (err, data) { 87 | assert.isNull(err); 88 | assert.deepEqual(data, this.store.store); 89 | } 90 | }, 91 | "the loadSync() method": { 92 | topic: function () { 93 | var data = this.store.loadSync(); 94 | return data; 95 | }, 96 | "should load the data correctly": function (result) { 97 | assert.deepEqual(result, this.store.store); 98 | } 99 | } 100 | } 101 | } 102 | }).addBatch({ 103 | "When using the nconf file store": { 104 | topic: function () { 105 | var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'), 106 | tmpStore = new nconf.File({ file: tmpPath }); 107 | return tmpStore; 108 | }, 109 | "the save() method": { 110 | topic: function (tmpStore) { 111 | var that = this; 112 | 113 | Object.keys(data).forEach(function (key) { 114 | tmpStore.set(key, data[key]); 115 | }); 116 | 117 | tmpStore.save(function () { 118 | fs.readFile(tmpStore.file, function (err, d) { 119 | fs.unlinkSync(tmpStore.file); 120 | 121 | return err 122 | ? that.callback(err) 123 | : that.callback(err, JSON.parse(d.toString())); 124 | }); 125 | }); 126 | }, 127 | "should save the data correctly": function (err, read) { 128 | assert.isNull(err); 129 | assert.deepEqual(read, data); 130 | } 131 | } 132 | } 133 | }).addBatch({ 134 | "When using the nconf file store": { 135 | topic: function () { 136 | var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'), 137 | tmpStore = new nconf.File({ file: tmpPath }); 138 | return tmpStore; 139 | }, 140 | "the saveSync() method": { 141 | topic: function (tmpStore) { 142 | var that = this; 143 | 144 | Object.keys(data).forEach(function (key) { 145 | tmpStore.set(key, data[key]); 146 | }); 147 | 148 | var saved = tmpStore.saveSync(); 149 | 150 | fs.readFile(tmpStore.file, function (err, d) { 151 | fs.unlinkSync(tmpStore.file); 152 | 153 | return err 154 | ? that.callback(err) 155 | : that.callback(err, JSON.parse(d.toString()), saved); 156 | }); 157 | }, 158 | "should save the data correctly": function (err, read, saved) { 159 | assert.isNull(err); 160 | assert.deepEqual(read, data); 161 | assert.deepEqual(read, saved); 162 | } 163 | } 164 | } 165 | }).addBatch({ 166 | "When using the nconf file store": { 167 | "the set() method": { 168 | "should respond with true": function () { 169 | assert.isTrue(store.set('foo:bar:bazz', 'buzz')); 170 | assert.isTrue(store.set('falsy:number', 0)); 171 | assert.isTrue(store.set('falsy:string', '')); 172 | assert.isTrue(store.set('falsy:boolean', false)); 173 | assert.isTrue(store.set('falsy:object', null)); 174 | } 175 | }, 176 | "the get() method": { 177 | "should respond with the correct value": function () { 178 | assert.equal(store.get('foo:bar:bazz'), 'buzz'); 179 | assert.equal(store.get('falsy:number'), 0); 180 | assert.equal(store.get('falsy:string'), ''); 181 | assert.equal(store.get('falsy:boolean'), false); 182 | assert.equal(store.get('falsy:object'), null); 183 | } 184 | }, 185 | "the clear() method": { 186 | "should respond with the true": function () { 187 | assert.equal(store.get('foo:bar:bazz'), 'buzz'); 188 | assert.isTrue(store.clear('foo:bar:bazz')); 189 | assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined'); 190 | } 191 | } 192 | } 193 | }).addBatch({ 194 | "When using the nconf file store": { 195 | "the search() method": { 196 | "when the target file exists higher in the directory tree": { 197 | topic: function () { 198 | var filePath = this.filePath = path.join(process.env.HOME, '.nconf'); 199 | fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); 200 | return new (nconf.File)({ 201 | file: '.nconf' 202 | }) 203 | }, 204 | "should update the file appropriately": function (store) { 205 | store.search(); 206 | assert.equal(store.file, this.filePath); 207 | fs.unlinkSync(this.filePath); 208 | } 209 | }, 210 | "when the target file doesn't exist higher in the directory tree": { 211 | topic: function () { 212 | var filePath = this.filePath = path.join(__dirname, '..', 'fixtures', 'search-store.json'); 213 | return new (nconf.File)({ 214 | dir: path.dirname(filePath), 215 | file: 'search-store.json' 216 | }) 217 | }, 218 | "should update the file appropriately": function (store) { 219 | store.search(); 220 | assert.equal(store.file, this.filePath); 221 | } 222 | } 223 | } 224 | } 225 | }).addBatch({ 226 | "When using the nconf file store": { 227 | topic: function () { 228 | var secureStore = new nconf.File({ 229 | file: path.join(__dirname, '..', 'fixtures', 'secure.json'), 230 | secure: 'super-secretzzz' 231 | }); 232 | 233 | secureStore.store = data; 234 | return secureStore; 235 | }, 236 | "the stringify() method should encrypt properly": function (store) { 237 | var contents = JSON.parse(store.stringify()); 238 | Object.keys(data).forEach(function (key) { 239 | assert.isObject(contents[key]); 240 | assert.isString(contents[key].value); 241 | assert.equal(contents[key].alg, 'aes-256-ctr'); 242 | }); 243 | }, 244 | "the parse() method should decrypt properly": function (store) { 245 | var contents = store.stringify(); 246 | var parsed = store.parse(contents); 247 | assert.deepEqual(parsed, data); 248 | }, 249 | "the load() method should decrypt properly": function (store) { 250 | store.load(function (err, loaded) { 251 | assert.isNull(err); 252 | assert.deepEqual(loaded, data); 253 | }); 254 | }, 255 | "the loadSync() method should decrypt properly": function (store) { 256 | var loaded = store.loadSync() 257 | assert.deepEqual(loaded, data); 258 | } 259 | } 260 | }).export(module); 261 | 262 | -------------------------------------------------------------------------------- /app/node_modules/nconf/README.md: -------------------------------------------------------------------------------- 1 | # nconf 2 | 3 | [![Version npm](https://img.shields.io/npm/v/nconf.svg?style=flat-square)](https://www.npmjs.com/package/nconf)[![npm Downloads](https://img.shields.io/npm/dm/nconf.svg?style=flat-square)](https://www.npmjs.com/package/nconf)[![Build Status](https://img.shields.io/travis/indexzero/nconf/master.svg?style=flat-square)](https://travis-ci.org/indexzero/nconf)[![Coverage](https://img.shields.io/coveralls/indexzero/nconf.svg?style=flat-square)](https://coveralls.io/github/indexzero/nconf)[![Dependencies](https://img.shields.io/david/indexzero/nconf.svg?style=flat-square)](https://david-dm.org/indexzero/nconf) 4 | 5 | Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging. 6 | 7 | ## Example 8 | Using nconf is easy; it is designed to be a simple key-value store with support for both local and remote storage. Keys are namespaced and delimited by `:`. Let's dive right into sample usage: 9 | 10 | ``` js 11 | var fs = require('fs'), 12 | nconf = require('nconf'); 13 | 14 | // 15 | // Setup nconf to use (in-order): 16 | // 1. Command-line arguments 17 | // 2. Environment variables 18 | // 3. A file located at 'path/to/config.json' 19 | // 20 | nconf.argv() 21 | .env() 22 | .file({ file: 'path/to/config.json' }); 23 | 24 | // 25 | // Set a few variables on `nconf`. 26 | // 27 | nconf.set('database:host', '127.0.0.1'); 28 | nconf.set('database:port', 5984); 29 | 30 | // 31 | // Get the entire database object from nconf. This will output 32 | // { host: '127.0.0.1', port: 5984 } 33 | // 34 | console.log('foo: ' + nconf.get('foo')); 35 | console.log('NODE_ENV: ' + nconf.get('NODE_ENV')); 36 | console.log('database: ' + nconf.get('database')); 37 | 38 | // 39 | // Save the configuration object to disk 40 | // 41 | nconf.save(function (err) { 42 | fs.readFile('path/to/your/config.json', function (err, data) { 43 | console.dir(JSON.parse(data.toString())) 44 | }); 45 | }); 46 | ``` 47 | 48 | If you run the above script: 49 | 50 | ``` bash 51 | $ NODE_ENV=production sample.js --foo bar 52 | ``` 53 | 54 | The output will be: 55 | 56 | ``` 57 | foo: bar 58 | NODE_ENV: production 59 | database: { host: '127.0.0.1', port: 5984 } 60 | ``` 61 | 62 | ## Hierarchical configuration 63 | 64 | Configuration management can get complicated very quickly for even trivial applications running in production. `nconf` addresses this problem by enabling you to setup a hierarchy for different sources of configuration with no defaults. **The order in which you attach these configuration sources determines their priority in the hierarchy.** Let's take a look at the options available to you 65 | 66 | 1. **nconf.argv(options)** Loads `process.argv` using yargs. If `options` is supplied it is passed along to yargs. 67 | 2. **nconf.env(options)** Loads `process.env` into the hierarchy. 68 | 3. **nconf.file(options)** Loads the configuration data at options.file into the hierarchy. 69 | 4. **nconf.defaults(options)** Loads the data in options.store into the hierarchy. 70 | 5. **nconf.overrides(options)** Loads the data in options.store into the hierarchy. 71 | 72 | A sane default for this could be: 73 | 74 | ``` js 75 | var nconf = require('nconf'); 76 | 77 | // 78 | // 1. any overrides 79 | // 80 | nconf.overrides({ 81 | 'always': 'be this value' 82 | }); 83 | 84 | // 85 | // 2. `process.env` 86 | // 3. `process.argv` 87 | // 88 | nconf.env().argv(); 89 | 90 | // 91 | // 4. Values in `config.json` 92 | // 93 | nconf.file('/path/to/config.json'); 94 | 95 | // 96 | // Or with a custom name 97 | // Note: A custom key must be supplied for hierarchy to work if multiple files are used. 98 | // 99 | nconf.file('custom', '/path/to/config.json'); 100 | 101 | // 102 | // Or searching from a base directory. 103 | // Note: `name` is optional. 104 | // 105 | nconf.file(name, { 106 | file: 'config.json', 107 | dir: 'search/from/here', 108 | search: true 109 | }); 110 | 111 | // 112 | // 5. Any default values 113 | // 114 | nconf.defaults({ 115 | 'if nothing else': 'use this value' 116 | }); 117 | ``` 118 | 119 | ## API Documentation 120 | 121 | The top-level of `nconf` is an instance of the `nconf.Provider` abstracts this all for you into a simple API. 122 | 123 | ### nconf.add(name, options) 124 | Adds a new store with the specified `name` and `options`. If `options.type` is not set, then `name` will be used instead: 125 | 126 | ``` js 127 | nconf.add('supplied', { type: 'literal', store: { 'some': 'config' }); 128 | nconf.add('user', { type: 'file', file: '/path/to/userconf.json' }); 129 | nconf.add('global', { type: 'file', file: '/path/to/globalconf.json' }); 130 | ``` 131 | 132 | ### nconf.use(name, options) 133 | Similar to `nconf.add`, except that it can replace an existing store if new options are provided 134 | 135 | ``` js 136 | // 137 | // Load a file store onto nconf with the specified settings 138 | // 139 | nconf.use('file', { file: '/path/to/some/config-file.json' }); 140 | 141 | // 142 | // Replace the file store with new settings 143 | // 144 | nconf.use('file', { file: 'path/to/a-new/config-file.json' }); 145 | ``` 146 | 147 | ### nconf.remove(name) 148 | Removes the store with the specified `name.` The configuration stored at that level will no longer be used for lookup(s). 149 | 150 | ``` js 151 | nconf.remove('file'); 152 | ```y 153 | 154 | ### nconf.required(keys) 155 | Declares a set of string keys to be mandatory, and throw an error if any are missing. 156 | 157 | ```js 158 | nconf.defaults({ 159 | keya: 'a', 160 | }); 161 | 162 | nconf.required(['keya', 'keyb']); 163 | // Error: Missing required keys: keyb 164 | ``` 165 | 166 | ## Storage Engines 167 | 168 | ### Memory 169 | A simple in-memory storage engine that stores a nested JSON representation of the configuration. To use this engine, just call `.use()` with the appropriate arguments. All calls to `.get()`, `.set()`, `.clear()`, `.reset()` methods are synchronous since we are only dealing with an in-memory object. 170 | 171 | ``` js 172 | nconf.use('memory'); 173 | ``` 174 | 175 | ### Argv 176 | Responsible for loading the values parsed from `process.argv` by `yargs` into the configuration hierarchy. See the [yargs option docs](https://github.com/bcoe/yargs#optionskey-opt) for more on the option format. 177 | 178 | ``` js 179 | // 180 | // Can optionally also be an object literal to pass to `yargs`. 181 | // 182 | nconf.argv({ 183 | "x": { 184 | alias: 'example', 185 | describe: 'Example description for usage generation', 186 | demand: true, 187 | default: 'some-value' 188 | } 189 | }); 190 | ``` 191 | 192 | ### Env 193 | Responsible for loading the values parsed from `process.env` into the configuration hierarchy. 194 | 195 | ``` js 196 | // 197 | // Can optionally also be an Array of values to limit process.env to. 198 | // 199 | nconf.env(['only', 'load', 'these', 'values', 'from', 'process.env']); 200 | 201 | // 202 | // Can also specify a separator for nested keys (instead of the default ':') 203 | // 204 | nconf.env('__'); 205 | // Get the value of the env variable 'database__host' 206 | var dbHost = nconf.get('database:host'); 207 | 208 | // 209 | // Or use all options 210 | // 211 | nconf.env({ 212 | separator: '__', 213 | match: /^whatever_matches_this_will_be_whitelisted/ 214 | whitelist: ['database__host', 'only', 'load', 'these', 'values', 'if', 'whatever_doesnt_match_but_is_whitelisted_gets_loaded_too'] 215 | }); 216 | var dbHost = nconf.get('database:host'); 217 | ``` 218 | 219 | ### Literal 220 | Loads a given object literal into the configuration hierarchy. Both `nconf.defaults()` and `nconf.overrides()` use the Literal store. 221 | 222 | ``` js 223 | nconf.defaults({ 224 | 'some': 'default value' 225 | }); 226 | ``` 227 | 228 | ### File 229 | Based on the Memory store, but provides additional methods `.save()` and `.load()` which allow you to read your configuration to and from file. As with the Memory store, all method calls are synchronous with the exception of `.save()` and `.load()` which take callback functions. 230 | 231 | It is important to note that setting keys in the File engine will not be persisted to disk until a call to `.save()` is made. Note a custom key must be supplied as the first parameter for hierarchy to work if multiple files are used. 232 | 233 | ``` js 234 | nconf.file('path/to/your/config.json'); 235 | // add multiple files, hierarchically. notice the unique key for each file 236 | nconf.file('user', 'path/to/your/user.json'); 237 | nconf.file('global', 'path/to/your/global.json'); 238 | ``` 239 | 240 | The file store is also extensible for multiple file formats, defaulting to `JSON`. To use a custom format, simply pass a format object to the `.use()` method. This object must have `.parse()` and `.stringify()` methods just like the native `JSON` object. 241 | 242 | If the file does not exist at the provided path, the store will simply be empty. 243 | 244 | #### Encrypting file contents 245 | 246 | As of `nconf@0.8.0` it is now possible to encrypt and decrypt file contents using the `secure` option: 247 | 248 | ``` js 249 | nconf.file('secure-file', { 250 | file: 'path/to/secure-file.json', 251 | secure: { 252 | secret: 'super-secretzzz-keyzz', 253 | alg: 'aes-256-ctr' 254 | } 255 | }) 256 | ``` 257 | 258 | This will encrypt each key using [`crypto.createCipher`](https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password), defaulting to `aes-256-ctr`. The encrypted file contents will look like this: 259 | 260 | ``` 261 | { 262 | "config-key-name": { 263 | "alg": "aes-256-ctr", // cipher used 264 | "value": "af07fbcf" // encrypted contents 265 | }, 266 | "another-config-key": { 267 | "alg": "aes-256-ctr", // cipher used 268 | "value": "e310f6d94f13" // encrypted contents 269 | }, 270 | } 271 | ``` 272 | 273 | ### Redis 274 | There is a separate Redis-based store available through [nconf-redis][0]. To install and use this store simply: 275 | 276 | ``` bash 277 | $ npm install nconf 278 | $ npm install nconf-redis 279 | ``` 280 | 281 | Once installing both `nconf` and `nconf-redis`, you must require both modules to use the Redis store: 282 | 283 | ``` js 284 | var nconf = require('nconf'); 285 | 286 | // 287 | // Requiring `nconf-redis` will extend the `nconf` 288 | // module. 289 | // 290 | require('nconf-redis'); 291 | 292 | nconf.use('redis', { host: 'localhost', port: 6379, ttl: 60 * 60 * 1000 }); 293 | ``` 294 | 295 | ## Installation 296 | ``` 297 | npm install nconf --save 298 | ``` 299 | 300 | ## Run Tests 301 | Tests are written in vows and give complete coverage of all APIs and storage engines. 302 | 303 | ``` bash 304 | $ npm test 305 | ``` 306 | 307 | #### Author: [Charlie Robbins](http://nodejitsu.com) 308 | #### License: MIT 309 | 310 | [0]: http://github.com/indexzero/nconf-redis 311 | -------------------------------------------------------------------------------- /app/node_modules/nconf/lib/nconf/provider.js: -------------------------------------------------------------------------------- 1 | /* 2 | * provider.js: Abstraction providing an interface into pluggable configuration storage. 3 | * 4 | * (C) 2011, Charlie Robbins and the Contributors. 5 | * 6 | */ 7 | 8 | var async = require('async'), 9 | common = require('./common'); 10 | 11 | // 12 | // ### function Provider (options) 13 | // #### @options {Object} Options for this instance. 14 | // Constructor function for the Provider object responsible 15 | // for exposing the pluggable storage features of `nconf`. 16 | // 17 | var Provider = exports.Provider = function (options) { 18 | // 19 | // Setup default options for working with `stores`, 20 | // `overrides`, `process.env` and `process.argv`. 21 | // 22 | options = options || {}; 23 | this.stores = {}; 24 | this.sources = []; 25 | this.init(options); 26 | }; 27 | 28 | // 29 | // Define wrapper functions for using basic stores 30 | // in this instance 31 | // 32 | 33 | ['argv', 'env'].forEach(function (type) { 34 | Provider.prototype[type] = function () { 35 | var args = [type].concat(Array.prototype.slice.call(arguments)); 36 | return this.add.apply(this, args); 37 | }; 38 | }); 39 | 40 | // 41 | // ### function file (key, options) 42 | // #### @key {string|Object} Fully qualified options, name of file store, or path. 43 | // #### @path {string|Object} **Optional** Full qualified options, or path. 44 | // Adds a new `File` store to this instance. Accepts the following options 45 | // 46 | // nconf.file({ file: '.jitsuconf', dir: process.env.HOME, search: true }); 47 | // nconf.file('path/to/config/file'); 48 | // nconf.file('userconfig', 'path/to/config/file'); 49 | // nconf.file('userconfig', { file: '.jitsuconf', search: true }); 50 | // 51 | Provider.prototype.file = function (key, options) { 52 | if (arguments.length == 1) { 53 | options = typeof key === 'string' ? { file: key } : key; 54 | key = 'file'; 55 | } 56 | else { 57 | options = typeof options === 'string' 58 | ? { file: options } 59 | : options; 60 | } 61 | 62 | options.type = 'file'; 63 | return this.add(key, options); 64 | }; 65 | 66 | // 67 | // Define wrapper functions for using 68 | // overrides and defaults 69 | // 70 | ['defaults', 'overrides'].forEach(function (type) { 71 | Provider.prototype[type] = function (options) { 72 | options = options || {}; 73 | if (!options.type) { 74 | options.type = 'literal'; 75 | } 76 | 77 | return this.add(type, options); 78 | }; 79 | }); 80 | 81 | // 82 | // ### function use (name, options) 83 | // #### @type {string} Type of the nconf store to use. 84 | // #### @options {Object} Options for the store instance. 85 | // Adds (or replaces) a new store with the specified `name` 86 | // and `options`. If `options.type` is not set, then `name` 87 | // will be used instead: 88 | // 89 | // provider.use('file'); 90 | // provider.use('file', { type: 'file', filename: '/path/to/userconf' }) 91 | // 92 | Provider.prototype.use = function (name, options) { 93 | options = options || {}; 94 | var type = options.type || name; 95 | 96 | function sameOptions (store) { 97 | return Object.keys(options).every(function (key) { 98 | return options[key] === store[key]; 99 | }); 100 | } 101 | 102 | var store = this.stores[name], 103 | update = store && !sameOptions(store); 104 | 105 | if (!store || update) { 106 | if (update) { 107 | this.remove(name); 108 | } 109 | 110 | this.add(name, options); 111 | } 112 | 113 | return this; 114 | }; 115 | 116 | // 117 | // ### function add (name, options) 118 | // #### @name {string} Name of the store to add to this instance 119 | // #### @options {Object} Options for the store to create 120 | // Adds a new store with the specified `name` and `options`. If `options.type` 121 | // is not set, then `name` will be used instead: 122 | // 123 | // provider.add('memory'); 124 | // provider.add('userconf', { type: 'file', filename: '/path/to/userconf' }) 125 | // 126 | Provider.prototype.add = function (name, options, usage) { 127 | options = options || {}; 128 | var type = options.type || name; 129 | 130 | if (!require('../nconf')[common.capitalize(type)]) { 131 | throw new Error('Cannot add store with unknown type: ' + type); 132 | } 133 | 134 | this.stores[name] = this.create(type, options, usage); 135 | 136 | if (this.stores[name].loadSync) { 137 | this.stores[name].loadSync(); 138 | } 139 | 140 | return this; 141 | }; 142 | 143 | // 144 | // ### function remove (name) 145 | // #### @name {string} Name of the store to remove from this instance 146 | // Removes a store with the specified `name` from this instance. Users 147 | // are allowed to pass in a type argument (e.g. `memory`) as name if 148 | // this was used in the call to `.add()`. 149 | // 150 | Provider.prototype.remove = function (name) { 151 | delete this.stores[name]; 152 | return this; 153 | }; 154 | 155 | // 156 | // ### function create (type, options) 157 | // #### @type {string} Type of the nconf store to use. 158 | // #### @options {Object} Options for the store instance. 159 | // Creates a store of the specified `type` using the 160 | // specified `options`. 161 | // 162 | Provider.prototype.create = function (type, options, usage) { 163 | return new (require('../nconf')[common.capitalize(type.toLowerCase())])(options, usage); 164 | }; 165 | 166 | // 167 | // ### function init (options) 168 | // #### @options {Object} Options to initialize this instance with. 169 | // Initializes this instance with additional `stores` or `sources` in the 170 | // `options` supplied. 171 | // 172 | Provider.prototype.init = function (options) { 173 | var self = this; 174 | 175 | // 176 | // Add any stores passed in through the options 177 | // to this instance. 178 | // 179 | if (options.type) { 180 | this.add(options.type, options); 181 | } 182 | else if (options.store) { 183 | this.add(options.store.name || options.store.type, options.store); 184 | } 185 | else if (options.stores) { 186 | Object.keys(options.stores).forEach(function (name) { 187 | var store = options.stores[name]; 188 | self.add(store.name || name || store.type, store); 189 | }); 190 | } 191 | 192 | // 193 | // Add any read-only sources to this instance 194 | // 195 | if (options.source) { 196 | this.sources.push(this.create(options.source.type || options.source.name, options.source)); 197 | } 198 | else if (options.sources) { 199 | Object.keys(options.sources).forEach(function (name) { 200 | var source = options.sources[name]; 201 | self.sources.push(self.create(source.type || source.name || name, source)); 202 | }); 203 | } 204 | }; 205 | 206 | // 207 | // ### function get (key, callback) 208 | // #### @key {string} Key to retrieve for this instance. 209 | // #### @callback {function} **Optional** Continuation to respond to when complete. 210 | // Retrieves the value for the specified key (if any). 211 | // 212 | Provider.prototype.get = function (key, callback) { 213 | if (typeof key === 'function') { 214 | // Allow a * key call to be made 215 | callback = key; 216 | key = null; 217 | } 218 | 219 | // 220 | // If there is no callback we can short-circuit into the default 221 | // logic for traversing stores. 222 | // 223 | if (!callback) { 224 | return this._execute('get', 1, key, callback); 225 | } 226 | 227 | // 228 | // Otherwise the asynchronous, hierarchical `get` is 229 | // slightly more complicated because we do not need to traverse 230 | // the entire set of stores, but up until there is a defined value. 231 | // 232 | var current = 0, 233 | names = Object.keys(this.stores), 234 | self = this, 235 | response, 236 | mergeObjs = []; 237 | 238 | async.whilst(function () { 239 | return typeof response === 'undefined' && current < names.length; 240 | }, function (next) { 241 | var store = self.stores[names[current]]; 242 | current++; 243 | 244 | if (store.get.length >= 2) { 245 | return store.get(key, function (err, value) { 246 | if (err) { 247 | return next(err); 248 | } 249 | 250 | response = value; 251 | 252 | // Merge objects if necessary 253 | if (response && typeof response === 'object' && !Array.isArray(response)) { 254 | mergeObjs.push(response); 255 | response = undefined; 256 | } 257 | 258 | next(); 259 | }); 260 | } 261 | 262 | response = store.get(key); 263 | 264 | // Merge objects if necessary 265 | if (response && typeof response === 'object' && !Array.isArray(response)) { 266 | mergeObjs.push(response); 267 | response = undefined; 268 | } 269 | 270 | next(); 271 | }, function (err) { 272 | if (!err && mergeObjs.length) { 273 | response = common.merge(mergeObjs.reverse()); 274 | } 275 | return err ? callback(err) : callback(null, response); 276 | }); 277 | }; 278 | 279 | // 280 | // ### function set (key, value, callback) 281 | // #### @key {string} Key to set in this instance 282 | // #### @value {literal|Object} Value for the specified key 283 | // #### @callback {function} **Optional** Continuation to respond to when complete. 284 | // Sets the `value` for the specified `key` in this instance. 285 | // 286 | Provider.prototype.set = function (key, value, callback) { 287 | return this._execute('set', 2, key, value, callback); 288 | }; 289 | 290 | 291 | // 292 | // ### function required (keys) 293 | // #### @keys {array} List of keys 294 | // Throws an error if any of `keys` has no value, otherwise returns `true` 295 | Provider.prototype.required = function (keys) { 296 | if (!Array.isArray(keys)) { 297 | throw new Error('Incorrect parameter, array expected'); 298 | } 299 | 300 | var missing = []; 301 | keys.forEach(function(key) { 302 | if (typeof this.get(key) === 'undefined') { 303 | missing.push(key); 304 | } 305 | }, this); 306 | 307 | if (missing.length) { 308 | throw new Error('Missing required keys: ' + missing.join(', ')); 309 | } else { 310 | return true; 311 | } 312 | 313 | }; 314 | 315 | // 316 | // ### function reset (callback) 317 | // #### @callback {function} **Optional** Continuation to respond to when complete. 318 | // Clears all keys associated with this instance. 319 | // 320 | Provider.prototype.reset = function (callback) { 321 | return this._execute('reset', 0, callback); 322 | }; 323 | 324 | // 325 | // ### function clear (key, callback) 326 | // #### @key {string} Key to remove from this instance 327 | // #### @callback {function} **Optional** Continuation to respond to when complete. 328 | // Removes the value for the specified `key` from this instance. 329 | // 330 | Provider.prototype.clear = function (key, callback) { 331 | return this._execute('clear', 1, key, callback); 332 | }; 333 | 334 | // 335 | // ### function merge ([key,] value [, callback]) 336 | // #### @key {string} Key to merge the value into 337 | // #### @value {literal|Object} Value to merge into the key 338 | // #### @callback {function} **Optional** Continuation to respond to when complete. 339 | // Merges the properties in `value` into the existing object value at `key`. 340 | // 341 | // 1. If the existing value `key` is not an Object, it will be completely overwritten. 342 | // 2. If `key` is not supplied, then the `value` will be merged into the root. 343 | // 344 | Provider.prototype.merge = function () { 345 | var self = this, 346 | args = Array.prototype.slice.call(arguments), 347 | callback = typeof args[args.length - 1] === 'function' && args.pop(), 348 | value = args.pop(), 349 | key = args.pop(); 350 | 351 | function mergeProperty (prop, next) { 352 | return self._execute('merge', 2, prop, value[prop], next); 353 | } 354 | 355 | if (!key) { 356 | if (Array.isArray(value) || typeof value !== 'object') { 357 | return onError(new Error('Cannot merge non-Object into top-level.'), callback); 358 | } 359 | 360 | return async.forEach(Object.keys(value), mergeProperty, callback || function () { }) 361 | } 362 | 363 | return this._execute('merge', 2, key, value, callback); 364 | }; 365 | 366 | // 367 | // ### function load (callback) 368 | // #### @callback {function} Continuation to respond to when complete. 369 | // Responds with an Object representing all keys associated in this instance. 370 | // 371 | Provider.prototype.load = function (callback) { 372 | var self = this; 373 | 374 | function getStores () { 375 | var stores = Object.keys(self.stores); 376 | stores.reverse(); 377 | return stores.map(function (name) { 378 | return self.stores[name]; 379 | }); 380 | } 381 | 382 | function loadStoreSync(store) { 383 | if (!store.loadSync) { 384 | throw new Error('nconf store ' + store.type + ' has no loadSync() method'); 385 | } 386 | 387 | return store.loadSync(); 388 | } 389 | 390 | function loadStore(store, next) { 391 | if (!store.load && !store.loadSync) { 392 | return next(new Error('nconf store ' + store.type + ' has no load() method')); 393 | } 394 | 395 | return store.loadSync 396 | ? next(null, store.loadSync()) 397 | : store.load(next); 398 | } 399 | 400 | function loadBatch (targets, done) { 401 | if (!done) { 402 | return common.merge(targets.map(loadStoreSync)); 403 | } 404 | 405 | async.map(targets, loadStore, function (err, objs) { 406 | return err ? done(err) : done(null, common.merge(objs)); 407 | }); 408 | } 409 | 410 | function mergeSources (data) { 411 | // 412 | // If `data` was returned then merge it into 413 | // the system store. 414 | // 415 | if (data && typeof data === 'object') { 416 | self.use('sources', { 417 | type: 'literal', 418 | store: data 419 | }); 420 | } 421 | } 422 | 423 | function loadSources () { 424 | var sourceHierarchy = self.sources.splice(0); 425 | sourceHierarchy.reverse(); 426 | 427 | // 428 | // If we don't have a callback and the current 429 | // store is capable of loading synchronously 430 | // then do so. 431 | // 432 | if (!callback) { 433 | mergeSources(loadBatch(sourceHierarchy)); 434 | return loadBatch(getStores()); 435 | } 436 | 437 | loadBatch(sourceHierarchy, function (err, data) { 438 | if (err) { 439 | return callback(err); 440 | } 441 | 442 | mergeSources(data); 443 | return loadBatch(getStores(), callback); 444 | }); 445 | } 446 | 447 | return self.sources.length 448 | ? loadSources() 449 | : loadBatch(getStores(), callback); 450 | }; 451 | 452 | // 453 | // ### function save (callback) 454 | // #### @callback {function} **optional** Continuation to respond to when 455 | // complete. 456 | // Instructs each provider to save. If a callback is provided, we will attempt 457 | // asynchronous saves on the providers, falling back to synchronous saves if 458 | // this isn't possible. If a provider does not know how to save, it will be 459 | // ignored. Returns an object consisting of all of the data which was 460 | // actually saved. 461 | // 462 | Provider.prototype.save = function (value, callback) { 463 | if (!callback && typeof value === 'function') { 464 | callback = value; 465 | value = null; 466 | } 467 | 468 | var self = this, 469 | names = Object.keys(this.stores); 470 | 471 | function saveStoreSync(memo, name) { 472 | var store = self.stores[name]; 473 | 474 | // 475 | // If the `store` doesn't have a `saveSync` method, 476 | // just ignore it and continue. 477 | // 478 | if (store.saveSync) { 479 | var ret = store.saveSync(); 480 | if (typeof ret == 'object' && ret !== null) { 481 | memo.push(ret); 482 | } 483 | } 484 | return memo; 485 | } 486 | 487 | function saveStore(memo, name, next) { 488 | var store = self.stores[name]; 489 | 490 | // 491 | // If the `store` doesn't have a `save` or saveSync` 492 | // method(s), just ignore it and continue. 493 | // 494 | 495 | if (store.save) { 496 | return store.save(value, function (err, data) { 497 | if (err) { 498 | return next(err); 499 | } 500 | 501 | if (typeof data == 'object' && data !== null) { 502 | memo.push(data); 503 | } 504 | 505 | next(null, memo); 506 | }); 507 | } 508 | else if (store.saveSync) { 509 | memo.push(store.saveSync()); 510 | } 511 | 512 | next(null, memo); 513 | } 514 | 515 | // 516 | // If we don't have a callback and the current 517 | // store is capable of saving synchronously 518 | // then do so. 519 | // 520 | if (!callback) { 521 | return common.merge(names.reduce(saveStoreSync, [])); 522 | } 523 | 524 | async.reduce(names, [], saveStore, function (err, objs) { 525 | return err ? callback(err) : callback(null, common.merge(objs)); 526 | }); 527 | }; 528 | 529 | // 530 | // ### @private function _execute (action, syncLength, [arguments]) 531 | // #### @action {string} Action to execute on `this.store`. 532 | // #### @syncLength {number} Function length of the sync version. 533 | // #### @arguments {Array} Arguments array to apply to the action 534 | // Executes the specified `action` on all stores for this instance, ensuring a callback supplied 535 | // to a synchronous store function is still invoked. 536 | // 537 | Provider.prototype._execute = function (action, syncLength /* [arguments] */) { 538 | var args = Array.prototype.slice.call(arguments, 2), 539 | callback = typeof args[args.length - 1] === 'function' && args.pop(), 540 | destructive = ['set', 'clear', 'merge', 'reset'].indexOf(action) !== -1, 541 | self = this, 542 | response, 543 | mergeObjs = [], 544 | keys = Object.keys(this.stores); 545 | 546 | 547 | function runAction (name, next) { 548 | var store = self.stores[name]; 549 | 550 | if (destructive && store.readOnly) { 551 | return next(); 552 | } 553 | 554 | return store[action].length > syncLength 555 | ? store[action].apply(store, args.concat(next)) 556 | : next(null, store[action].apply(store, args)); 557 | } 558 | 559 | if (callback) { 560 | return async.forEach(keys, runAction, function (err) { 561 | return err ? callback(err) : callback(); 562 | }); 563 | } 564 | 565 | keys.forEach(function (name) { 566 | if (typeof response === 'undefined') { 567 | var store = self.stores[name]; 568 | 569 | if (destructive && store.readOnly) { 570 | return; 571 | } 572 | 573 | response = store[action].apply(store, args); 574 | 575 | // Merge objects if necessary 576 | if (response && action === 'get' && typeof response === 'object' && !Array.isArray(response)) { 577 | mergeObjs.push(response); 578 | response = undefined; 579 | } 580 | } 581 | }); 582 | 583 | if (mergeObjs.length) { 584 | response = common.merge(mergeObjs.reverse()); 585 | } 586 | 587 | return response; 588 | } 589 | 590 | // 591 | // Throw the `err` if a callback is not supplied 592 | // 593 | function onError(err, callback) { 594 | if (callback) { 595 | return callback(err); 596 | } 597 | 598 | throw err; 599 | } 600 | --------------------------------------------------------------------------------