├── README.md ├── images ├── bg_hr.png ├── blacktocat.png ├── icon_download.png └── sprite_download.png ├── index.html ├── javascripts └── main.js ├── lib └── jsonExtender.js ├── package.json ├── params.json └── stylesheets ├── github-light.css └── stylesheet.css /README.md: -------------------------------------------------------------------------------- 1 | # json-server-extension 2 | 3 | json-server is great for stub server usage 4 | but in my opinion there where some caveat that i tried to solve in this package 5 | 6 | ### so what this package gives you 7 | - [x] **splitting to static files -** json-server can serve only single file but in medium/large applications it not ideal, by using this package you can split your json object to files 8 | - [x] **dynamic generation -** with json server you can generate the whole file 9 | now you can create multiple generated objects decoupled each other and even combine 10 | static and generated files 11 | 12 | ## Example 13 | full example can be found here https://github.com/maty21/json-server-extension-example 14 | ## Install 15 | `` npm i json-server-extension `` 16 | ### init example 17 | ```js 18 | const jsonServer = require('json-server'); 19 | const _jsonExtender = require('./jsonExtender'); 20 | 21 | //options: 22 | //fullPath:fullpath for the combined object 23 | //generatedPath:the path where the generated files will be found 24 | //staticPath:the path where the static files will be found 25 | const jsonExtender = new _jsonExtender({filePath:'./db_extends.json', 26 | generatedPath:'./generated', 27 | staticPath:'./static'}) 28 | 29 | //register accept array of generators or path to the generator scripts 30 | //const funcs = Object.keys(generators).map(key => generators[key]) 31 | jsonExtender.register('../../../generators'); 32 | jsonExtender.generate().then((data)=>{ 33 | console.log(`wow ${data}`); 34 | var server = jsonServer.create() 35 | var router = jsonServer.router('./db_extends.json') 36 | var middlewares = jsonServer.defaults() 37 | 38 | server.use(middlewares) 39 | server.use(router) 40 | server.listen(4000, function () { 41 | console.log('JSON Server is running') 42 | }).catch((err) => {console.log(err)}) 43 | 44 | }); 45 | ``` 46 | ### generator Example 47 | 48 | ```js 49 | const amount = 100; 50 | const func =next =>create => { 51 | const path = `feed/feedList.json`; 52 | const data = (amount)=> { 53 | let temp = []; 54 | for (let i = 0; i < amount; i++) { 55 | temp.push({ 56 | id: `${i}N12134`, 57 | newNotificationCount: i * 3, 58 | isRead: (i % 2 == 0), 59 | isStarMark: (i % 4 == 0), 60 | iconType: "SocialNotifications", 61 | description: i + ": this is a new feed ", 62 | date: new Date(Date.now()).toLocaleString() 63 | } 64 | ) 65 | } 66 | return temp; 67 | } 68 | create({data: {feed: data(amount)}, path: path}) 69 | next(create); 70 | 71 | } 72 | module.exports = func; 73 | ``` 74 | 75 | 76 | ## api 77 | 78 | #### constructor 79 | ``constructor({filePath:'string',generatedPath:'string, staticPath:'string'}) `` 80 | - ``fullPath``- fullpath for the combined object 81 | - ``generatedPath``- the path where the generated files will be found ``default 82 | : './generated'`` 83 | - ``staticPath``- the path where the static files will be found ``default 84 | : './static'`` 85 | 86 | #### register 87 | ``register('path name') / register([...generator scripts]) `` 88 | - ``register('path name')`` - a path where the generators scripts will be found the package will insatiate the scripts automatically 89 | - ``register([...generator scripts])`` -array of your generators after requiring them manually 90 | 91 | #### generate 92 | ``generate(isRun[default:true]) return promise`` 93 | - ``isRun`` - there is ability to not generate the db.json each time good when you want to save the state after you close the process the promise will recive the same data so you will not have to change the code 94 | - ``promise`` 95 | - ``resolve`` -{files:array of combined files, filePath:the combined file path } 96 | - ``reject``- error 97 | 98 | ### generator 99 | 100 | ``` const func= next =>create => {}``` - the generator should be initiated as follows first you will have to call for create this is sync function and the for next 101 | - ``create({data: {feed: generatedObject}, path: path})`` 102 | - ``data`` - the generated data where the name of the property will be the routing name in this case ``feed`` 103 | - ``path`` - a relative path under the generated path that you set in the constructor where you wish to put the output 104 | - ``next(create)`` - just pass the create function there so it's reference will be passed in the pipeline 105 | -------------------------------------------------------------------------------- /images/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maty21/json-server-extension/7be5fe21e1faefbfddad10da8a2abc940e3a2c9e/images/bg_hr.png -------------------------------------------------------------------------------- /images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maty21/json-server-extension/7be5fe21e1faefbfddad10da8a2abc940e3a2c9e/images/blacktocat.png -------------------------------------------------------------------------------- /images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maty21/json-server-extension/7be5fe21e1faefbfddad10da8a2abc940e3a2c9e/images/icon_download.png -------------------------------------------------------------------------------- /images/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maty21/json-server-extension/7be5fe21e1faefbfddad10da8a2abc940e3a2c9e/images/sprite_download.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Json-server-extension 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | View on GitHub 20 | 21 |

