├── posts ├── .gitkeep └── 1332691107-welcome-to-quil.md ├── quill.js ├── .gitignore ├── themes ├── obviovs │ ├── assets │ │ ├── favicon.png │ │ └── structure.css │ └── index.html ├── barebones │ ├── assets │ │ ├── favicon.png │ │ └── structure.css │ └── index.html ├── bootstrap │ ├── assets │ │ ├── favicon.png │ │ └── bootstrap │ │ │ ├── img │ │ │ ├── glyphicons-halflings.png │ │ │ └── glyphicons-halflings-white.png │ │ │ ├── css │ │ │ ├── bootstrap-responsive.min.css │ │ │ └── bootstrap-responsive.css │ │ │ └── js │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.js │ └── index.html └── simplertimes │ ├── assets │ ├── back.png │ ├── favicon.png │ ├── header.png │ ├── tasky_pattern.png │ └── structure.css │ └── index.html ├── LICENSE ├── quill ├── scaffolding │ ├── package.json │ └── realtime.js ├── server.js ├── intro.js └── compiler.js ├── config.json ├── package.json ├── bin └── quill └── README.md /posts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quill.js: -------------------------------------------------------------------------------- 1 | var app = require('./quill/server'); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.sw[a-z] 3 | *.orig 4 | .DS_Store 5 | _site/ 6 | -------------------------------------------------------------------------------- /themes/obviovs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/obviovs/assets/favicon.png -------------------------------------------------------------------------------- /themes/barebones/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/barebones/assets/favicon.png -------------------------------------------------------------------------------- /themes/bootstrap/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/bootstrap/assets/favicon.png -------------------------------------------------------------------------------- /themes/simplertimes/assets/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/simplertimes/assets/back.png -------------------------------------------------------------------------------- /themes/simplertimes/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/simplertimes/assets/favicon.png -------------------------------------------------------------------------------- /themes/simplertimes/assets/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/simplertimes/assets/header.png -------------------------------------------------------------------------------- /themes/simplertimes/assets/tasky_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/simplertimes/assets/tasky_pattern.png -------------------------------------------------------------------------------- /themes/bootstrap/assets/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/bootstrap/assets/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /themes/bootstrap/assets/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/quill/master/themes/bootstrap/assets/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /posts/1332691107-welcome-to-quil.md: -------------------------------------------------------------------------------- 1 | This is a sample blog post on Quill 2 | 3 | > Markdown is supported 4 | 5 | Which means you can do _cool_ things like [add links](http://twitter.com/justquillin) and *things*. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------------------------------------------------------- 3 | * "THE BEER-WARE LICENSE" (Hacker Revision): 4 | * and wrote these files. As 5 | * long as you retain this notice you can do whatever you want with this stuff. 6 | * If we meet some day, and you think this stuff is worth it, you can buy us a 7 | * beer in return. Signed Swift & Ian. 8 | * ---------------------------------------------------------------------------- 9 | */ 10 | -------------------------------------------------------------------------------- /quill/scaffolding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quill", 3 | "description": "This is a blog based on Quill!", 4 | "version": "0.0.1", 5 | "engines": { 6 | "node": "~0.6.9" 7 | }, 8 | "dependencies": { 9 | "colors": "0.6.0-1", 10 | "handlebars": "1.0.5beta", 11 | "marked": "0.2.3", 12 | "node-static": "0.5.9", 13 | "socket.io": "0.9.2", 14 | "underscore": "1.3.1", 15 | "wrench": "1.3.7", 16 | "consolidate": "0.1.0", 17 | "whiskers": "0.1.5" 18 | }, 19 | "devDependencies": {}, 20 | "scripts": { 21 | "start": "quill.js" 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": true, 3 | "theme": "bootstrap", 4 | "name": "Quill", 5 | "description": "Blogging for Hackers.", 6 | "blogroll": [ 7 | { 8 | "title": "Download Quill", 9 | "url": "https://github.com/theycallmeswift/quill", 10 | "description": "Get the quill server" 11 | }, 12 | { 13 | "title": "Official Docs", 14 | "url": "https://github.com/theycallmeswift/quill", 15 | "description": "The official documentation" 16 | }, 17 | { 18 | "title": "Follow us on Twitter", 19 | "url": "http://twitter.com/justquillin", 20 | "description": "Follow @justquillin on Twitter" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /quill/scaffolding/realtime.js: -------------------------------------------------------------------------------- 1 | var socket = io.connect('/'); 2 | socket.on('connected', function() { 3 | var divs = document.getElementsByTagName('div') 4 | , i 5 | , lastPost = 0; 6 | 7 | for(i = 0; i < divs.length; i++) { 8 | if(divs[i].className && divs[i].className.indexOf('_post') !== -1) { 9 | lastPost = divs[i].id; 10 | break; 11 | } 12 | } 13 | this.emit('update', lastPost); 14 | }); 15 | 16 | socket.on('update', function(files) { 17 | var newElement = 'A new post has been made!
Click here to see it.
'; 18 | var bodyElement = document.body; 19 | bodyElement.innerHTML = newElement + bodyElement.innerHTML; 20 | }); 21 | -------------------------------------------------------------------------------- /themes/obviovs/assets/structure.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | margin: 0px; 4 | padding: 0px; 5 | } 6 | body { 7 | font-size: 16px; 8 | line-height: 1.5em; 9 | font-family: 'Open Sans', sans-serif; 10 | color: #2d2d2d; 11 | } 12 | p { 13 | margin: 12px 0px; 14 | } 15 | small, .muted { 16 | color: #777; 17 | } 18 | small { 19 | font-size: .7em; 20 | } 21 | a { 22 | color: #000; 23 | } 24 | a:hover { 25 | text-decoration: none; 26 | } 27 | h1,h2,h3,h4,h5,h6 { 28 | line-height: 1.1em; 29 | margin: 12px 0px; 30 | } 31 | h1 { 32 | font-size: 2em; 33 | } 34 | h2 { 35 | font-size: 1.8em; 36 | } 37 | h3 { 38 | font-size: 1.6em; 39 | } 40 | h4 { 41 | font-size: 1.4em; 42 | } 43 | h5, h6 { 44 | font-size: 1.2em; 45 | } 46 | p { 47 | font-size: 1em; 48 | } 49 | blockquote { 50 | padding-left: 12px; 51 | border-left: 6px solid #000; 52 | } 53 | ul, ol { 54 | 55 | list-style-position:inside; 56 | } -------------------------------------------------------------------------------- /themes/barebones/assets/structure.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | margin: 0px; 4 | padding: 0px; 5 | } 6 | body { 7 | font-size: 16px; 8 | line-height: 1.5em; 9 | font-family: 'Open Sans', sans-serif; 10 | color: #2d2d2d; 11 | padding: 18px; 12 | } 13 | p { 14 | margin: 12px 0px; 15 | } 16 | small, .muted { 17 | color: #777; 18 | } 19 | small { 20 | font-size: .7em; 21 | } 22 | a { 23 | color: #000; 24 | } 25 | a:hover { 26 | text-decoration: none; 27 | } 28 | h1,h2,h3,h4,h5,h6 { 29 | line-height: 1.1em; 30 | margin: 12px 0px; 31 | } 32 | h1 { 33 | font-size: 2em; 34 | } 35 | h2 { 36 | font-size: 1.8em; 37 | } 38 | h3 { 39 | font-size: 1.6em; 40 | } 41 | h4 { 42 | font-size: 1.4em; 43 | } 44 | h5, h6 { 45 | font-size: 1.2em; 46 | } 47 | p { 48 | font-size: 1em; 49 | } 50 | blockquote { 51 | padding-left: 12px; 52 | border-left: 6px solid #000; 53 | } 54 | ul, ol { 55 | list-style-position:inside; 56 | } -------------------------------------------------------------------------------- /themes/simplertimes/assets/structure.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | margin: 0px; 4 | padding: 0px; 5 | } 6 | body { 7 | font-size: 16px; 8 | line-height: 1.5em; 9 | font-family: 'Open Sans', sans-serif; 10 | color: #2d2d2d; 11 | padding: 18px; 12 | } 13 | p { 14 | margin: 12px 0px; 15 | } 16 | small, .muted { 17 | color: #777; 18 | } 19 | small { 20 | font-size: .7em; 21 | } 22 | a { 23 | color: #000; 24 | } 25 | a:hover { 26 | text-decoration: none; 27 | } 28 | h1,h2,h3,h4,h5,h6 { 29 | line-height: 1.1em; 30 | margin: 12px 0px; 31 | } 32 | h1 { 33 | font-size: 2em; 34 | } 35 | h2 { 36 | font-size: 1.8em; 37 | } 38 | h3 { 39 | font-size: 1.6em; 40 | } 41 | h4 { 42 | font-size: 1.4em; 43 | } 44 | h5, h6 { 45 | font-size: 1.2em; 46 | } 47 | p { 48 | font-size: 1em; 49 | } 50 | blockquote { 51 | padding-left: 12px; 52 | border-left: 6px solid #000; 53 | } 54 | ul, ol { 55 | list-style-position:inside; 56 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": [ 3 | "Mike Swift (http://theycallmeswift.com)", 4 | "Ian Jennings (http://meetjennings.com)" 5 | ], 6 | "name": "quill", 7 | "description": "A realtime git backed blogging engine for node.js", 8 | "version": "0.1.6", 9 | "homepage": "http://justquillin.com/", 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:theycallmeswift/quill.git" 13 | }, 14 | "bin": { 15 | "quill": "./bin/quill" 16 | }, 17 | "engines": { 18 | "node": "~0.8.0" 19 | }, 20 | "dependencies": { 21 | "colors": "0.6.0-1", 22 | "handlebars": "1.0.5beta", 23 | "marked": "0.2.3", 24 | "node-static": "0.5.9", 25 | "socket.io": "0.9.2", 26 | "underscore": "1.3.1", 27 | "wrench": "1.3.7", 28 | "consolidate": "0.1.0", 29 | "whiskers": "0.1.5" 30 | }, 31 | "devDependencies": {}, 32 | "domains": [ 33 | "justquillin.com", 34 | "www.justquillin.com" 35 | ], 36 | "subdomain": "quill", 37 | "scripts": { 38 | "start": "quill.js" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /themes/barebones/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Barebones Theme 4 | 5 | 6 | 14 | 15 | 16 |
17 | 32 |
33 |
    34 | {{#posts}} 35 |
  1. 36 |

    {{{ title }}} {{{ timestamp }}}

    37 | {{{ body }}} 38 |
  2. 39 | {{/posts}} 40 |
41 |
42 |
43 | {{{ realtime }}} 44 | 45 | 46 | -------------------------------------------------------------------------------- /quill/server.js: -------------------------------------------------------------------------------- 1 | var colors = require('colors') 2 | , compiler = require('./compiler') 3 | , config = require('../config.json') 4 | , pack = require('../package.json') 5 | , http = require('http') 6 | , path = require('path') 7 | , sio = require('socket.io') 8 | , static = require('node-static') 9 | , util = require('util') 10 | , intro = require('./intro'); 11 | 12 | var themeDir = path.join(__dirname, '..', 'themes', config.theme); 13 | var postsDir = path.join(__dirname, '..', 'posts'); 14 | 15 | compiler.compile(postsDir, themeDir, config, function(err, files) { 16 | if(err) { 17 | throw err; 18 | } 19 | 20 | var filePath = path.join(__dirname, '..', '_site'); 21 | var fileServer = new static.Server(filePath); 22 | 23 | var app = http.createServer(function(req, res) { 24 | req.addListener('end', function () { 25 | // Treat all other requests as static file requests. 26 | fileServer.serve(req, res, function (err, result) { 27 | if (err) { 28 | util.log("Error serving " + req.url + " - " + err.message); 29 | 30 | res.writeHead(err.status, err.headers); 31 | res.end(); 32 | return; 33 | } 34 | }); 35 | }); 36 | }); 37 | 38 | var sioApp = sio.listen(app); 39 | sioApp.sockets.on('connection', function(socket) { 40 | socket.emit('connected'); 41 | socket.on('update', function(postId) { 42 | if(files && files.length && postId * 1000 < files[0].timestamp) { 43 | socket.emit('update', files); 44 | } 45 | }); 46 | }); 47 | 48 | app.on('listening', function() { 49 | intro(); 50 | console.log(''); 51 | console.log('v'.yellow + String(pack.version).yellow); 52 | for(var a in pack.author){ 53 | console.log(pack.author[a].cyan); 54 | } 55 | console.log('a ' + 'HackNY'.red + ' hack'); 56 | console.log(''); 57 | console.log('-------------------------------------'); 58 | console.log('Theme: ' + String(config.theme).cyan); 59 | console.log('-------------------------------------') 60 | console.log('Starting server on port 8000'); 61 | }); 62 | 63 | 64 | app.listen(8000); 65 | }); 66 | -------------------------------------------------------------------------------- /bin/quill: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var argv = process.argv.slice(2) 3 | , fs = require("fs") 4 | , fd = require("path") 5 | , wrench = require('wrench'); 6 | 7 | var expandPath = function(path, dir){ 8 | if (fd.basename(path) == path) { 9 | path = dir + path 10 | } 11 | return fd.normalize(path); 12 | }; 13 | 14 | var dasherize = function(str){ 15 | return str.trim(str).toLowerCase().replace(/[_\s]+/g, '-').replace(/([A-Z])/g, '-$1').replace(/-+/g, '-'); 16 | }; 17 | 18 | var copyFile = function(source, target) { 19 | var is = fs.createReadStream(source); 20 | var os = fs.createWriteStream(target); 21 | is.pipe(os); 22 | }; 23 | 24 | var help = [ 25 | "usage: quill [options] ", 26 | "", 27 | "Generates a Quill blog using the specified command-line options", 28 | "", 29 | "options:", 30 | " new path Generates a new Quill blog", 31 | " post \"Post Title\" Generates a new Quill post for the current blog" 32 | ].join('\n'); 33 | 34 | var generators = {}; 35 | 36 | generators['new'] = function() { 37 | var path = fd.normalize(argv[1]); 38 | 39 | // Create parent dir 40 | if (fs.existsSync(path)) throw(path + " already exists"); 41 | fs.mkdirSync(path, 0775); 42 | 43 | var baseDir = __dirname + '/../' 44 | , sourceDir 45 | , targetDir; 46 | 47 | sourceDir = expandPath('themes', baseDir); 48 | targetDir = expandPath('themes', path + "/"); 49 | 50 | wrench.copyDirRecursive(sourceDir, targetDir, function(err) { 51 | if (err) throw(err); 52 | 53 | sourceDir = expandPath('quill', baseDir); 54 | targetDir = expandPath('quill', path + "/"); 55 | 56 | wrench.copyDirRecursive(sourceDir, targetDir, function(err) { 57 | if (err) throw(err); 58 | 59 | fs.mkdirSync(expandPath('posts', path + '/'), 0775); 60 | 61 | sourceDir = expandPath('quill.js', baseDir); 62 | targetDir = expandPath('quill.js', path + "/"); 63 | 64 | copyFile(sourceDir, targetDir); 65 | 66 | sourceDir = expandPath('package.json', baseDir + 'quill/scaffolding/'); 67 | targetDir = expandPath('package.json', path + "/"); 68 | 69 | var package = require(sourceDir); 70 | package.name = dasherize(argv[1]); 71 | fs.writeFileSync(targetDir, JSON.stringify(package)); 72 | 73 | sourceDir = expandPath('config.json', baseDir); 74 | targetDir = expandPath('config.json', path + "/"); 75 | 76 | copyFile(sourceDir, targetDir); 77 | 78 | }); 79 | }); 80 | } 81 | 82 | generators.post = function() { 83 | var timestamp = Math.round(new Date().getTime() / 1000) 84 | var template = process.cwd() + "/posts/"; 85 | var filename = timestamp + "-" + dasherize(argv[1]) + ".md"; 86 | var fullpath = expandPath(filename, template); 87 | 88 | fs.writeFile(fullpath, "", function() { 89 | console.log("Successfully created post: " + fullpath); 90 | }); 91 | }; 92 | 93 | if (generators[argv[0]]) { 94 | generators[argv[0]](); 95 | } else { 96 | console.log(help); 97 | } 98 | -------------------------------------------------------------------------------- /themes/bootstrap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ config.name }}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 44 | 45 | 46 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 60 | 69 | 70 |
71 | 72 | 73 |
74 | {{#if config.name}} 75 |

{{ config.name }}}

76 | {{/if}} 77 | {{#if config.description}} 78 |

{{ config.description }}}

79 | {{/if}} 80 |
81 | 82 | 83 |
84 |
85 |

Posts

86 | {{#posts}} 87 |
88 |

{{{ title }}}

89 | {{{ body }}} 90 |
91 | {{/posts}} 92 |
93 |
94 |

Cool Links

95 | 100 |
101 |
102 | 103 |
104 | 105 |
106 |

© {{ config.name }}} 2012

107 |
108 | 109 |
110 | {{{ realtime }}} 111 | 112 | 113 | -------------------------------------------------------------------------------- /quill/intro.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | console.log(' ```...----:::::////////////:-`'.green); 3 | console.log(' ``..-::///++++++++++++++++++++++++:.` '.green); 4 | console.log(' ``.-://+++++++++++++++++++++++++++++++:.` '.green); 5 | console.log(' `.-:/+++++++++++++++++++++++++++++++++++/.` '.green); 6 | console.log(' `.:/++++++++++++++++++++++++++++++++++++++:` '.green); 7 | console.log(' `-/+++++++++++++++++++++++++++++++++++++++/.` '.green); 8 | console.log(' `:/++++++++++++++++++++++++++++++++++++++++/.` '.green); 9 | console.log(' `-/++++++++++++++++++++++++++++/++++++++++++/. '.green); 10 | console.log(' `:+++++++++++++++++++++++++++///++++++++++++/. '.green); 11 | console.log(' `-++++++++++++++++++++++++++:-:/+++++++++++++. '.green); 12 | console.log(' .++++++++++++++++++++++++/-.:/++++++++++++++-` '.green); 13 | console.log(' `-------:://++++++++++/:..:/+++++++++++++++/` '.green); 14 | console.log(' `--:://////++++++++++/-``:/+++++++++++++++++. '.green); 15 | console.log(' .//////++++++++++++/.``-/++++++++++++++++++:` '.green); 16 | console.log(' `.--:://+++++++++/:. `-/+++++++++++++++++++/` '.green); 17 | console.log(' .++++++++++++++/:` ./+++++++++++++++++++++- '.green); 18 | console.log(' `:++++++++++++/-` .:++++++++++++++++++++++/` '.green); 19 | console.log(' .+++++++++++/-` `:++++++++++++++++++++++++- ``` ``` `.` '.green); 20 | console.log(' :+++++++++/-` `-/++++++++++++++++++++++++:` ./-` `::` -+. '.green); 21 | console.log(' `/+++++++/-` -/+++++++++++++++++++++++++/` .:` -+. `//` '.green); 22 | console.log(' -++++++/-` ./++++++++++++++++++++++++++:`` `-` ``` `+:` -+- '.green); 23 | console.log(' `/+++++:` `:++++++++++++++++++++++++++/-`-- `:+` `-/` -+. :/` '.green); 24 | console.log(' .++++/. `:+++++++++++++++++++++++++++:. .+- `-+/` `.:+/` `.// `/: '.green); 25 | console.log(' `/++/. `:+++++++++++++++++++++++++++:.` :+` ``:://```.::-+- ``:/+- ``:+: `'.green); 26 | console.log(' .+/. `:++++++++++++++++++++++++++/-.` :/``.::.`./::::.`-+. ``-/-.+-```.::.//` ``.-'.green); 27 | console.log(' `.:. .:++++++++++++++++++++++++//-.` .//:-.` `..` -+:..:/-` `//::::.``:+:---:-`'.green); 28 | console.log(' `.:. `-/+++++++++++++++++++++//:-.` `-::-.` `.-.` `.---.` '.green); 29 | console.log(' ``-:. `------::::///////::::--..`` '.green); 30 | console.log(' `.--..---` '.green); 31 | console.log('`-//---.` '.green); 32 | } -------------------------------------------------------------------------------- /themes/obviovs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ config.name }}} 5 | 6 | 7 | 8 | 9 | 10 | 15 | 137 | 138 | 139 |
140 | 141 | 157 |
158 |
    159 | {{#posts}} 160 |
  1. 161 |

    {{ title }}

    162 | {{{ body }}} 163 |
  2. 164 | {{/posts}} 165 |
166 |
167 |
168 | {{{ realtime }}} 169 | 170 | -------------------------------------------------------------------------------- /themes/simplertimes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ config.name }}} 5 | 6 | 7 | 8 | 9 | 10 | 15 | 150 | 151 | 152 |
153 | 176 |

The Quill Blog Powered by Quill

177 |
178 |
    179 | {{#posts}} 180 |
  1. 181 |

    {{{ title }}}

    182 | {{{ body }}} 183 |
  2. 184 | {{/posts}} 185 |
186 |
187 |
188 | {{{ realtime }}} 189 | 190 | 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quill 2 | 3 | Quill is a simple blog engine inspired by 4 | [Jekyll](https://github.com/mojombo/jekyll). Quill runs on 5 | [node](http://nodejs.org/) and has 6 | an easy command line interface. Themeing is as simple as editing a single html page. 7 | 8 | ## Installing & Deployment 9 | 10 | You can install Quill using the npm package manager. Just type: 11 | 12 | npm install quill -g 13 | 14 | Now, we have access to the quill command. We can start a new blog by typing: 15 | 16 | quill new my_new_blog 17 | 18 | This will create a new folder in the specified path where our blog will live. 19 | Lets start by making a post. 20 | 21 | cd my_new_blog 22 | quill post "My First Post" 23 | 24 | This will create a post file in the `posts/` directory which is prefixed by the 25 | current timestamp. All post files are written in 26 | [Markdown](http://daringfireball.net/projects/markdown/syntax) and will 27 | compile to static HTML. 28 | 29 | To deploy the site we are going to use 30 | [Nodejitsu](http://nodejitsu.com/). Instructions on setting up their 31 | commandline tool Jitsu can be found 32 | [here](https://github.com/nodejitsu/jitsu). Once you have jitsu set up it is 33 | as simple as typing: 34 | 35 | jitsu deploy 36 | 37 | ### Local development 38 | 39 | If you want to edit your themes locally, you can run the quill server by 40 | typing: 41 | 42 | npm install 43 | node quill 44 | 45 | Now you can navigate to `http://localhost:8000` and see your blog while you 46 | edit it. __Note__: You have to restart the server when you make changes to 47 | see them. 48 | 49 | # Config.json 50 | 51 | We configure Quill with [config.json](https://github.com/theycallmeswift/quill/blob/master/config.json). 52 | 53 | { 54 | "development": true, 55 | "theme": "bootstrap", 56 | "name": "Quill", 57 | "description": "Blogging for Hackers.", 58 | "blogroll": [ 59 | { 60 | "title": "Download Quill", 61 | "url": "https://github.com/theycallmeswift/quill", 62 | "description": "Get the quill server" 63 | }, 64 | { 65 | "title": "Official Docs", 66 | "url": "https://github.com/theycallmeswift/quill", 67 | "description": "The official documentation" 68 | }, 69 | { 70 | "title": "Follow us on Twitter", 71 | "url": "http://twitter.com/justquillin", 72 | "description": "Follow @justquillin on Twitter" 73 | } 74 | ] 75 | } 76 | 77 | Here it is line by line: 78 | 79 | ## Development Flag 80 | 81 | "development": true, 82 | 83 | You won't care about this unless you're helping to build Quill (please do!). 84 | 85 | ## Theme 86 | 87 | "theme": "barebones", 88 | 89 | What directory the theme is stored in. ```/themes/[config.theme]/index.html```. Every variable from config.json is passed to the theme. Look at the next section to see how the blog name and description appear. 90 | 91 | There are four example themes in this repository, included the theme found at http://justquillin.com. 92 | 93 | ## Name and Description 94 | 95 | The name of the blog and a short blub about it. 96 | 97 | "name": "Quill", 98 | "description": "Blogging for Hackers.", 99 | 100 | In the ***barebones theme*** we render the name and description in an hgroup: 101 | 102 |
103 | {{#if config.name}} 104 |

{{ config.name }}}

105 | {{/if}} 106 | {{#if config.description}} 107 |

{{ config.description }}}

108 | {{/if}} 109 |

110 | 111 | ## Blogroll 112 | 113 | The blogroll is an array of objects. These links are also rendered in the template. 114 | 115 | "blogroll": [ 116 | { 117 | "title": "Download Quill", 118 | "url": "https://github.com/theycallmeswift/quill", 119 | "description": "Get the quill server" 120 | }, 121 | { 122 | "title": "Official Docs", 123 | "url": "https://github.com/theycallmeswift/quill", 124 | "description": "The official documentation" 125 | }, 126 | { 127 | "title": "Follow us on Twitter", 128 | "url": "http://twitter.com/justquillin", 129 | "description": "Follow @justquillin on Twitter" 130 | } 131 | ] 132 | 133 | Here's an example of how the ***barebones theme*** renders the blogroll: 134 | 135 | 136 | {{ #each config.blogroll }} 137 |
  • {{ title }}
  • 138 | {{ /each }} 139 | 140 | 141 | # Blog Posts 142 | 143 | Blog posts are transformed from markdown into templates. Blog posts start as markdown files in /posts and then get passed to our template (as specified in the config). 144 | 145 | These markdown posts are just like the blogroll found in config. Here's an example of how they're styled in the ***barebones theme***: 146 | 147 | {{#posts}} 148 |
  • 149 |

    {{{ title }}} {{{ timestamp }}}

    150 | {{{ body }}} 151 |
  • 152 | {{/posts}} 153 | 154 | The ```{{{ body }}}``` variable outputs compiled markdown. This markdown is wrapped in a ```
    ``` with a class of ```_post``` and a unique ```id```. 155 | 156 |
    [compiled markdown (html)]
    157 | 158 | Remember that newlines in markdown get ```p``` wrappers. 159 | 160 | # Realtime! 161 | 162 | It wouldn't be node if it wasn't realtime. The crowd-pleaser for the hackny hackathon, we integrated a realtime notification system for new blog posts. 163 | 164 | This is added as a bonus variable for templates. Include it at the end of your template, right before the `````` tag. 165 | 166 | {{{ realtime }}} 167 | 168 | When you publish a new blog post (by deploying the server), all of the connected clients will be notified in realtime. The notification is appended to the body of the document [with javascript](https://github.com/theycallmeswift/quill/blob/master/quill/scaffolding/realtime.js). 169 | 170 | This code gets added to the page whenever a new post is made. It will not exist on the page before this. To test, you can copy and paste the following before the `````` tag. 171 | 172 | A new post has been made!
    Click here to see it.
    173 | 174 | # Assets 175 | 176 | Throw all your static assets (like images or javascript) into the ```/assets``` folder within the template directory. You can reference these files in your template like this: 177 | 178 | 179 | 180 | # Bonus Structure.css 181 | 182 | If you want an easy way to render markdown, check out *structure.css*, bundled into the four provided themes within the ```/assets``` directory. Based off the [rawr framwork](http://getrawr.com). -------------------------------------------------------------------------------- /themes/bootstrap/assets/bootstrap/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} 2 | .clearfix:after{clear:both;} 3 | .hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} 4 | .input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} 5 | .hidden{display:none;visibility:hidden;} 6 | .visible-phone{display:none;} 7 | .visible-tablet{display:none;} 8 | .visible-desktop{display:block;} 9 | .hidden-phone{display:block;} 10 | .hidden-tablet{display:block;} 11 | .hidden-desktop{display:none;} 12 | @media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} 13 | -------------------------------------------------------------------------------- /quill/compiler.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | , hbs = require('handlebars') 3 | , marked = require('marked') 4 | , path = require('path') 5 | , util = require('util') 6 | , wrench = require('wrench'); 7 | 8 | /** 9 | * Markdown Options 10 | */ 11 | marked.setOptions({ 12 | gfm: true, 13 | pedantic: false, 14 | sanitize: false 15 | }); 16 | 17 | /** 18 | * Handlebars Realtime Helper 19 | */ 20 | hbs.registerHelper('realtime', function() { 21 | return "\n"; 22 | }); 23 | 24 | /** 25 | * copyAssets 26 | * 27 | * Copys the assets folder from the theme directory into the new static site 28 | * directory. Also move any quill files into the new assets folder. 29 | * 30 | * @param String themeDirectory Directory containing the theme 31 | * @param String siteDirectory Directory where the compiled static site exists 32 | * @param Function callback Callback function 33 | */ 34 | var copyAssets = function(themeDirectory, siteDirectory, callback) { 35 | 36 | var siteAssets = path.join(siteDirectory, 'assets') 37 | , themeAssets = path.join(themeDirectory, 'assets'); 38 | 39 | var moveQuillAssets = function() { 40 | var quillAssetsFolder = path.join(siteAssets, 'quill') 41 | , realtimeAssetPath = path.join(siteAssets, 'quill', 'realtime.js') 42 | , realtimePath = path.join(__dirname, 'scaffolding', 'realtime.js'); 43 | 44 | fs.mkdir(quillAssetsFolder, function() { 45 | var is = fs.createReadStream(realtimePath); 46 | var os = fs.createWriteStream(realtimeAssetPath); 47 | util.pump(is, os); 48 | callback(); 49 | }); 50 | }; 51 | 52 | fs.exists(themeDirectory, function(exists) { 53 | if(!exists) { 54 | return callback(new Error("Error: Theme does not exist")); 55 | } 56 | 57 | fs.exists(themeAssets, function(exists) { 58 | if(!exists) { 59 | util.log("No assets directory found in " + themeDirectory); 60 | fs.mkdir(siteAssets, function() { 61 | return moveQuillAssets(); 62 | }); 63 | } 64 | 65 | wrench.copyDirRecursive(themeAssets, siteAssets, function() { 66 | util.log("Successfully copied assets"); 67 | moveQuillAssets(); 68 | }); 69 | }); 70 | }); 71 | }; 72 | 73 | /** 74 | * createSiteDirectory 75 | * 76 | * Creates the directory where the static site will exist. Removes any 77 | * existing directory beforehand. 78 | * 79 | * @param String directory Directory where the static site will be stored 80 | * @param Function callback Callback function 81 | */ 82 | var createSiteDirectory = function(directory, callback) { 83 | fs.exists(directory, function(exists) { 84 | if(exists) { 85 | util.log("Removing directory: " + directory); 86 | wrench.rmdirSyncRecursive(directory); 87 | } 88 | 89 | fs.mkdir(directory, function() { 90 | util.log("Successfully created directory: " + directory); 91 | callback(); 92 | }); 93 | }); 94 | }; 95 | 96 | /** 97 | * createPostDirectory 98 | * 99 | * Creates the directory where each post's index.html will exist. Removes any 100 | * existing directory beforehand. 101 | * 102 | * @param String directory Directory where the static site will be stored 103 | * @param HTML output for post 104 | * @param Function callback Callback function 105 | */ 106 | var createPostDirectory = function(directory, layoutHTML, callback) { 107 | var outputFilename; 108 | 109 | fs.exists(directory, function(exists) { 110 | if(exists) { 111 | wrench.rmdirSyncRecursive(directory); 112 | } 113 | 114 | fs.mkdir(directory, function() { 115 | outputFilename = path.join(directory, 'index.html'); 116 | 117 | fs.writeFile(outputFilename, layoutHTML, function(err) { 118 | if(err) { 119 | return callback(err); 120 | } 121 | callback(); 122 | }); 123 | 124 | }); 125 | }); 126 | }; 127 | 128 | /** 129 | * findPosts 130 | * 131 | * Locate all the generated post markdown files inside the posts directory. 132 | * Only matches filenames in the `1234567890-post-title.md` formate. 133 | * Returns an * array of file objects that have `name`, `path`, `timestamp`, 134 | * `url`, `body`, and `title` properties. 135 | * 136 | * @param String postsDir Directory containing all the post files 137 | * @param Function callback Callback function 138 | * @return Array 139 | */ 140 | var findPosts = function(postsDir, callback) { 141 | fs.readdir(postsDir, function(err, files) { 142 | var fileCounter = 0 143 | , filename 144 | , fileObject 145 | , filePath 146 | , match 147 | , results = []; 148 | 149 | if(err) { 150 | return callback(err); 151 | } 152 | 153 | var matchingFiles = []; 154 | for(var key in files) { 155 | filename = files[key]; 156 | match = filename.match(/^(\d+)-(.*).md$/); 157 | 158 | if(match) { 159 | matchingFiles.push({ 160 | filename: filename, 161 | hypenizedTitle: match[2], 162 | timestamp: match[1] 163 | }); 164 | } 165 | } 166 | 167 | if(matchingFiles.length == 0) { 168 | return callback(false, []); 169 | } 170 | 171 | fileCallback = function() { 172 | fileCounter += 1; 173 | if(fileCounter == matchingFiles.length) { 174 | return callback(false, results); 175 | } 176 | }; 177 | 178 | for(var key in matchingFiles) { 179 | fileObject = matchingFiles[key]; 180 | (function() { 181 | var title = fileObject.hypenizedTitle 182 | , time = fileObject.timestamp; 183 | 184 | filePath = path.join(__dirname, '..', 'posts', fileObject.filename); 185 | fs.readFile(filePath, function(err, data) { 186 | if(err) { 187 | return callback(err); 188 | } 189 | 190 | wrappedBody = "
    " + marked(data.toString()) + "
    "; 191 | 192 | results.push({ 193 | title: humanized(title), 194 | body: wrappedBody, 195 | name: filename, 196 | path: filePath, 197 | timestamp: time * 1000, 198 | url: title.toLowerCase() 199 | }); 200 | 201 | fileCallback(); 202 | }); 203 | })(); 204 | } 205 | }); 206 | }; 207 | 208 | /** 209 | * generateHTMLfiles 210 | * 211 | * Takes an array of file objectes and a layout and compiles them to static html 212 | * files within the output directory. Also exposes the config to the markdown 213 | * files. Also compiles an index file containing all the pages. 214 | * 215 | * @param Array files the array of file objects to render html pages for. 216 | * @param String layout the path to the layout file 217 | * @param String outputDir the output directory for the HTML files 218 | * @param Object config the contents of config.json 219 | * @param Function callback the callback function 220 | * @return Array the sorted array of posts in the index 221 | */ 222 | var generateHTMLFiles = function(files, layout, outputDir, config, callback) { 223 | var counter = 0 224 | , compileCompleted 225 | , compiledTemplate 226 | , layoutHTML 227 | , file 228 | , fileStream 229 | , outputFilename 230 | , resultsArray; 231 | 232 | 233 | compileCompleted = function(posts) { 234 | counter += 1; 235 | if(counter == files.length + 1) { 236 | util.log("Generating static HTML files"); 237 | callback(false, posts); 238 | } 239 | }; 240 | 241 | fs.readFile(layout, function(err, layoutBuffer) { 242 | if(err) { 243 | return callback(err); 244 | } 245 | 246 | compiledTemplate = hbs.compile(layoutBuffer.toString()); 247 | 248 | var indexPosts = []; 249 | 250 | for(var key in files) { 251 | file = files[key]; 252 | var pagePosts = []; 253 | 254 | pagePosts.push(file); 255 | indexPosts.push(file); 256 | 257 | layoutHTML = compiledTemplate({ config: config, posts: pagePosts }); 258 | 259 | var outputFileDir = path.join(outputDir, file.url); 260 | 261 | createPostDirectory(outputFileDir, layoutHTML, compileCompleted); 262 | } 263 | 264 | var sortByTimestamp = function(a, b) { 265 | return (a.timestamp < b.timestamp) ? 1 : -1; 266 | } 267 | indexPosts.sort(sortByTimestamp); 268 | 269 | indexHTML = compiledTemplate({ config: config, posts: indexPosts }); 270 | 271 | outputFilename = path.join(outputDir, 'index.html'); 272 | var fileRes = fs.writeFile(outputFilename, indexHTML, function(err) { 273 | if(err) { 274 | return callback(err); 275 | } 276 | compileCompleted(indexPosts); 277 | }); 278 | }); 279 | }; 280 | 281 | /** 282 | * humanized 283 | * 284 | * Takes in a hypen-separated-string and converts it to Human Readable Format 285 | * 286 | * @param String string String to convert to human format. 287 | * @return String the human readable string 288 | */ 289 | var humanized = function(string) { 290 | var terms = string.split('-'); 291 | 292 | for(var i=0; i < terms.length; i++){ 293 | terms[i] = terms[i].toLowerCase().charAt(0).toUpperCase() + terms[i].slice(1); 294 | } 295 | 296 | return terms.join(' '); 297 | } 298 | 299 | /** 300 | * compile 301 | * 302 | * Take a posts directory containing markdown posts, a theme directory containing 303 | * an index.html and any assets in an assets folder, and a config and produces a 304 | * compiled, static HTML site in the _site folder of the root directory. Also, 305 | * returns a sorted array of files so the server can hundle real time updates. 306 | * 307 | * @param String postsDir directory containing all the markdown posts 308 | * @param String themeDir directory containing the theme files 309 | * @param Object siteConfig contents of config.json 310 | * @param Function callback the callback function 311 | * @return Array a sorted array of file objects 312 | */ 313 | var compile = function(postsDir, themeDir, siteConfig, callback) { 314 | var counter = 0 315 | , files 316 | , siteDirectory = path.join(__dirname, '..', '_site') 317 | , layout = path.join(themeDir, 'index.html'); 318 | 319 | findPosts(postsDir, function(err, result) { 320 | if(err) { 321 | return callback(err); 322 | } 323 | files = result; 324 | 325 | createSiteDirectory(siteDirectory, function(err) { 326 | if(err) { 327 | return callback(err); 328 | } 329 | 330 | copyAssets(themeDir, siteDirectory, function(err) { 331 | if(err) { 332 | return callback(err); 333 | } 334 | 335 | generateHTMLFiles(files, layout, siteDirectory, siteConfig, function(err, posts) { 336 | if(err) { 337 | return callback(err); 338 | } 339 | util.log("Site successfully compiled into _site"); 340 | callback(false, posts); 341 | }); 342 | }); 343 | }); 344 | }); 345 | }; 346 | 347 | module.exports = { 348 | compile: compile 349 | }; -------------------------------------------------------------------------------- /themes/bootstrap/assets/bootstrap/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | .clearfix { 11 | *zoom: 1; 12 | } 13 | .clearfix:before, 14 | .clearfix:after { 15 | display: table; 16 | content: ""; 17 | } 18 | .clearfix:after { 19 | clear: both; 20 | } 21 | .hide-text { 22 | overflow: hidden; 23 | text-indent: 100%; 24 | white-space: nowrap; 25 | } 26 | .input-block-level { 27 | display: block; 28 | width: 100%; 29 | min-height: 28px; 30 | /* Make inputs at least the height of their button counterpart */ 31 | 32 | /* Makes inputs behave like true block-level elements */ 33 | 34 | -webkit-box-sizing: border-box; 35 | -moz-box-sizing: border-box; 36 | -ms-box-sizing: border-box; 37 | box-sizing: border-box; 38 | } 39 | .hidden { 40 | display: none; 41 | visibility: hidden; 42 | } 43 | .visible-phone { 44 | display: none; 45 | } 46 | .visible-tablet { 47 | display: none; 48 | } 49 | .visible-desktop { 50 | display: block; 51 | } 52 | .hidden-phone { 53 | display: block; 54 | } 55 | .hidden-tablet { 56 | display: block; 57 | } 58 | .hidden-desktop { 59 | display: none; 60 | } 61 | @media (max-width: 767px) { 62 | .visible-phone { 63 | display: block; 64 | } 65 | .hidden-phone { 66 | display: none; 67 | } 68 | .hidden-desktop { 69 | display: block; 70 | } 71 | .visible-desktop { 72 | display: none; 73 | } 74 | } 75 | @media (min-width: 768px) and (max-width: 979px) { 76 | .visible-tablet { 77 | display: block; 78 | } 79 | .hidden-tablet { 80 | display: none; 81 | } 82 | .hidden-desktop { 83 | display: block; 84 | } 85 | .visible-desktop { 86 | display: none; 87 | } 88 | } 89 | @media (max-width: 480px) { 90 | .nav-collapse { 91 | -webkit-transform: translate3d(0, 0, 0); 92 | } 93 | .page-header h1 small { 94 | display: block; 95 | line-height: 18px; 96 | } 97 | input[type="checkbox"], 98 | input[type="radio"] { 99 | border: 1px solid #ccc; 100 | } 101 | .form-horizontal .control-group > label { 102 | float: none; 103 | width: auto; 104 | padding-top: 0; 105 | text-align: left; 106 | } 107 | .form-horizontal .controls { 108 | margin-left: 0; 109 | } 110 | .form-horizontal .control-list { 111 | padding-top: 0; 112 | } 113 | .form-horizontal .form-actions { 114 | padding-left: 10px; 115 | padding-right: 10px; 116 | } 117 | .modal { 118 | position: absolute; 119 | top: 10px; 120 | left: 10px; 121 | right: 10px; 122 | width: auto; 123 | margin: 0; 124 | } 125 | .modal.fade.in { 126 | top: auto; 127 | } 128 | .modal-header .close { 129 | padding: 10px; 130 | margin: -10px; 131 | } 132 | .carousel-caption { 133 | position: static; 134 | } 135 | } 136 | @media (max-width: 767px) { 137 | body { 138 | padding-left: 20px; 139 | padding-right: 20px; 140 | } 141 | .navbar-fixed-top { 142 | margin-left: -20px; 143 | margin-right: -20px; 144 | } 145 | .container { 146 | width: auto; 147 | } 148 | .row-fluid { 149 | width: 100%; 150 | } 151 | .row { 152 | margin-left: 0; 153 | } 154 | .row > [class*="span"], 155 | .row-fluid > [class*="span"] { 156 | float: none; 157 | display: block; 158 | width: auto; 159 | margin: 0; 160 | } 161 | .thumbnails [class*="span"] { 162 | width: auto; 163 | } 164 | input[class*="span"], 165 | select[class*="span"], 166 | textarea[class*="span"], 167 | .uneditable-input { 168 | display: block; 169 | width: 100%; 170 | min-height: 28px; 171 | /* Make inputs at least the height of their button counterpart */ 172 | 173 | /* Makes inputs behave like true block-level elements */ 174 | 175 | -webkit-box-sizing: border-box; 176 | -moz-box-sizing: border-box; 177 | -ms-box-sizing: border-box; 178 | box-sizing: border-box; 179 | } 180 | .input-prepend input[class*="span"], 181 | .input-append input[class*="span"] { 182 | width: auto; 183 | } 184 | } 185 | @media (min-width: 768px) and (max-width: 979px) { 186 | .row { 187 | margin-left: -20px; 188 | *zoom: 1; 189 | } 190 | .row:before, 191 | .row:after { 192 | display: table; 193 | content: ""; 194 | } 195 | .row:after { 196 | clear: both; 197 | } 198 | [class*="span"] { 199 | float: left; 200 | margin-left: 20px; 201 | } 202 | .container, 203 | .navbar-fixed-top .container, 204 | .navbar-fixed-bottom .container { 205 | width: 724px; 206 | } 207 | .span12 { 208 | width: 724px; 209 | } 210 | .span11 { 211 | width: 662px; 212 | } 213 | .span10 { 214 | width: 600px; 215 | } 216 | .span9 { 217 | width: 538px; 218 | } 219 | .span8 { 220 | width: 476px; 221 | } 222 | .span7 { 223 | width: 414px; 224 | } 225 | .span6 { 226 | width: 352px; 227 | } 228 | .span5 { 229 | width: 290px; 230 | } 231 | .span4 { 232 | width: 228px; 233 | } 234 | .span3 { 235 | width: 166px; 236 | } 237 | .span2 { 238 | width: 104px; 239 | } 240 | .span1 { 241 | width: 42px; 242 | } 243 | .offset12 { 244 | margin-left: 764px; 245 | } 246 | .offset11 { 247 | margin-left: 702px; 248 | } 249 | .offset10 { 250 | margin-left: 640px; 251 | } 252 | .offset9 { 253 | margin-left: 578px; 254 | } 255 | .offset8 { 256 | margin-left: 516px; 257 | } 258 | .offset7 { 259 | margin-left: 454px; 260 | } 261 | .offset6 { 262 | margin-left: 392px; 263 | } 264 | .offset5 { 265 | margin-left: 330px; 266 | } 267 | .offset4 { 268 | margin-left: 268px; 269 | } 270 | .offset3 { 271 | margin-left: 206px; 272 | } 273 | .offset2 { 274 | margin-left: 144px; 275 | } 276 | .offset1 { 277 | margin-left: 82px; 278 | } 279 | .row-fluid { 280 | width: 100%; 281 | *zoom: 1; 282 | } 283 | .row-fluid:before, 284 | .row-fluid:after { 285 | display: table; 286 | content: ""; 287 | } 288 | .row-fluid:after { 289 | clear: both; 290 | } 291 | .row-fluid > [class*="span"] { 292 | float: left; 293 | margin-left: 2.762430939%; 294 | } 295 | .row-fluid > [class*="span"]:first-child { 296 | margin-left: 0; 297 | } 298 | .row-fluid > .span12 { 299 | width: 99.999999993%; 300 | } 301 | .row-fluid > .span11 { 302 | width: 91.436464082%; 303 | } 304 | .row-fluid > .span10 { 305 | width: 82.87292817100001%; 306 | } 307 | .row-fluid > .span9 { 308 | width: 74.30939226%; 309 | } 310 | .row-fluid > .span8 { 311 | width: 65.74585634900001%; 312 | } 313 | .row-fluid > .span7 { 314 | width: 57.182320438000005%; 315 | } 316 | .row-fluid > .span6 { 317 | width: 48.618784527%; 318 | } 319 | .row-fluid > .span5 { 320 | width: 40.055248616%; 321 | } 322 | .row-fluid > .span4 { 323 | width: 31.491712705%; 324 | } 325 | .row-fluid > .span3 { 326 | width: 22.928176794%; 327 | } 328 | .row-fluid > .span2 { 329 | width: 14.364640883%; 330 | } 331 | .row-fluid > .span1 { 332 | width: 5.801104972%; 333 | } 334 | input, 335 | textarea, 336 | .uneditable-input { 337 | margin-left: 0; 338 | } 339 | input.span12, textarea.span12, .uneditable-input.span12 { 340 | width: 714px; 341 | } 342 | input.span11, textarea.span11, .uneditable-input.span11 { 343 | width: 652px; 344 | } 345 | input.span10, textarea.span10, .uneditable-input.span10 { 346 | width: 590px; 347 | } 348 | input.span9, textarea.span9, .uneditable-input.span9 { 349 | width: 528px; 350 | } 351 | input.span8, textarea.span8, .uneditable-input.span8 { 352 | width: 466px; 353 | } 354 | input.span7, textarea.span7, .uneditable-input.span7 { 355 | width: 404px; 356 | } 357 | input.span6, textarea.span6, .uneditable-input.span6 { 358 | width: 342px; 359 | } 360 | input.span5, textarea.span5, .uneditable-input.span5 { 361 | width: 280px; 362 | } 363 | input.span4, textarea.span4, .uneditable-input.span4 { 364 | width: 218px; 365 | } 366 | input.span3, textarea.span3, .uneditable-input.span3 { 367 | width: 156px; 368 | } 369 | input.span2, textarea.span2, .uneditable-input.span2 { 370 | width: 94px; 371 | } 372 | input.span1, textarea.span1, .uneditable-input.span1 { 373 | width: 32px; 374 | } 375 | } 376 | @media (max-width: 979px) { 377 | body { 378 | padding-top: 0; 379 | } 380 | .navbar-fixed-top { 381 | position: static; 382 | margin-bottom: 18px; 383 | } 384 | .navbar-fixed-top .navbar-inner { 385 | padding: 5px; 386 | } 387 | .navbar .container { 388 | width: auto; 389 | padding: 0; 390 | } 391 | .navbar .brand { 392 | padding-left: 10px; 393 | padding-right: 10px; 394 | margin: 0 0 0 -5px; 395 | } 396 | .navbar .nav-collapse { 397 | clear: left; 398 | } 399 | .navbar .nav { 400 | float: none; 401 | margin: 0 0 9px; 402 | } 403 | .navbar .nav > li { 404 | float: none; 405 | } 406 | .navbar .nav > li > a { 407 | margin-bottom: 2px; 408 | } 409 | .navbar .nav > .divider-vertical { 410 | display: none; 411 | } 412 | .navbar .nav .nav-header { 413 | color: #999999; 414 | text-shadow: none; 415 | } 416 | .navbar .nav > li > a, 417 | .navbar .dropdown-menu a { 418 | padding: 6px 15px; 419 | font-weight: bold; 420 | color: #999999; 421 | -webkit-border-radius: 3px; 422 | -moz-border-radius: 3px; 423 | border-radius: 3px; 424 | } 425 | .navbar .dropdown-menu li + li a { 426 | margin-bottom: 2px; 427 | } 428 | .navbar .nav > li > a:hover, 429 | .navbar .dropdown-menu a:hover { 430 | background-color: #222222; 431 | } 432 | .navbar .dropdown-menu { 433 | position: static; 434 | top: auto; 435 | left: auto; 436 | float: none; 437 | display: block; 438 | max-width: none; 439 | margin: 0 15px; 440 | padding: 0; 441 | background-color: transparent; 442 | border: none; 443 | -webkit-border-radius: 0; 444 | -moz-border-radius: 0; 445 | border-radius: 0; 446 | -webkit-box-shadow: none; 447 | -moz-box-shadow: none; 448 | box-shadow: none; 449 | } 450 | .navbar .dropdown-menu:before, 451 | .navbar .dropdown-menu:after { 452 | display: none; 453 | } 454 | .navbar .dropdown-menu .divider { 455 | display: none; 456 | } 457 | .navbar-form, 458 | .navbar-search { 459 | float: none; 460 | padding: 9px 15px; 461 | margin: 9px 0; 462 | border-top: 1px solid #222222; 463 | border-bottom: 1px solid #222222; 464 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 465 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 466 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 467 | } 468 | .navbar .nav.pull-right { 469 | float: none; 470 | margin-left: 0; 471 | } 472 | .navbar-static .navbar-inner { 473 | padding-left: 10px; 474 | padding-right: 10px; 475 | } 476 | .btn-navbar { 477 | display: block; 478 | } 479 | .nav-collapse { 480 | overflow: hidden; 481 | height: 0; 482 | } 483 | } 484 | @media (min-width: 980px) { 485 | .nav-collapse.collapse { 486 | height: auto !important; 487 | overflow: visible !important; 488 | } 489 | } 490 | @media (min-width: 1200px) { 491 | .row { 492 | margin-left: -30px; 493 | *zoom: 1; 494 | } 495 | .row:before, 496 | .row:after { 497 | display: table; 498 | content: ""; 499 | } 500 | .row:after { 501 | clear: both; 502 | } 503 | [class*="span"] { 504 | float: left; 505 | margin-left: 30px; 506 | } 507 | .container, 508 | .navbar-fixed-top .container, 509 | .navbar-fixed-bottom .container { 510 | width: 1170px; 511 | } 512 | .span12 { 513 | width: 1170px; 514 | } 515 | .span11 { 516 | width: 1070px; 517 | } 518 | .span10 { 519 | width: 970px; 520 | } 521 | .span9 { 522 | width: 870px; 523 | } 524 | .span8 { 525 | width: 770px; 526 | } 527 | .span7 { 528 | width: 670px; 529 | } 530 | .span6 { 531 | width: 570px; 532 | } 533 | .span5 { 534 | width: 470px; 535 | } 536 | .span4 { 537 | width: 370px; 538 | } 539 | .span3 { 540 | width: 270px; 541 | } 542 | .span2 { 543 | width: 170px; 544 | } 545 | .span1 { 546 | width: 70px; 547 | } 548 | .offset12 { 549 | margin-left: 1230px; 550 | } 551 | .offset11 { 552 | margin-left: 1130px; 553 | } 554 | .offset10 { 555 | margin-left: 1030px; 556 | } 557 | .offset9 { 558 | margin-left: 930px; 559 | } 560 | .offset8 { 561 | margin-left: 830px; 562 | } 563 | .offset7 { 564 | margin-left: 730px; 565 | } 566 | .offset6 { 567 | margin-left: 630px; 568 | } 569 | .offset5 { 570 | margin-left: 530px; 571 | } 572 | .offset4 { 573 | margin-left: 430px; 574 | } 575 | .offset3 { 576 | margin-left: 330px; 577 | } 578 | .offset2 { 579 | margin-left: 230px; 580 | } 581 | .offset1 { 582 | margin-left: 130px; 583 | } 584 | .row-fluid { 585 | width: 100%; 586 | *zoom: 1; 587 | } 588 | .row-fluid:before, 589 | .row-fluid:after { 590 | display: table; 591 | content: ""; 592 | } 593 | .row-fluid:after { 594 | clear: both; 595 | } 596 | .row-fluid > [class*="span"] { 597 | float: left; 598 | margin-left: 2.564102564%; 599 | } 600 | .row-fluid > [class*="span"]:first-child { 601 | margin-left: 0; 602 | } 603 | .row-fluid > .span12 { 604 | width: 100%; 605 | } 606 | .row-fluid > .span11 { 607 | width: 91.45299145300001%; 608 | } 609 | .row-fluid > .span10 { 610 | width: 82.905982906%; 611 | } 612 | .row-fluid > .span9 { 613 | width: 74.358974359%; 614 | } 615 | .row-fluid > .span8 { 616 | width: 65.81196581200001%; 617 | } 618 | .row-fluid > .span7 { 619 | width: 57.264957265%; 620 | } 621 | .row-fluid > .span6 { 622 | width: 48.717948718%; 623 | } 624 | .row-fluid > .span5 { 625 | width: 40.170940171000005%; 626 | } 627 | .row-fluid > .span4 { 628 | width: 31.623931624%; 629 | } 630 | .row-fluid > .span3 { 631 | width: 23.076923077%; 632 | } 633 | .row-fluid > .span2 { 634 | width: 14.529914530000001%; 635 | } 636 | .row-fluid > .span1 { 637 | width: 5.982905983%; 638 | } 639 | input, 640 | textarea, 641 | .uneditable-input { 642 | margin-left: 0; 643 | } 644 | input.span12, textarea.span12, .uneditable-input.span12 { 645 | width: 1160px; 646 | } 647 | input.span11, textarea.span11, .uneditable-input.span11 { 648 | width: 1060px; 649 | } 650 | input.span10, textarea.span10, .uneditable-input.span10 { 651 | width: 960px; 652 | } 653 | input.span9, textarea.span9, .uneditable-input.span9 { 654 | width: 860px; 655 | } 656 | input.span8, textarea.span8, .uneditable-input.span8 { 657 | width: 760px; 658 | } 659 | input.span7, textarea.span7, .uneditable-input.span7 { 660 | width: 660px; 661 | } 662 | input.span6, textarea.span6, .uneditable-input.span6 { 663 | width: 560px; 664 | } 665 | input.span5, textarea.span5, .uneditable-input.span5 { 666 | width: 460px; 667 | } 668 | input.span4, textarea.span4, .uneditable-input.span4 { 669 | width: 360px; 670 | } 671 | input.span3, textarea.span3, .uneditable-input.span3 { 672 | width: 260px; 673 | } 674 | input.span2, textarea.span2, .uneditable-input.span2 { 675 | width: 160px; 676 | } 677 | input.span1, textarea.span1, .uneditable-input.span1 { 678 | width: 60px; 679 | } 680 | .thumbnails { 681 | margin-left: -30px; 682 | } 683 | .thumbnails > li { 684 | margin-left: 30px; 685 | } 686 | } 687 | -------------------------------------------------------------------------------- /themes/bootstrap/assets/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('