├── .gitignore ├── app.js ├── build.js ├── client ├── app │ ├── api │ │ ├── Api.js │ │ └── api.js │ ├── controllers │ │ ├── BaseController.js │ │ └── authorsController.js │ ├── core.js │ ├── model │ │ └── Author.js │ ├── templates │ │ └── Main.handlebars │ └── views │ │ └── MainView.js ├── css │ ├── bootstrap-responsive.css │ ├── bootstrap.css │ ├── client.css │ └── main.css ├── img │ ├── favicon.ico │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── index.html ├── lib │ ├── bootstrap │ │ └── bootstrap.js │ ├── emberjs │ │ ├── ember-0.9.8.1.js │ │ └── load.js │ ├── jquery │ │ └── jquery.js │ └── requirejs │ │ ├── plugins │ │ ├── order.js │ │ └── text.js │ │ └── require.js └── main.js ├── package.json ├── readme.md ├── server ├── api │ └── authors.js └── model │ └── Author.js └── test ├── create-author.sh ├── db └── start-db.sh ├── delete-author.sh ├── get-authors.sh └── jshint └── jshint-check.sh /.gitignore: -------------------------------------------------------------------------------- 1 | test/db/mongodb-*/ 2 | test/db/data/ 3 | node_modules/ 4 | nbproject/ 5 | client-build/ 6 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /*global console:false, __dirname:false, process:false */ 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var requirejs = require('requirejs'); 8 | 9 | requirejs.config({ 10 | nodeRequire: require 11 | }); 12 | 13 | requirejs([ 14 | 'express', 15 | 'mongoose', 16 | 'server/api/authors' 17 | ], function( 18 | express, 19 | mongoose, 20 | api_authors 21 | ){ 22 | "use strict"; 23 | 24 | // connect to MongoDB 25 | mongoose.connect('mongodb://localhost/example'); 26 | 27 | var app = express(); 28 | 29 | app.configure(function(){ 30 | app.use(express.favicon()); 31 | app.use(express.bodyParser()); 32 | app.use(express.methodOverride()); 33 | app.use(app.router); 34 | }); 35 | 36 | app.configure('development', function(){ 37 | app.use(express.logger('dev')); 38 | app.use(express['static'](__dirname + '/client')); 39 | app.use(express.errorHandler({ 40 | dumpExceptions: true, 41 | showStack: true 42 | })); 43 | }); 44 | 45 | app.configure('production', function(){ 46 | 47 | // build client if directory client-build not found 48 | var path = require('path'); 49 | if (!path.existsSync("client-build")) { 50 | require('./build'); 51 | } 52 | 53 | app.use(express['static'](__dirname + '/client-build')); 54 | }); 55 | 56 | // ROUTES 57 | app.get('/api/authors', api_authors.getAll); 58 | app.del('/api/authors/:id', api_authors.remove); 59 | app.post('/api/authors', api_authors.create); 60 | 61 | // HTTP 62 | var port = process.env.PORT || 3000; 63 | app.listen(port); 64 | 65 | console.log("Http server listening on port 3000"); 66 | }); 67 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | // Based on https://gist.github.com/1509135 2 | 3 | //Load the requirejs optimizer 4 | var requirejs = require('requirejs'), 5 | fs = require('fs'), 6 | crypto = require('crypto'); 7 | 8 | //Set up basic config, include config that is 9 | //common to all the requirejs.optimize() calls. 10 | var baseConfig = { 11 | baseUrl: "client", 12 | outputDir: 'client-build', 13 | optimize: "uglify", 14 | // optimize: "none", // For debugging built versions 15 | wrap: true, 16 | inlineText: true, 17 | preserveLicenseComments: false 18 | }; 19 | 20 | //Create an array of build configs, the baseConfig will 21 | //be mixed in to each one of these below. 22 | 23 | var configs = [ 24 | { 25 | include: ['lib/requirejs/require', 'main'], 26 | out: 'main.js' 27 | }, 28 | { 29 | cssIn: baseConfig.baseUrl + '/css/main.css', 30 | out: 'css/main.css' 31 | }, 32 | { 33 | appDir: [baseConfig.baseUrl + '/img'], 34 | dir: baseConfig.outputDir + '/img' 35 | } 36 | ]; 37 | 38 | // Function used to mix in baseConfig to a new config target 39 | function mix(target) { 40 | for (var prop in baseConfig) { 41 | if (baseConfig.hasOwnProperty(prop)) { 42 | target[prop] = baseConfig[prop]; 43 | } 44 | } 45 | if (target.out !== undefined) { 46 | target.out = baseConfig.outputDir + '/' + target.out; 47 | } 48 | return target; 49 | } 50 | 51 | // Function used to calculate MD5 checksum of file 52 | function md5file(filename) { 53 | return crypto.createHash('md5').update(fs.readFileSync(filename, 'utf8')).digest("hex"); 54 | } 55 | 56 | //Create a runner that will run a separate build for each item 57 | //in the configs array. Thanks to @jwhitley for this cleverness 58 | var runner = configs.reduceRight(function(prev, currentConfig) { 59 | return function (buildReportText) { 60 | if (buildReportText === undefined) { 61 | console.log('Optimizing...'); 62 | } else { 63 | console.log(buildReportText); 64 | } 65 | requirejs.optimize(mix(currentConfig), prev); 66 | }; 67 | }, function(buildReportText) { 68 | console.log(buildReportText); 69 | }); 70 | 71 | //Run the builds 72 | runner(); 73 | 74 | // set in index.html parameter to css and js url, forcing client to download new versions 75 | console.log('Optimized, modifying index.html...'); 76 | 77 | var cssVersion = md5file(baseConfig.outputDir + '/css/main.css'); 78 | var jsVersion = md5file(baseConfig.outputDir + '/main.js'); 79 | 80 | var content = fs.readFileSync(baseConfig.baseUrl + '/index.html', 'utf8'); 81 | 82 | content = content.replace('main.css', 'main.css?v=' + cssVersion) 83 | .replace('lib/requirejs/require.js', 'main.js?v=' + jsVersion) 84 | .replace('data-main="main"', ''); 85 | 86 | fs.writeFileSync(baseConfig.outputDir + '/index.html', content, 'utf8'); 87 | 88 | // remove empty file img/build.txt 89 | fs.unlinkSync(baseConfig.outputDir + '/img/build.txt'); 90 | 91 | console.log('Done'); 92 | -------------------------------------------------------------------------------- /client/app/api/Api.js: -------------------------------------------------------------------------------- 1 | /*global console:false */ 2 | 3 | define([ 4 | 'lib/emberjs/load' 5 | ],function( 6 | em 7 | ){ 8 | "use strict"; 9 | 10 | return em.Object.extend({ 11 | 12 | baseUrl: '/api', 13 | 14 | getAllAuthors: function(onDone, onFail) { 15 | this._get('/authors', onDone, onFail); 16 | }, 17 | 18 | removeAuthor: function(authorId, onDone, onFail) { 19 | this._delete('/authors/'+authorId, null, onDone, onFail); 20 | }, 21 | 22 | createAuthor: function(author, onDone, onFail) { 23 | this._post('/authors', author, onDone, onFail); 24 | }, 25 | 26 | _ajax: function(type, resourceUrl, data, onDone, onFail, onAlways) { 27 | console.log('_'+type+'('+this.baseUrl + resourceUrl+')'); 28 | return em.$.ajax({ 29 | url: this.baseUrl + resourceUrl, 30 | dataType: 'json', 31 | data: data, 32 | type: type 33 | }).done(function(json) { 34 | if (onDone) { 35 | onDone(json); 36 | } 37 | }).fail(function(error) { 38 | if (onFail) { 39 | onFail(error); 40 | } 41 | }).always(function() { 42 | if (onAlways) { 43 | onAlways(); 44 | } 45 | }); 46 | }, 47 | 48 | _get: function(resourceUrl, onDone, onFail, onAlways) { 49 | this._ajax('GET', resourceUrl, null, onDone, onFail, onAlways); 50 | }, 51 | 52 | _post: function(resourceUrl, data, onDone, onFail, onAlways) { 53 | this._ajax('POST', resourceUrl, data, onDone, onFail, onAlways); 54 | }, 55 | 56 | _put: function(resourceUrl, data, onDone, onFail, onAlways) { 57 | this._ajax('PUT', resourceUrl, data, onDone, onFail, onAlways); 58 | }, 59 | 60 | _delete: function(resourceUrl, data, onDone, onFail, onAlways) { 61 | this._ajax('DELETE', resourceUrl, data, onDone, onFail, onAlways); 62 | } 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /client/app/api/api.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'app/api/Api' 3 | ],function( 4 | Api 5 | ){ 6 | "use strict"; 7 | 8 | return Api.create({}); 9 | }); 10 | -------------------------------------------------------------------------------- /client/app/controllers/BaseController.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lib/emberjs/load', 3 | 'app/api/api' 4 | ],function( 5 | em, 6 | api 7 | ){ 8 | "use strict"; 9 | 10 | return em.ArrayController.extend({ 11 | 12 | resourceType: em.required(), 13 | 14 | api: api, 15 | 16 | load: function(json) { 17 | var resource = this.get('resourceType').create(json); 18 | this.pushObject(resource); 19 | }, 20 | 21 | loadAll: function(json) { 22 | for (var i=0; i < json.length; i++) { 23 | this.load(json[i]); 24 | } 25 | }, 26 | 27 | clearAll: function() { 28 | this.set('content', []); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /client/app/controllers/authorsController.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'app/model/Author', 3 | 'app/controllers/BaseController' 4 | ],function( 5 | Author, 6 | BaseController 7 | ){ 8 | "use strict"; 9 | 10 | return BaseController.create({ 11 | 12 | resourceType: Author, 13 | 14 | getAll: function(onDone, onFail) { 15 | this.clearAll(); 16 | this.api.getAllAuthors(function(json) { 17 | this.loadAll(json); 18 | if (onDone) { 19 | onDone(); 20 | } 21 | }.bind(this), function(err) { 22 | if (onFail) { 23 | onFail(err); 24 | } 25 | }.bind(this)); 26 | } 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /client/app/core.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lib/emberjs/load', 3 | "app/views/MainView", 4 | "app/controllers/authorsController" 5 | ],function( 6 | em, 7 | MainView, 8 | authorsController 9 | ){ 10 | "use strict"; 11 | 12 | em.App = em.Application.create({ 13 | mainView: MainView.create({}), 14 | authorsController: authorsController 15 | }); 16 | 17 | em.App.mainView.appendTo('body'); 18 | 19 | return em.App; 20 | }); 21 | -------------------------------------------------------------------------------- /client/app/model/Author.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'lib/emberjs/load' 3 | ],function( 4 | em 5 | ){ 6 | "use strict"; 7 | 8 | return em.Object.extend({ 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /client/app/templates/Main.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

nodejs-emberjs-mongodb

5 |

Proof of concept application.

6 |

Dependencies

7 | 17 |
18 |
19 |

Authors
Refresh

20 | 21 | 22 | 23 | 24 | 25 | 26 | {{#each Em.App.authorsController}} 27 | 28 | 29 | 30 | 31 | 32 | {{/each}} 33 | 34 |
IdName
{{_id}}{{name}}
remove
35 | 36 |
37 | {{view Ember.TextField valueBinding="newAuthor.name" id="authorName" placeholder="New author name"}} 38 |
Create new author
39 |
40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /client/app/views/MainView.js: -------------------------------------------------------------------------------- 1 | /*global console:false */ 2 | 3 | define([ 4 | 'lib/emberjs/load', 5 | 'app/model/Author', 6 | 'app/controllers/authorsController', 7 | 'lib/requirejs/plugins/text!app/templates/Main.handlebars' 8 | ],function( 9 | em, 10 | Author, 11 | authorsController, 12 | mainTemplateSource 13 | ){ 14 | "use strict"; 15 | 16 | return em.View.extend({ 17 | template: em.Handlebars.compile(mainTemplateSource), 18 | 19 | newAuthor: null, 20 | 21 | init: function() { 22 | this._super(); 23 | this.refresh(); 24 | this.newAuthor = Author.create(); 25 | }, 26 | 27 | refresh: function() { 28 | authorsController.getAll(); 29 | }, 30 | 31 | removeAuthor: function(event) { 32 | var authorId = event.context.get('_id'); 33 | 34 | authorsController.api.removeAuthor(authorId, function() { 35 | 36 | authorsController.removeObject(event.context); 37 | 38 | }.bind(this), function(err) { 39 | console.log('error', err); 40 | }.bind(this)); 41 | }, 42 | 43 | createAuthor: function() { 44 | 45 | var authorName = this.newAuthor.get('name'); 46 | 47 | if (authorName === undefined || authorName === null || authorName === '') { 48 | em.$('#authorName').focus(); 49 | return; 50 | } 51 | 52 | authorsController.api.createAuthor({ 53 | name: authorName 54 | },function(data) { 55 | 56 | this.newAuthor.set('_id', data.id); 57 | 58 | authorsController.pushObject(this.newAuthor); 59 | 60 | this.set('newAuthor', Author.create()); 61 | 62 | }.bind(this), function(err) { 63 | console.log('error', err); 64 | }.bind(this)); 65 | } 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /client/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.3 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 | 11 | .clearfix { 12 | *zoom: 1; 13 | } 14 | 15 | .clearfix:before, 16 | .clearfix:after { 17 | display: table; 18 | content: ""; 19 | } 20 | 21 | .clearfix:after { 22 | clear: both; 23 | } 24 | 25 | .hide-text { 26 | font: 0/0 a; 27 | color: transparent; 28 | text-shadow: none; 29 | background-color: transparent; 30 | border: 0; 31 | } 32 | 33 | .input-block-level { 34 | display: block; 35 | width: 100%; 36 | min-height: 28px; 37 | -webkit-box-sizing: border-box; 38 | -moz-box-sizing: border-box; 39 | -ms-box-sizing: border-box; 40 | box-sizing: border-box; 41 | } 42 | 43 | .hidden { 44 | display: none; 45 | visibility: hidden; 46 | } 47 | 48 | .visible-phone { 49 | display: none !important; 50 | } 51 | 52 | .visible-tablet { 53 | display: none !important; 54 | } 55 | 56 | .hidden-desktop { 57 | display: none !important; 58 | } 59 | 60 | @media (max-width: 767px) { 61 | .visible-phone { 62 | display: inherit !important; 63 | } 64 | .hidden-phone { 65 | display: none !important; 66 | } 67 | .hidden-desktop { 68 | display: inherit !important; 69 | } 70 | .visible-desktop { 71 | display: none !important; 72 | } 73 | } 74 | 75 | @media (min-width: 768px) and (max-width: 979px) { 76 | .visible-tablet { 77 | display: inherit !important; 78 | } 79 | .hidden-tablet { 80 | display: none !important; 81 | } 82 | .hidden-desktop { 83 | display: inherit !important; 84 | } 85 | .visible-desktop { 86 | display: none !important ; 87 | } 88 | } 89 | 90 | @media (max-width: 480px) { 91 | .nav-collapse { 92 | -webkit-transform: translate3d(0, 0, 0); 93 | } 94 | .page-header h1 small { 95 | display: block; 96 | line-height: 18px; 97 | } 98 | input[type="checkbox"], 99 | input[type="radio"] { 100 | border: 1px solid #ccc; 101 | } 102 | .form-horizontal .control-group > label { 103 | float: none; 104 | width: auto; 105 | padding-top: 0; 106 | text-align: left; 107 | } 108 | .form-horizontal .controls { 109 | margin-left: 0; 110 | } 111 | .form-horizontal .control-list { 112 | padding-top: 0; 113 | } 114 | .form-horizontal .form-actions { 115 | padding-right: 10px; 116 | padding-left: 10px; 117 | } 118 | .modal { 119 | position: absolute; 120 | top: 10px; 121 | right: 10px; 122 | left: 10px; 123 | width: auto; 124 | margin: 0; 125 | } 126 | .modal.fade.in { 127 | top: auto; 128 | } 129 | .modal-header .close { 130 | padding: 10px; 131 | margin: -10px; 132 | } 133 | .carousel-caption { 134 | position: static; 135 | } 136 | } 137 | 138 | @media (max-width: 767px) { 139 | body { 140 | padding-right: 20px; 141 | padding-left: 20px; 142 | } 143 | .navbar-fixed-top, 144 | .navbar-fixed-bottom { 145 | margin-right: -20px; 146 | margin-left: -20px; 147 | } 148 | .container-fluid { 149 | padding: 0; 150 | } 151 | .dl-horizontal dt { 152 | float: none; 153 | width: auto; 154 | clear: none; 155 | text-align: left; 156 | } 157 | .dl-horizontal dd { 158 | margin-left: 0; 159 | } 160 | .container { 161 | width: auto; 162 | } 163 | .row-fluid { 164 | width: 100%; 165 | } 166 | .row, 167 | .thumbnails { 168 | margin-left: 0; 169 | } 170 | [class*="span"], 171 | .row-fluid [class*="span"] { 172 | display: block; 173 | float: none; 174 | width: auto; 175 | margin-left: 0; 176 | } 177 | .input-large, 178 | .input-xlarge, 179 | .input-xxlarge, 180 | input[class*="span"], 181 | select[class*="span"], 182 | textarea[class*="span"], 183 | .uneditable-input { 184 | display: block; 185 | width: 100%; 186 | min-height: 28px; 187 | -webkit-box-sizing: border-box; 188 | -moz-box-sizing: border-box; 189 | -ms-box-sizing: border-box; 190 | box-sizing: border-box; 191 | } 192 | .input-prepend input, 193 | .input-append input, 194 | .input-prepend input[class*="span"], 195 | .input-append input[class*="span"] { 196 | display: inline-block; 197 | width: auto; 198 | } 199 | } 200 | 201 | @media (min-width: 768px) and (max-width: 979px) { 202 | .row { 203 | margin-left: -20px; 204 | *zoom: 1; 205 | } 206 | .row:before, 207 | .row:after { 208 | display: table; 209 | content: ""; 210 | } 211 | .row:after { 212 | clear: both; 213 | } 214 | [class*="span"] { 215 | float: left; 216 | margin-left: 20px; 217 | } 218 | .container, 219 | .navbar-fixed-top .container, 220 | .navbar-fixed-bottom .container { 221 | width: 724px; 222 | } 223 | .span12 { 224 | width: 724px; 225 | } 226 | .span11 { 227 | width: 662px; 228 | } 229 | .span10 { 230 | width: 600px; 231 | } 232 | .span9 { 233 | width: 538px; 234 | } 235 | .span8 { 236 | width: 476px; 237 | } 238 | .span7 { 239 | width: 414px; 240 | } 241 | .span6 { 242 | width: 352px; 243 | } 244 | .span5 { 245 | width: 290px; 246 | } 247 | .span4 { 248 | width: 228px; 249 | } 250 | .span3 { 251 | width: 166px; 252 | } 253 | .span2 { 254 | width: 104px; 255 | } 256 | .span1 { 257 | width: 42px; 258 | } 259 | .offset12 { 260 | margin-left: 764px; 261 | } 262 | .offset11 { 263 | margin-left: 702px; 264 | } 265 | .offset10 { 266 | margin-left: 640px; 267 | } 268 | .offset9 { 269 | margin-left: 578px; 270 | } 271 | .offset8 { 272 | margin-left: 516px; 273 | } 274 | .offset7 { 275 | margin-left: 454px; 276 | } 277 | .offset6 { 278 | margin-left: 392px; 279 | } 280 | .offset5 { 281 | margin-left: 330px; 282 | } 283 | .offset4 { 284 | margin-left: 268px; 285 | } 286 | .offset3 { 287 | margin-left: 206px; 288 | } 289 | .offset2 { 290 | margin-left: 144px; 291 | } 292 | .offset1 { 293 | margin-left: 82px; 294 | } 295 | .row-fluid { 296 | width: 100%; 297 | *zoom: 1; 298 | } 299 | .row-fluid:before, 300 | .row-fluid:after { 301 | display: table; 302 | content: ""; 303 | } 304 | .row-fluid:after { 305 | clear: both; 306 | } 307 | .row-fluid [class*="span"] { 308 | display: block; 309 | float: left; 310 | width: 100%; 311 | min-height: 28px; 312 | margin-left: 2.762430939%; 313 | *margin-left: 2.709239449638298%; 314 | -webkit-box-sizing: border-box; 315 | -moz-box-sizing: border-box; 316 | -ms-box-sizing: border-box; 317 | box-sizing: border-box; 318 | } 319 | .row-fluid [class*="span"]:first-child { 320 | margin-left: 0; 321 | } 322 | .row-fluid .span12 { 323 | width: 99.999999993%; 324 | *width: 99.9468085036383%; 325 | } 326 | .row-fluid .span11 { 327 | width: 91.436464082%; 328 | *width: 91.38327259263829%; 329 | } 330 | .row-fluid .span10 { 331 | width: 82.87292817100001%; 332 | *width: 82.8197366816383%; 333 | } 334 | .row-fluid .span9 { 335 | width: 74.30939226%; 336 | *width: 74.25620077063829%; 337 | } 338 | .row-fluid .span8 { 339 | width: 65.74585634900001%; 340 | *width: 65.6926648596383%; 341 | } 342 | .row-fluid .span7 { 343 | width: 57.182320438000005%; 344 | *width: 57.129128948638304%; 345 | } 346 | .row-fluid .span6 { 347 | width: 48.618784527%; 348 | *width: 48.5655930376383%; 349 | } 350 | .row-fluid .span5 { 351 | width: 40.055248616%; 352 | *width: 40.0020571266383%; 353 | } 354 | .row-fluid .span4 { 355 | width: 31.491712705%; 356 | *width: 31.4385212156383%; 357 | } 358 | .row-fluid .span3 { 359 | width: 22.928176794%; 360 | *width: 22.874985304638297%; 361 | } 362 | .row-fluid .span2 { 363 | width: 14.364640883%; 364 | *width: 14.311449393638298%; 365 | } 366 | .row-fluid .span1 { 367 | width: 5.801104972%; 368 | *width: 5.747913482638298%; 369 | } 370 | input, 371 | textarea, 372 | .uneditable-input { 373 | margin-left: 0; 374 | } 375 | input.span12, 376 | textarea.span12, 377 | .uneditable-input.span12 { 378 | width: 714px; 379 | } 380 | input.span11, 381 | textarea.span11, 382 | .uneditable-input.span11 { 383 | width: 652px; 384 | } 385 | input.span10, 386 | textarea.span10, 387 | .uneditable-input.span10 { 388 | width: 590px; 389 | } 390 | input.span9, 391 | textarea.span9, 392 | .uneditable-input.span9 { 393 | width: 528px; 394 | } 395 | input.span8, 396 | textarea.span8, 397 | .uneditable-input.span8 { 398 | width: 466px; 399 | } 400 | input.span7, 401 | textarea.span7, 402 | .uneditable-input.span7 { 403 | width: 404px; 404 | } 405 | input.span6, 406 | textarea.span6, 407 | .uneditable-input.span6 { 408 | width: 342px; 409 | } 410 | input.span5, 411 | textarea.span5, 412 | .uneditable-input.span5 { 413 | width: 280px; 414 | } 415 | input.span4, 416 | textarea.span4, 417 | .uneditable-input.span4 { 418 | width: 218px; 419 | } 420 | input.span3, 421 | textarea.span3, 422 | .uneditable-input.span3 { 423 | width: 156px; 424 | } 425 | input.span2, 426 | textarea.span2, 427 | .uneditable-input.span2 { 428 | width: 94px; 429 | } 430 | input.span1, 431 | textarea.span1, 432 | .uneditable-input.span1 { 433 | width: 32px; 434 | } 435 | } 436 | 437 | @media (min-width: 1200px) { 438 | .row { 439 | margin-left: -30px; 440 | *zoom: 1; 441 | } 442 | .row:before, 443 | .row:after { 444 | display: table; 445 | content: ""; 446 | } 447 | .row:after { 448 | clear: both; 449 | } 450 | [class*="span"] { 451 | float: left; 452 | margin-left: 30px; 453 | } 454 | .container, 455 | .navbar-fixed-top .container, 456 | .navbar-fixed-bottom .container { 457 | width: 1170px; 458 | } 459 | .span12 { 460 | width: 1170px; 461 | } 462 | .span11 { 463 | width: 1070px; 464 | } 465 | .span10 { 466 | width: 970px; 467 | } 468 | .span9 { 469 | width: 870px; 470 | } 471 | .span8 { 472 | width: 770px; 473 | } 474 | .span7 { 475 | width: 670px; 476 | } 477 | .span6 { 478 | width: 570px; 479 | } 480 | .span5 { 481 | width: 470px; 482 | } 483 | .span4 { 484 | width: 370px; 485 | } 486 | .span3 { 487 | width: 270px; 488 | } 489 | .span2 { 490 | width: 170px; 491 | } 492 | .span1 { 493 | width: 70px; 494 | } 495 | .offset12 { 496 | margin-left: 1230px; 497 | } 498 | .offset11 { 499 | margin-left: 1130px; 500 | } 501 | .offset10 { 502 | margin-left: 1030px; 503 | } 504 | .offset9 { 505 | margin-left: 930px; 506 | } 507 | .offset8 { 508 | margin-left: 830px; 509 | } 510 | .offset7 { 511 | margin-left: 730px; 512 | } 513 | .offset6 { 514 | margin-left: 630px; 515 | } 516 | .offset5 { 517 | margin-left: 530px; 518 | } 519 | .offset4 { 520 | margin-left: 430px; 521 | } 522 | .offset3 { 523 | margin-left: 330px; 524 | } 525 | .offset2 { 526 | margin-left: 230px; 527 | } 528 | .offset1 { 529 | margin-left: 130px; 530 | } 531 | .row-fluid { 532 | width: 100%; 533 | *zoom: 1; 534 | } 535 | .row-fluid:before, 536 | .row-fluid:after { 537 | display: table; 538 | content: ""; 539 | } 540 | .row-fluid:after { 541 | clear: both; 542 | } 543 | .row-fluid [class*="span"] { 544 | display: block; 545 | float: left; 546 | width: 100%; 547 | min-height: 28px; 548 | margin-left: 2.564102564%; 549 | *margin-left: 2.510911074638298%; 550 | -webkit-box-sizing: border-box; 551 | -moz-box-sizing: border-box; 552 | -ms-box-sizing: border-box; 553 | box-sizing: border-box; 554 | } 555 | .row-fluid [class*="span"]:first-child { 556 | margin-left: 0; 557 | } 558 | .row-fluid .span12 { 559 | width: 100%; 560 | *width: 99.94680851063829%; 561 | } 562 | .row-fluid .span11 { 563 | width: 91.45299145300001%; 564 | *width: 91.3997999636383%; 565 | } 566 | .row-fluid .span10 { 567 | width: 82.905982906%; 568 | *width: 82.8527914166383%; 569 | } 570 | .row-fluid .span9 { 571 | width: 74.358974359%; 572 | *width: 74.30578286963829%; 573 | } 574 | .row-fluid .span8 { 575 | width: 65.81196581200001%; 576 | *width: 65.7587743226383%; 577 | } 578 | .row-fluid .span7 { 579 | width: 57.264957265%; 580 | *width: 57.2117657756383%; 581 | } 582 | .row-fluid .span6 { 583 | width: 48.717948718%; 584 | *width: 48.6647572286383%; 585 | } 586 | .row-fluid .span5 { 587 | width: 40.170940171000005%; 588 | *width: 40.117748681638304%; 589 | } 590 | .row-fluid .span4 { 591 | width: 31.623931624%; 592 | *width: 31.5707401346383%; 593 | } 594 | .row-fluid .span3 { 595 | width: 23.076923077%; 596 | *width: 23.0237315876383%; 597 | } 598 | .row-fluid .span2 { 599 | width: 14.529914530000001%; 600 | *width: 14.4767230406383%; 601 | } 602 | .row-fluid .span1 { 603 | width: 5.982905983%; 604 | *width: 5.929714493638298%; 605 | } 606 | input, 607 | textarea, 608 | .uneditable-input { 609 | margin-left: 0; 610 | } 611 | input.span12, 612 | textarea.span12, 613 | .uneditable-input.span12 { 614 | width: 1160px; 615 | } 616 | input.span11, 617 | textarea.span11, 618 | .uneditable-input.span11 { 619 | width: 1060px; 620 | } 621 | input.span10, 622 | textarea.span10, 623 | .uneditable-input.span10 { 624 | width: 960px; 625 | } 626 | input.span9, 627 | textarea.span9, 628 | .uneditable-input.span9 { 629 | width: 860px; 630 | } 631 | input.span8, 632 | textarea.span8, 633 | .uneditable-input.span8 { 634 | width: 760px; 635 | } 636 | input.span7, 637 | textarea.span7, 638 | .uneditable-input.span7 { 639 | width: 660px; 640 | } 641 | input.span6, 642 | textarea.span6, 643 | .uneditable-input.span6 { 644 | width: 560px; 645 | } 646 | input.span5, 647 | textarea.span5, 648 | .uneditable-input.span5 { 649 | width: 460px; 650 | } 651 | input.span4, 652 | textarea.span4, 653 | .uneditable-input.span4 { 654 | width: 360px; 655 | } 656 | input.span3, 657 | textarea.span3, 658 | .uneditable-input.span3 { 659 | width: 260px; 660 | } 661 | input.span2, 662 | textarea.span2, 663 | .uneditable-input.span2 { 664 | width: 160px; 665 | } 666 | input.span1, 667 | textarea.span1, 668 | .uneditable-input.span1 { 669 | width: 60px; 670 | } 671 | .thumbnails { 672 | margin-left: -30px; 673 | } 674 | .thumbnails > li { 675 | margin-left: 30px; 676 | } 677 | .row-fluid .thumbnails { 678 | margin-left: 0; 679 | } 680 | } 681 | 682 | @media (max-width: 979px) { 683 | body { 684 | padding-top: 0; 685 | } 686 | .navbar-fixed-top { 687 | position: static; 688 | margin-bottom: 18px; 689 | } 690 | .navbar-fixed-top .navbar-inner { 691 | padding: 5px; 692 | } 693 | .navbar .container { 694 | width: auto; 695 | padding: 0; 696 | } 697 | .navbar .brand { 698 | padding-right: 10px; 699 | padding-left: 10px; 700 | margin: 0 0 0 -5px; 701 | } 702 | .nav-collapse { 703 | clear: both; 704 | } 705 | .nav-collapse .nav { 706 | float: none; 707 | margin: 0 0 9px; 708 | } 709 | .nav-collapse .nav > li { 710 | float: none; 711 | } 712 | .nav-collapse .nav > li > a { 713 | margin-bottom: 2px; 714 | } 715 | .nav-collapse .nav > .divider-vertical { 716 | display: none; 717 | } 718 | .nav-collapse .nav .nav-header { 719 | color: #999999; 720 | text-shadow: none; 721 | } 722 | .nav-collapse .nav > li > a, 723 | .nav-collapse .dropdown-menu a { 724 | padding: 6px 15px; 725 | font-weight: bold; 726 | color: #999999; 727 | -webkit-border-radius: 3px; 728 | -moz-border-radius: 3px; 729 | border-radius: 3px; 730 | } 731 | .nav-collapse .btn { 732 | padding: 4px 10px 4px; 733 | font-weight: normal; 734 | -webkit-border-radius: 4px; 735 | -moz-border-radius: 4px; 736 | border-radius: 4px; 737 | } 738 | .nav-collapse .dropdown-menu li + li a { 739 | margin-bottom: 2px; 740 | } 741 | .nav-collapse .nav > li > a:hover, 742 | .nav-collapse .dropdown-menu a:hover { 743 | background-color: #222222; 744 | } 745 | .nav-collapse.in .btn-group { 746 | padding: 0; 747 | margin-top: 5px; 748 | } 749 | .nav-collapse .dropdown-menu { 750 | position: static; 751 | top: auto; 752 | left: auto; 753 | display: block; 754 | float: none; 755 | max-width: none; 756 | padding: 0; 757 | margin: 0 15px; 758 | background-color: transparent; 759 | border: none; 760 | -webkit-border-radius: 0; 761 | -moz-border-radius: 0; 762 | border-radius: 0; 763 | -webkit-box-shadow: none; 764 | -moz-box-shadow: none; 765 | box-shadow: none; 766 | } 767 | .nav-collapse .dropdown-menu:before, 768 | .nav-collapse .dropdown-menu:after { 769 | display: none; 770 | } 771 | .nav-collapse .dropdown-menu .divider { 772 | display: none; 773 | } 774 | .nav-collapse .navbar-form, 775 | .nav-collapse .navbar-search { 776 | float: none; 777 | padding: 9px 15px; 778 | margin: 9px 0; 779 | border-top: 1px solid #222222; 780 | border-bottom: 1px solid #222222; 781 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 782 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 783 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 784 | } 785 | .navbar .nav-collapse .nav.pull-right { 786 | float: none; 787 | margin-left: 0; 788 | } 789 | .nav-collapse, 790 | .nav-collapse.collapse { 791 | height: 0; 792 | overflow: hidden; 793 | } 794 | .navbar .btn-navbar { 795 | display: block; 796 | } 797 | .navbar-static .navbar-inner { 798 | padding-right: 10px; 799 | padding-left: 10px; 800 | } 801 | } 802 | 803 | @media (min-width: 980px) { 804 | .nav-collapse.collapse { 805 | height: auto !important; 806 | overflow: visible !important; 807 | } 808 | } 809 | -------------------------------------------------------------------------------- /client/css/client.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abelaska/nodejs-emberjs-mongodb/661b87b995352011ad58805533df852a3360cbee/client/css/client.css -------------------------------------------------------------------------------- /client/css/main.css: -------------------------------------------------------------------------------- 1 | @import url("bootstrap.css"); 2 | @import url("bootstrap-responsive.css"); 3 | @import url("client.css"); 4 | -------------------------------------------------------------------------------- /client/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abelaska/nodejs-emberjs-mongodb/661b87b995352011ad58805533df852a3360cbee/client/img/favicon.ico -------------------------------------------------------------------------------- /client/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abelaska/nodejs-emberjs-mongodb/661b87b995352011ad58805533df852a3360cbee/client/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /client/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abelaska/nodejs-emberjs-mongodb/661b87b995352011ad58805533df852a3360cbee/client/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Node.js + Require.js + Express.js + Ember.js + Mongoose.js + MongoDB + Twitter Bootstrap 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /client/lib/bootstrap/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | $(function () { 24 | 25 | "use strict"; // jshint ;_; 26 | 27 | 28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 29 | * ======================================================= */ 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd' 40 | , 'msTransition' : 'MSTransitionEnd' 41 | , 'transition' : 'transitionend' 42 | } 43 | , name 44 | 45 | for (name in transEndEventNames){ 46 | if (el.style[name] !== undefined) { 47 | return transEndEventNames[name] 48 | } 49 | } 50 | 51 | }()) 52 | 53 | return transitionEnd && { 54 | end: transitionEnd 55 | } 56 | 57 | })() 58 | 59 | }) 60 | 61 | }(window.jQuery);/* ========================================================== 62 | * bootstrap-alert.js v2.0.3 63 | * http://twitter.github.com/bootstrap/javascript.html#alerts 64 | * ========================================================== 65 | * Copyright 2012 Twitter, Inc. 66 | * 67 | * Licensed under the Apache License, Version 2.0 (the "License"); 68 | * you may not use this file except in compliance with the License. 69 | * You may obtain a copy of the License at 70 | * 71 | * http://www.apache.org/licenses/LICENSE-2.0 72 | * 73 | * Unless required by applicable law or agreed to in writing, software 74 | * distributed under the License is distributed on an "AS IS" BASIS, 75 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | * See the License for the specific language governing permissions and 77 | * limitations under the License. 78 | * ========================================================== */ 79 | 80 | 81 | !function ($) { 82 | 83 | "use strict"; // jshint ;_; 84 | 85 | 86 | /* ALERT CLASS DEFINITION 87 | * ====================== */ 88 | 89 | var dismiss = '[data-dismiss="alert"]' 90 | , Alert = function (el) { 91 | $(el).on('click', dismiss, this.close) 92 | } 93 | 94 | Alert.prototype.close = function (e) { 95 | var $this = $(this) 96 | , selector = $this.attr('data-target') 97 | , $parent 98 | 99 | if (!selector) { 100 | selector = $this.attr('href') 101 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 102 | } 103 | 104 | $parent = $(selector) 105 | 106 | e && e.preventDefault() 107 | 108 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 109 | 110 | $parent.trigger(e = $.Event('close')) 111 | 112 | if (e.isDefaultPrevented()) return 113 | 114 | $parent.removeClass('in') 115 | 116 | function removeElement() { 117 | $parent 118 | .trigger('closed') 119 | .remove() 120 | } 121 | 122 | $.support.transition && $parent.hasClass('fade') ? 123 | $parent.on($.support.transition.end, removeElement) : 124 | removeElement() 125 | } 126 | 127 | 128 | /* ALERT PLUGIN DEFINITION 129 | * ======================= */ 130 | 131 | $.fn.alert = function (option) { 132 | return this.each(function () { 133 | var $this = $(this) 134 | , data = $this.data('alert') 135 | if (!data) $this.data('alert', (data = new Alert(this))) 136 | if (typeof option == 'string') data[option].call($this) 137 | }) 138 | } 139 | 140 | $.fn.alert.Constructor = Alert 141 | 142 | 143 | /* ALERT DATA-API 144 | * ============== */ 145 | 146 | $(function () { 147 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) 148 | }) 149 | 150 | }(window.jQuery);/* ============================================================ 151 | * bootstrap-button.js v2.0.3 152 | * http://twitter.github.com/bootstrap/javascript.html#buttons 153 | * ============================================================ 154 | * Copyright 2012 Twitter, Inc. 155 | * 156 | * Licensed under the Apache License, Version 2.0 (the "License"); 157 | * you may not use this file except in compliance with the License. 158 | * You may obtain a copy of the License at 159 | * 160 | * http://www.apache.org/licenses/LICENSE-2.0 161 | * 162 | * Unless required by applicable law or agreed to in writing, software 163 | * distributed under the License is distributed on an "AS IS" BASIS, 164 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 165 | * See the License for the specific language governing permissions and 166 | * limitations under the License. 167 | * ============================================================ */ 168 | 169 | 170 | !function ($) { 171 | 172 | "use strict"; // jshint ;_; 173 | 174 | 175 | /* BUTTON PUBLIC CLASS DEFINITION 176 | * ============================== */ 177 | 178 | var Button = function (element, options) { 179 | this.$element = $(element) 180 | this.options = $.extend({}, $.fn.button.defaults, options) 181 | } 182 | 183 | Button.prototype.setState = function (state) { 184 | var d = 'disabled' 185 | , $el = this.$element 186 | , data = $el.data() 187 | , val = $el.is('input') ? 'val' : 'html' 188 | 189 | state = state + 'Text' 190 | data.resetText || $el.data('resetText', $el[val]()) 191 | 192 | $el[val](data[state] || this.options[state]) 193 | 194 | // push to event loop to allow forms to submit 195 | setTimeout(function () { 196 | state == 'loadingText' ? 197 | $el.addClass(d).attr(d, d) : 198 | $el.removeClass(d).removeAttr(d) 199 | }, 0) 200 | } 201 | 202 | Button.prototype.toggle = function () { 203 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]') 204 | 205 | $parent && $parent 206 | .find('.active') 207 | .removeClass('active') 208 | 209 | this.$element.toggleClass('active') 210 | } 211 | 212 | 213 | /* BUTTON PLUGIN DEFINITION 214 | * ======================== */ 215 | 216 | $.fn.button = function (option) { 217 | return this.each(function () { 218 | var $this = $(this) 219 | , data = $this.data('button') 220 | , options = typeof option == 'object' && option 221 | if (!data) $this.data('button', (data = new Button(this, options))) 222 | if (option == 'toggle') data.toggle() 223 | else if (option) data.setState(option) 224 | }) 225 | } 226 | 227 | $.fn.button.defaults = { 228 | loadingText: 'loading...' 229 | } 230 | 231 | $.fn.button.Constructor = Button 232 | 233 | 234 | /* BUTTON DATA-API 235 | * =============== */ 236 | 237 | $(function () { 238 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { 239 | var $btn = $(e.target) 240 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 241 | $btn.button('toggle') 242 | }) 243 | }) 244 | 245 | }(window.jQuery);/* ========================================================== 246 | * bootstrap-carousel.js v2.0.3 247 | * http://twitter.github.com/bootstrap/javascript.html#carousel 248 | * ========================================================== 249 | * Copyright 2012 Twitter, Inc. 250 | * 251 | * Licensed under the Apache License, Version 2.0 (the "License"); 252 | * you may not use this file except in compliance with the License. 253 | * You may obtain a copy of the License at 254 | * 255 | * http://www.apache.org/licenses/LICENSE-2.0 256 | * 257 | * Unless required by applicable law or agreed to in writing, software 258 | * distributed under the License is distributed on an "AS IS" BASIS, 259 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 260 | * See the License for the specific language governing permissions and 261 | * limitations under the License. 262 | * ========================================================== */ 263 | 264 | 265 | !function ($) { 266 | 267 | "use strict"; // jshint ;_; 268 | 269 | 270 | /* CAROUSEL CLASS DEFINITION 271 | * ========================= */ 272 | 273 | var Carousel = function (element, options) { 274 | this.$element = $(element) 275 | this.options = options 276 | this.options.slide && this.slide(this.options.slide) 277 | this.options.pause == 'hover' && this.$element 278 | .on('mouseenter', $.proxy(this.pause, this)) 279 | .on('mouseleave', $.proxy(this.cycle, this)) 280 | } 281 | 282 | Carousel.prototype = { 283 | 284 | cycle: function (e) { 285 | if (!e) this.paused = false 286 | this.options.interval 287 | && !this.paused 288 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 289 | return this 290 | } 291 | 292 | , to: function (pos) { 293 | var $active = this.$element.find('.active') 294 | , children = $active.parent().children() 295 | , activePos = children.index($active) 296 | , that = this 297 | 298 | if (pos > (children.length - 1) || pos < 0) return 299 | 300 | if (this.sliding) { 301 | return this.$element.one('slid', function () { 302 | that.to(pos) 303 | }) 304 | } 305 | 306 | if (activePos == pos) { 307 | return this.pause().cycle() 308 | } 309 | 310 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) 311 | } 312 | 313 | , pause: function (e) { 314 | if (!e) this.paused = true 315 | clearInterval(this.interval) 316 | this.interval = null 317 | return this 318 | } 319 | 320 | , next: function () { 321 | if (this.sliding) return 322 | return this.slide('next') 323 | } 324 | 325 | , prev: function () { 326 | if (this.sliding) return 327 | return this.slide('prev') 328 | } 329 | 330 | , slide: function (type, next) { 331 | var $active = this.$element.find('.active') 332 | , $next = next || $active[type]() 333 | , isCycling = this.interval 334 | , direction = type == 'next' ? 'left' : 'right' 335 | , fallback = type == 'next' ? 'first' : 'last' 336 | , that = this 337 | , e = $.Event('slide') 338 | 339 | this.sliding = true 340 | 341 | isCycling && this.pause() 342 | 343 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 344 | 345 | if ($next.hasClass('active')) return 346 | 347 | if ($.support.transition && this.$element.hasClass('slide')) { 348 | this.$element.trigger(e) 349 | if (e.isDefaultPrevented()) return 350 | $next.addClass(type) 351 | $next[0].offsetWidth // force reflow 352 | $active.addClass(direction) 353 | $next.addClass(direction) 354 | this.$element.one($.support.transition.end, function () { 355 | $next.removeClass([type, direction].join(' ')).addClass('active') 356 | $active.removeClass(['active', direction].join(' ')) 357 | that.sliding = false 358 | setTimeout(function () { that.$element.trigger('slid') }, 0) 359 | }) 360 | } else { 361 | this.$element.trigger(e) 362 | if (e.isDefaultPrevented()) return 363 | $active.removeClass('active') 364 | $next.addClass('active') 365 | this.sliding = false 366 | this.$element.trigger('slid') 367 | } 368 | 369 | isCycling && this.cycle() 370 | 371 | return this 372 | } 373 | 374 | } 375 | 376 | 377 | /* CAROUSEL PLUGIN DEFINITION 378 | * ========================== */ 379 | 380 | $.fn.carousel = function (option) { 381 | return this.each(function () { 382 | var $this = $(this) 383 | , data = $this.data('carousel') 384 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) 385 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 386 | if (typeof option == 'number') data.to(option) 387 | else if (typeof option == 'string' || (option = options.slide)) data[option]() 388 | else if (options.interval) data.cycle() 389 | }) 390 | } 391 | 392 | $.fn.carousel.defaults = { 393 | interval: 5000 394 | , pause: 'hover' 395 | } 396 | 397 | $.fn.carousel.Constructor = Carousel 398 | 399 | 400 | /* CAROUSEL DATA-API 401 | * ================= */ 402 | 403 | $(function () { 404 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { 405 | var $this = $(this), href 406 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 407 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) 408 | $target.carousel(options) 409 | e.preventDefault() 410 | }) 411 | }) 412 | 413 | }(window.jQuery);/* ============================================================= 414 | * bootstrap-collapse.js v2.0.3 415 | * http://twitter.github.com/bootstrap/javascript.html#collapse 416 | * ============================================================= 417 | * Copyright 2012 Twitter, Inc. 418 | * 419 | * Licensed under the Apache License, Version 2.0 (the "License"); 420 | * you may not use this file except in compliance with the License. 421 | * You may obtain a copy of the License at 422 | * 423 | * http://www.apache.org/licenses/LICENSE-2.0 424 | * 425 | * Unless required by applicable law or agreed to in writing, software 426 | * distributed under the License is distributed on an "AS IS" BASIS, 427 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 428 | * See the License for the specific language governing permissions and 429 | * limitations under the License. 430 | * ============================================================ */ 431 | 432 | 433 | !function ($) { 434 | 435 | "use strict"; // jshint ;_; 436 | 437 | 438 | /* COLLAPSE PUBLIC CLASS DEFINITION 439 | * ================================ */ 440 | 441 | var Collapse = function (element, options) { 442 | this.$element = $(element) 443 | this.options = $.extend({}, $.fn.collapse.defaults, options) 444 | 445 | if (this.options.parent) { 446 | this.$parent = $(this.options.parent) 447 | } 448 | 449 | this.options.toggle && this.toggle() 450 | } 451 | 452 | Collapse.prototype = { 453 | 454 | constructor: Collapse 455 | 456 | , dimension: function () { 457 | var hasWidth = this.$element.hasClass('width') 458 | return hasWidth ? 'width' : 'height' 459 | } 460 | 461 | , show: function () { 462 | var dimension 463 | , scroll 464 | , actives 465 | , hasData 466 | 467 | if (this.transitioning) return 468 | 469 | dimension = this.dimension() 470 | scroll = $.camelCase(['scroll', dimension].join('-')) 471 | actives = this.$parent && this.$parent.find('> .accordion-group > .in') 472 | 473 | if (actives && actives.length) { 474 | hasData = actives.data('collapse') 475 | if (hasData && hasData.transitioning) return 476 | actives.collapse('hide') 477 | hasData || actives.data('collapse', null) 478 | } 479 | 480 | this.$element[dimension](0) 481 | this.transition('addClass', $.Event('show'), 'shown') 482 | this.$element[dimension](this.$element[0][scroll]) 483 | } 484 | 485 | , hide: function () { 486 | var dimension 487 | if (this.transitioning) return 488 | dimension = this.dimension() 489 | this.reset(this.$element[dimension]()) 490 | this.transition('removeClass', $.Event('hide'), 'hidden') 491 | this.$element[dimension](0) 492 | } 493 | 494 | , reset: function (size) { 495 | var dimension = this.dimension() 496 | 497 | this.$element 498 | .removeClass('collapse') 499 | [dimension](size || 'auto') 500 | [0].offsetWidth 501 | 502 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') 503 | 504 | return this 505 | } 506 | 507 | , transition: function (method, startEvent, completeEvent) { 508 | var that = this 509 | , complete = function () { 510 | if (startEvent.type == 'show') that.reset() 511 | that.transitioning = 0 512 | that.$element.trigger(completeEvent) 513 | } 514 | 515 | this.$element.trigger(startEvent) 516 | 517 | if (startEvent.isDefaultPrevented()) return 518 | 519 | this.transitioning = 1 520 | 521 | this.$element[method]('in') 522 | 523 | $.support.transition && this.$element.hasClass('collapse') ? 524 | this.$element.one($.support.transition.end, complete) : 525 | complete() 526 | } 527 | 528 | , toggle: function () { 529 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 530 | } 531 | 532 | } 533 | 534 | 535 | /* COLLAPSIBLE PLUGIN DEFINITION 536 | * ============================== */ 537 | 538 | $.fn.collapse = function (option) { 539 | return this.each(function () { 540 | var $this = $(this) 541 | , data = $this.data('collapse') 542 | , options = typeof option == 'object' && option 543 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 544 | if (typeof option == 'string') data[option]() 545 | }) 546 | } 547 | 548 | $.fn.collapse.defaults = { 549 | toggle: true 550 | } 551 | 552 | $.fn.collapse.Constructor = Collapse 553 | 554 | 555 | /* COLLAPSIBLE DATA-API 556 | * ==================== */ 557 | 558 | $(function () { 559 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 560 | var $this = $(this), href 561 | , target = $this.attr('data-target') 562 | || e.preventDefault() 563 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 564 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 565 | $(target).collapse(option) 566 | }) 567 | }) 568 | 569 | }(window.jQuery);/* ============================================================ 570 | * bootstrap-dropdown.js v2.0.3 571 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 572 | * ============================================================ 573 | * Copyright 2012 Twitter, Inc. 574 | * 575 | * Licensed under the Apache License, Version 2.0 (the "License"); 576 | * you may not use this file except in compliance with the License. 577 | * You may obtain a copy of the License at 578 | * 579 | * http://www.apache.org/licenses/LICENSE-2.0 580 | * 581 | * Unless required by applicable law or agreed to in writing, software 582 | * distributed under the License is distributed on an "AS IS" BASIS, 583 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 584 | * See the License for the specific language governing permissions and 585 | * limitations under the License. 586 | * ============================================================ */ 587 | 588 | 589 | !function ($) { 590 | 591 | "use strict"; // jshint ;_; 592 | 593 | 594 | /* DROPDOWN CLASS DEFINITION 595 | * ========================= */ 596 | 597 | var toggle = '[data-toggle="dropdown"]' 598 | , Dropdown = function (element) { 599 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 600 | $('html').on('click.dropdown.data-api', function () { 601 | $el.parent().removeClass('open') 602 | }) 603 | } 604 | 605 | Dropdown.prototype = { 606 | 607 | constructor: Dropdown 608 | 609 | , toggle: function (e) { 610 | var $this = $(this) 611 | , $parent 612 | , selector 613 | , isActive 614 | 615 | if ($this.is('.disabled, :disabled')) return 616 | 617 | selector = $this.attr('data-target') 618 | 619 | if (!selector) { 620 | selector = $this.attr('href') 621 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 622 | } 623 | 624 | $parent = $(selector) 625 | $parent.length || ($parent = $this.parent()) 626 | 627 | isActive = $parent.hasClass('open') 628 | 629 | clearMenus() 630 | 631 | if (!isActive) $parent.toggleClass('open') 632 | 633 | return false 634 | } 635 | 636 | } 637 | 638 | function clearMenus() { 639 | $(toggle).parent().removeClass('open') 640 | } 641 | 642 | 643 | /* DROPDOWN PLUGIN DEFINITION 644 | * ========================== */ 645 | 646 | $.fn.dropdown = function (option) { 647 | return this.each(function () { 648 | var $this = $(this) 649 | , data = $this.data('dropdown') 650 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 651 | if (typeof option == 'string') data[option].call($this) 652 | }) 653 | } 654 | 655 | $.fn.dropdown.Constructor = Dropdown 656 | 657 | 658 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 659 | * =================================== */ 660 | 661 | $(function () { 662 | $('html').on('click.dropdown.data-api', clearMenus) 663 | $('body') 664 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() }) 665 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 666 | }) 667 | 668 | }(window.jQuery);/* ========================================================= 669 | * bootstrap-modal.js v2.0.3 670 | * http://twitter.github.com/bootstrap/javascript.html#modals 671 | * ========================================================= 672 | * Copyright 2012 Twitter, Inc. 673 | * 674 | * Licensed under the Apache License, Version 2.0 (the "License"); 675 | * you may not use this file except in compliance with the License. 676 | * You may obtain a copy of the License at 677 | * 678 | * http://www.apache.org/licenses/LICENSE-2.0 679 | * 680 | * Unless required by applicable law or agreed to in writing, software 681 | * distributed under the License is distributed on an "AS IS" BASIS, 682 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 683 | * See the License for the specific language governing permissions and 684 | * limitations under the License. 685 | * ========================================================= */ 686 | 687 | 688 | !function ($) { 689 | 690 | "use strict"; // jshint ;_; 691 | 692 | 693 | /* MODAL CLASS DEFINITION 694 | * ====================== */ 695 | 696 | var Modal = function (content, options) { 697 | this.options = options 698 | this.$element = $(content) 699 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 700 | } 701 | 702 | Modal.prototype = { 703 | 704 | constructor: Modal 705 | 706 | , toggle: function () { 707 | return this[!this.isShown ? 'show' : 'hide']() 708 | } 709 | 710 | , show: function () { 711 | var that = this 712 | , e = $.Event('show') 713 | 714 | this.$element.trigger(e) 715 | 716 | if (this.isShown || e.isDefaultPrevented()) return 717 | 718 | $('body').addClass('modal-open') 719 | 720 | this.isShown = true 721 | 722 | escape.call(this) 723 | backdrop.call(this, function () { 724 | var transition = $.support.transition && that.$element.hasClass('fade') 725 | 726 | if (!that.$element.parent().length) { 727 | that.$element.appendTo(document.body) //don't move modals dom position 728 | } 729 | 730 | that.$element 731 | .show() 732 | 733 | if (transition) { 734 | that.$element[0].offsetWidth // force reflow 735 | } 736 | 737 | that.$element.addClass('in') 738 | 739 | transition ? 740 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : 741 | that.$element.trigger('shown') 742 | 743 | }) 744 | } 745 | 746 | , hide: function (e) { 747 | e && e.preventDefault() 748 | 749 | var that = this 750 | 751 | e = $.Event('hide') 752 | 753 | this.$element.trigger(e) 754 | 755 | if (!this.isShown || e.isDefaultPrevented()) return 756 | 757 | this.isShown = false 758 | 759 | $('body').removeClass('modal-open') 760 | 761 | escape.call(this) 762 | 763 | this.$element.removeClass('in') 764 | 765 | $.support.transition && this.$element.hasClass('fade') ? 766 | hideWithTransition.call(this) : 767 | hideModal.call(this) 768 | } 769 | 770 | } 771 | 772 | 773 | /* MODAL PRIVATE METHODS 774 | * ===================== */ 775 | 776 | function hideWithTransition() { 777 | var that = this 778 | , timeout = setTimeout(function () { 779 | that.$element.off($.support.transition.end) 780 | hideModal.call(that) 781 | }, 500) 782 | 783 | this.$element.one($.support.transition.end, function () { 784 | clearTimeout(timeout) 785 | hideModal.call(that) 786 | }) 787 | } 788 | 789 | function hideModal(that) { 790 | this.$element 791 | .hide() 792 | .trigger('hidden') 793 | 794 | backdrop.call(this) 795 | } 796 | 797 | function backdrop(callback) { 798 | var that = this 799 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 800 | 801 | if (this.isShown && this.options.backdrop) { 802 | var doAnimate = $.support.transition && animate 803 | 804 | this.$backdrop = $('