Json-server-extension

22 |

nice additions for json-server in order to support large scale applications

23 | 24 |
25 | Download this project as a .zip file 26 | Download this project as a tar.gz file 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

35 | json-server-extension

36 | 37 |

json-server is great for stub server usage 38 | but in my opinion there where some caveat that i tried to solve in this package

39 | 40 |

41 | so what this package gives you

42 | 43 | 49 | 50 |

51 | Example

52 | 53 |

full example can be found here https://github.com/maty21/json-server-extension-example

54 | 55 |

56 | init example

57 | 58 |
const jsonServer = require('json-server');
 59 | const _jsonExtender = require('./jsonExtender');
 60 | 
 61 | //options:
 62 | //fullPath:fullpath for the combined object
 63 | //generatedPath:the path where the generated files will be found
 64 | //staticPath:the path where the static files will be found
 65 | const jsonExtender = new _jsonExtender({filePath:'./db_extends.json',
 66 |                                         generatedPath:'./generated',
 67 |                                         staticPath:'./static'})
 68 | 
 69 | //register accept array of generators or path to the generator scripts
 70 | //const funcs =  Object.keys(generators).map(key => generators[key])
 71 | jsonExtender.register('../../../generators');
 72 | jsonExtender.generate().then((data)=>{
 73 |   console.log(`wow ${data}`);
 74 |   var server = jsonServer.create()
 75 |   var router = jsonServer.router('./db_extends.json')
 76 |   var middlewares = jsonServer.defaults()
 77 | 
 78 |   server.use(middlewares)
 79 |   server.use(router)
 80 |   server.listen(4000, function () {
 81 |     console.log('JSON Server is running')
 82 |   }).catch((err) => {console.log(err)})
 83 | 
 84 | });
85 | 86 |

87 | generator Example

88 | 89 |
const amount = 100;
 90 |  const func =next =>create => {
 91 |     const path = `feed/feedList.json`;
 92 |     const data = (amount)=> {
 93 |         let temp = [];
 94 |         for (let i = 0; i < amount; i++) {
 95 |             temp.push({
 96 |                     id: `${i}N12134`,
 97 |                     newNotificationCount: i * 3,
 98 |                     isRead: (i % 2 == 0),
 99 |                     isStarMark: (i % 4 == 0),
100 |                     iconType: "SocialNotifications",
101 |                     description: i + ": this is a new  feed ",
102 |                     date: new Date(Date.now()).toLocaleString()
103 |                 }
104 |             )
105 |         }
106 |         return temp;
107 |     }
108 |     create({data: {feed: data(amount)}, path: path})
109 |     next(create);
110 | 
111 | }
112 | module.exports = func;
113 | 114 |

115 | api

116 | 117 |

118 | constructor

119 | 120 |

constructor({filePath:'string',generatedPath:'string, staticPath:'string'})

121 | 122 | 134 | 135 |

136 | register

137 | 138 |

register('path name') / register([...generator scripts])

139 | 140 | 146 | 147 |

148 | generate

149 | 150 |

generate(isRun[default:true]) return promise

151 | 152 | 166 | 167 |

168 | generator

169 | 170 |

const func= next =>create => {} - the generator should be initiated as follows first you will have to call for create this is sync function and the for next

171 | 172 | 187 |
188 |
189 | 190 | 191 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /lib/jsonExtender.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const jsonData = './db.json' 4 | const jsonConcat = require("json-concat"); 5 | const Finder = require('fs-finder'); 6 | const fsExtra = require('fs-extra'); 7 | const requireDir = require('require-dir'); 8 | const colors = require('colors'); 9 | class jsonExtender { 10 | constructor(options) { 11 | const base = __dirname; 12 | this.generatedPath = options.generatedPath || `${base}/generated` 13 | this.filePath = options.filePath; 14 | this.staticPath =options.staticPath||`${base}/static` ; 15 | this.filesToGenerate = []; 16 | this.startingPoint = null; 17 | this.outputs = []; 18 | this.promise = new Promise((resolve,reject)=>{ 19 | this.resolve =resolve; 20 | }); 21 | } 22 | 23 | register(params) { 24 | if(params instanceof Array ){ 25 | this.filesToGenerate.push(...params); 26 | } 27 | else if ( typeof params == 'string' ){ 28 | let requires = requireDir(params); 29 | let funcs = Object.keys(requires).map(key => requires[key]) ; 30 | this.filesToGenerate.push(...funcs); 31 | } 32 | } 33 | 34 | generate(isRun=true) { 35 | if(!isRun){ 36 | return new Promise((resolve,reject)=>{ 37 | return resolve({files:'',filePath:`${this.filePath}`}); 38 | }) 39 | } 40 | return new Promise((resolve,reject)=>{ 41 | this.status = 'success'; 42 | let prevFunc = this.extend.bind(this); 43 | this.filesToGenerate.reverse().map(func=> { 44 | this.startingPoint = prevFunc = func(prevFunc); 45 | 46 | }) 47 | this.startingPoint(this.createJson.bind(this)); 48 | this.promise.then(data=> resolve(data)).catch((e)=>reject(e)); 49 | 50 | }) 51 | 52 | } 53 | 54 | createJson(object) { 55 | let absolutePath = `${this.generatedPath}/${object.path}`; 56 | fsExtra.outputJSONSync(absolutePath, object.data, null, (err) => { 57 | console.log(err) // => null 58 | 59 | }) 60 | 61 | } 62 | 63 | 64 | extend(arr) { 65 | 66 | let generatedFiles = Finder.from(`${this.generatedPath}`).findFiles('*.json'); 67 | let staticFiles = Finder.from(`${this.staticPath}`).findFiles('*.json'); 68 | let files = [...staticFiles, 69 | ...generatedFiles] 70 | jsonConcat({ 71 | src: [ 72 | jsonData, 73 | ...files 74 | ], 75 | dest: this.filePath 76 | }, (json)=> { 77 | console.log('___________________________________________________________________________________________'.green) 78 | console.log(`finished successfuly`.green) 79 | console.log(`the files created and combined:`.green) 80 | let counter = 1; 81 | files.map((file)=>{ 82 | console.log(` ${counter}) ${file}`.green) 83 | counter++; 84 | }) 85 | console.log(`the result saved to ${this.filePath} `.green) 86 | console.log('___________________________________________________________________________________________'.green) 87 | this.resolve({files:`${files}`,filePath:`${this.filePath}`}); 88 | }); 89 | 90 | } 91 | 92 | } 93 | 94 | module.exports = jsonExtender; 95 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-server-extension", 3 | "version": "1.1.2", 4 | "description": "nice additions for json-server in order to support large scale applications", 5 | "main": "lib/jsonExtender.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": ["json","json-server","stub","combine","stub server"], 10 | "author": "maty zisserman", 11 | "license": "MIT", 12 | "dependencies": { 13 | "colors": "^1.1.2", 14 | "fs-extra": "^1.0.0", 15 | "fs-finder": "^1.8.1", 16 | "json-concat": "0.0.1", 17 | "json-server": "^0.9.0", 18 | "require-dir": "^0.3.1" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/maty21/json-server-extension.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/maty21/json-server-extension/issues" 26 | }, 27 | "homepage": "https://github.com/maty21/json-server-extension#readme" 28 | } 29 | -------------------------------------------------------------------------------- /params.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Json-server-extension", 3 | "tagline": "nice additions for json-server in order to support large scale applications", 4 | "body": "# json-server-extension\r\n\r\njson-server is great for stub server usage\r\nbut in my opinion there where some caveat that i tried to solve in this package\r\n\r\n### so what this package gives you\r\n- [x] **splitting to static files -** json-server can serve only single file but in medium/large applications it not ideal, by using this package you can split your json object to files\r\n- [x] **dynamic generation -** with json server you can generate the whole file\r\n now you can create multiple generated objects decoupled each other and even combine\r\n static and generated files\r\n\r\n## Example\r\nfull example can be found here https://github.com/maty21/json-server-extension-example\r\n### init example\r\n```js\r\nconst jsonServer = require('json-server');\r\nconst _jsonExtender = require('./jsonExtender');\r\n\r\n//options:\r\n//fullPath:fullpath for the combined object\r\n//generatedPath:the path where the generated files will be found\r\n//staticPath:the path where the static files will be found\r\nconst jsonExtender = new _jsonExtender({filePath:'./db_extends.json',\r\n generatedPath:'./generated',\r\n staticPath:'./static'})\r\n\r\n//register accept array of generators or path to the generator scripts\r\n//const funcs = Object.keys(generators).map(key => generators[key])\r\njsonExtender.register('../../../generators');\r\njsonExtender.generate().then((data)=>{\r\n console.log(`wow ${data}`);\r\n var server = jsonServer.create()\r\n var router = jsonServer.router('./db_extends.json')\r\n var middlewares = jsonServer.defaults()\r\n\r\n server.use(middlewares)\r\n server.use(router)\r\n server.listen(4000, function () {\r\n console.log('JSON Server is running')\r\n }).catch((err) => {console.log(err)})\r\n\r\n});\r\n```\r\n### generator Example\r\n\r\n```js\r\nconst amount = 100;\r\n const func =next =>create => {\r\n const path = `feed/feedList.json`;\r\n const data = (amount)=> {\r\n let temp = [];\r\n for (let i = 0; i < amount; i++) {\r\n temp.push({\r\n id: `${i}N12134`,\r\n newNotificationCount: i * 3,\r\n isRead: (i % 2 == 0),\r\n isStarMark: (i % 4 == 0),\r\n iconType: \"SocialNotifications\",\r\n description: i + \": this is a new feed \",\r\n date: new Date(Date.now()).toLocaleString()\r\n }\r\n )\r\n }\r\n return temp;\r\n }\r\n create({data: {feed: data(amount)}, path: path})\r\n next(create);\r\n\r\n}\r\nmodule.exports = func;\r\n```\r\n\r\n\r\n## api\r\n\r\n#### constructor\r\n``constructor({filePath:'string',generatedPath:'string, staticPath:'string'}) ``\r\n- ``fullPath``- fullpath for the combined object\r\n- ``generatedPath``- the path where the generated files will be found ``default\r\n: './generated'``\r\n- ``staticPath``- the path where the static files will be found ``default\r\n: './static'``\r\n\r\n#### register\r\n``register('path name') / register([...generator scripts]) ``\r\n- ``register('path name')`` - a path where the generators scripts will be found the package will insatiate the scripts automatically\r\n- ``register([...generator scripts])`` -array of your generators after requiring them manually\r\n\r\n#### generate\r\n``generate(isRun[default:true]) return promise``\r\n- ``isRun`` - there is ability to not generate the db.json each time good when you want to save the state after you close the process the promise will recive the same data so you will not have to change the code\r\n- ``promise``\r\n - ``resolve`` -{files:array of combined files, filePath:the combined file path }\r\n - ``reject``- error\r\n\r\n### generator\r\n\r\n``` const func= next =>create => {}``` - the generator should be initiated as follows first you will have to call for create this is sync function and the for next\r\n- ``create({data: {feed: generatedObject}, path: path})``\r\n - ``data`` - the generated data where the name of the property will be the routing name in this case ``feed``\r\n - ``path`` - a relative path under the generated path that you set in the constructor where you wish to put the output\r\n- ``next(create)`` - just pass the create function there so it's reference will be passed in the pipeline\r\n", 5 | "note": "Don't delete this file! It's used internally to help with page regeneration." 6 | } -------------------------------------------------------------------------------- /stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 GitHub, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | .pl-c /* comment */ { 27 | color: #969896; 28 | } 29 | 30 | .pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */, 31 | .pl-s .pl-v /* string variable */ { 32 | color: #0086b3; 33 | } 34 | 35 | .pl-e /* entity */, 36 | .pl-en /* entity.name */ { 37 | color: #795da3; 38 | } 39 | 40 | .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, 41 | .pl-s .pl-s1 /* string source */ { 42 | color: #333; 43 | } 44 | 45 | .pl-ent /* entity.name.tag */ { 46 | color: #63a35c; 47 | } 48 | 49 | .pl-k /* keyword, storage, storage.type */ { 50 | color: #a71d5d; 51 | } 52 | 53 | .pl-s /* string */, 54 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 56 | .pl-sr /* string.regexp */, 57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 58 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */, 59 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { 60 | color: #183691; 61 | } 62 | 63 | .pl-v /* variable */ { 64 | color: #ed6a43; 65 | } 66 | 67 | .pl-id /* invalid.deprecated */ { 68 | color: #b52a1d; 69 | } 70 | 71 | .pl-ii /* invalid.illegal */ { 72 | color: #f8f8f8; 73 | background-color: #b52a1d; 74 | } 75 | 76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 77 | font-weight: bold; 78 | color: #63a35c; 79 | } 80 | 81 | .pl-ml /* markup.list */ { 82 | color: #693a17; 83 | } 84 | 85 | .pl-mh /* markup.heading */, 86 | .pl-mh .pl-en /* markup.heading entity.name */, 87 | .pl-ms /* meta.separator */ { 88 | font-weight: bold; 89 | color: #1d3e81; 90 | } 91 | 92 | .pl-mq /* markup.quote */ { 93 | color: #008080; 94 | } 95 | 96 | .pl-mi /* markup.italic */ { 97 | font-style: italic; 98 | color: #333; 99 | } 100 | 101 | .pl-mb /* markup.bold */ { 102 | font-weight: bold; 103 | color: #333; 104 | } 105 | 106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 107 | color: #bd2c00; 108 | background-color: #ffecec; 109 | } 110 | 111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 112 | color: #55a532; 113 | background-color: #eaffea; 114 | } 115 | 116 | .pl-mdr /* meta.diff.range */ { 117 | font-weight: bold; 118 | color: #795da3; 119 | } 120 | 121 | .pl-mo /* meta.output */ { 122 | color: #1d3e81; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for GitHub Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(github-light.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | 47 | /******************************************************************************* 48 | Theme Styles 49 | *******************************************************************************/ 50 | 51 | body { 52 | box-sizing: border-box; 53 | color:#373737; 54 | background: #212121; 55 | font-size: 16px; 56 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 57 | line-height: 1.5; 58 | -webkit-font-smoothing: antialiased; 59 | } 60 | 61 | h1, h2, h3, h4, h5, h6 { 62 | margin: 10px 0; 63 | font-weight: 700; 64 | color:#222222; 65 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 66 | letter-spacing: -1px; 67 | } 68 | 69 | h1 { 70 | font-size: 36px; 71 | font-weight: 700; 72 | } 73 | 74 | h2 { 75 | padding-bottom: 10px; 76 | font-size: 32px; 77 | background: url('../images/bg_hr.png') repeat-x bottom; 78 | } 79 | 80 | h3 { 81 | font-size: 24px; 82 | } 83 | 84 | h4 { 85 | font-size: 21px; 86 | } 87 | 88 | h5 { 89 | font-size: 18px; 90 | } 91 | 92 | h6 { 93 | font-size: 16px; 94 | } 95 | 96 | p { 97 | margin: 10px 0 15px 0; 98 | } 99 | 100 | footer p { 101 | color: #f2f2f2; 102 | } 103 | 104 | a { 105 | text-decoration: none; 106 | color: #007edf; 107 | text-shadow: none; 108 | 109 | transition: color 0.5s ease; 110 | transition: text-shadow 0.5s ease; 111 | -webkit-transition: color 0.5s ease; 112 | -webkit-transition: text-shadow 0.5s ease; 113 | -moz-transition: color 0.5s ease; 114 | -moz-transition: text-shadow 0.5s ease; 115 | -o-transition: color 0.5s ease; 116 | -o-transition: text-shadow 0.5s ease; 117 | -ms-transition: color 0.5s ease; 118 | -ms-transition: text-shadow 0.5s ease; 119 | } 120 | 121 | a:hover, a:focus {text-decoration: underline;} 122 | 123 | footer a { 124 | color: #F2F2F2; 125 | text-decoration: underline; 126 | } 127 | 128 | em { 129 | font-style: italic; 130 | } 131 | 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | img { 137 | position: relative; 138 | margin: 0 auto; 139 | max-width: 739px; 140 | padding: 5px; 141 | margin: 10px 0 10px 0; 142 | border: 1px solid #ebebeb; 143 | 144 | box-shadow: 0 0 5px #ebebeb; 145 | -webkit-box-shadow: 0 0 5px #ebebeb; 146 | -moz-box-shadow: 0 0 5px #ebebeb; 147 | -o-box-shadow: 0 0 5px #ebebeb; 148 | -ms-box-shadow: 0 0 5px #ebebeb; 149 | } 150 | 151 | p img { 152 | display: inline; 153 | margin: 0; 154 | padding: 0; 155 | vertical-align: middle; 156 | text-align: center; 157 | border: none; 158 | } 159 | 160 | pre, code { 161 | width: 100%; 162 | color: #222; 163 | background-color: #fff; 164 | 165 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 166 | font-size: 14px; 167 | 168 | border-radius: 2px; 169 | -moz-border-radius: 2px; 170 | -webkit-border-radius: 2px; 171 | } 172 | 173 | pre { 174 | width: 100%; 175 | padding: 10px; 176 | box-shadow: 0 0 10px rgba(0,0,0,.1); 177 | overflow: auto; 178 | } 179 | 180 | code { 181 | padding: 3px; 182 | margin: 0 3px; 183 | box-shadow: 0 0 10px rgba(0,0,0,.1); 184 | } 185 | 186 | pre code { 187 | display: block; 188 | box-shadow: none; 189 | } 190 | 191 | blockquote { 192 | color: #666; 193 | margin-bottom: 20px; 194 | padding: 0 0 0 20px; 195 | border-left: 3px solid #bbb; 196 | } 197 | 198 | 199 | ul, ol, dl { 200 | margin-bottom: 15px 201 | } 202 | 203 | ul { 204 | list-style-position: inside; 205 | list-style: disc; 206 | padding-left: 20px; 207 | } 208 | 209 | ol { 210 | list-style-position: inside; 211 | list-style: decimal; 212 | padding-left: 20px; 213 | } 214 | 215 | dl dt { 216 | font-weight: bold; 217 | } 218 | 219 | dl dd { 220 | padding-left: 20px; 221 | font-style: italic; 222 | } 223 | 224 | dl p { 225 | padding-left: 20px; 226 | font-style: italic; 227 | } 228 | 229 | hr { 230 | height: 1px; 231 | margin-bottom: 5px; 232 | border: none; 233 | background: url('../images/bg_hr.png') repeat-x center; 234 | } 235 | 236 | table { 237 | border: 1px solid #373737; 238 | margin-bottom: 20px; 239 | text-align: left; 240 | } 241 | 242 | th { 243 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 244 | padding: 10px; 245 | background: #373737; 246 | color: #fff; 247 | } 248 | 249 | td { 250 | padding: 10px; 251 | border: 1px solid #373737; 252 | } 253 | 254 | form { 255 | background: #f2f2f2; 256 | padding: 20px; 257 | } 258 | 259 | /******************************************************************************* 260 | Full-Width Styles 261 | *******************************************************************************/ 262 | 263 | .outer { 264 | width: 100%; 265 | } 266 | 267 | .inner { 268 | position: relative; 269 | max-width: 640px; 270 | padding: 20px 10px; 271 | margin: 0 auto; 272 | } 273 | 274 | #forkme_banner { 275 | display: block; 276 | position: absolute; 277 | top:0; 278 | right: 10px; 279 | z-index: 10; 280 | padding: 10px 50px 10px 10px; 281 | color: #fff; 282 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; 283 | font-weight: 700; 284 | box-shadow: 0 0 10px rgba(0,0,0,.5); 285 | border-bottom-left-radius: 2px; 286 | border-bottom-right-radius: 2px; 287 | } 288 | 289 | #header_wrap { 290 | background: #212121; 291 | background: -moz-linear-gradient(top, #373737, #212121); 292 | background: -webkit-linear-gradient(top, #373737, #212121); 293 | background: -ms-linear-gradient(top, #373737, #212121); 294 | background: -o-linear-gradient(top, #373737, #212121); 295 | background: linear-gradient(top, #373737, #212121); 296 | } 297 | 298 | #header_wrap .inner { 299 | padding: 50px 10px 30px 10px; 300 | } 301 | 302 | #project_title { 303 | margin: 0; 304 | color: #fff; 305 | font-size: 42px; 306 | font-weight: 700; 307 | text-shadow: #111 0px 0px 10px; 308 | } 309 | 310 | #project_tagline { 311 | color: #fff; 312 | font-size: 24px; 313 | font-weight: 300; 314 | background: none; 315 | text-shadow: #111 0px 0px 10px; 316 | } 317 | 318 | #downloads { 319 | position: absolute; 320 | width: 210px; 321 | z-index: 10; 322 | bottom: -40px; 323 | right: 0; 324 | height: 70px; 325 | background: url('../images/icon_download.png') no-repeat 0% 90%; 326 | } 327 | 328 | .zip_download_link { 329 | display: block; 330 | float: right; 331 | width: 90px; 332 | height:70px; 333 | text-indent: -5000px; 334 | overflow: hidden; 335 | background: url(../images/sprite_download.png) no-repeat bottom left; 336 | } 337 | 338 | .tar_download_link { 339 | display: block; 340 | float: right; 341 | width: 90px; 342 | height:70px; 343 | text-indent: -5000px; 344 | overflow: hidden; 345 | background: url(../images/sprite_download.png) no-repeat bottom right; 346 | margin-left: 10px; 347 | } 348 | 349 | .zip_download_link:hover { 350 | background: url(../images/sprite_download.png) no-repeat top left; 351 | } 352 | 353 | .tar_download_link:hover { 354 | background: url(../images/sprite_download.png) no-repeat top right; 355 | } 356 | 357 | #main_content_wrap { 358 | background: #f2f2f2; 359 | border-top: 1px solid #111; 360 | border-bottom: 1px solid #111; 361 | } 362 | 363 | #main_content { 364 | padding-top: 40px; 365 | } 366 | 367 | #footer_wrap { 368 | background: #212121; 369 | } 370 | 371 | 372 | 373 | /******************************************************************************* 374 | Small Device Styles 375 | *******************************************************************************/ 376 | 377 | @media screen and (max-width: 480px) { 378 | body { 379 | font-size:14px; 380 | } 381 | 382 | #downloads { 383 | display: none; 384 | } 385 | 386 | .inner { 387 | min-width: 320px; 388 | max-width: 480px; 389 | } 390 | 391 | #project_title { 392 | font-size: 32px; 393 | } 394 | 395 | h1 { 396 | font-size: 28px; 397 | } 398 | 399 | h2 { 400 | font-size: 24px; 401 | } 402 | 403 | h3 { 404 | font-size: 21px; 405 | } 406 | 407 | h4 { 408 | font-size: 18px; 409 | } 410 | 411 | h5 { 412 | font-size: 14px; 413 | } 414 | 415 | h6 { 416 | font-size: 12px; 417 | } 418 | 419 | code, pre { 420 | min-width: 320px; 421 | max-width: 480px; 422 | font-size: 11px; 423 | } 424 | 425 | } 426 | --------------------------------------------------------------------------------