├── .bowerrc ├── .gitignore ├── .travis.yml ├── Contributing.md ├── LICENSE ├── Readme.md ├── _img ├── fields │ ├── array.png │ ├── date.png │ ├── ref.png │ ├── text.png │ └── textarea.png ├── landing.png └── sriracha.jpg ├── bower.json ├── examples ├── advanced │ ├── mock.js │ ├── models │ │ ├── Post.js │ │ └── User.js │ ├── passport.js │ └── server.js └── simple │ ├── mock.js │ ├── models │ ├── Post.js │ └── User.js │ └── server.js ├── gulpfile.js ├── index.js ├── lib ├── controllers │ └── main.js ├── models │ ├── Collection.js │ └── Document.js ├── options.js ├── static │ ├── css │ │ ├── bootstrap-rtl.css │ │ ├── bootstrap-rtl.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── plugins │ │ │ └── morris.css │ │ ├── sb-admin-rtl.css │ │ └── sb-admin.css │ ├── favicon.ico │ ├── font-awesome │ │ ├── css │ │ │ ├── font-awesome.css │ │ │ └── font-awesome.min.css │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── less │ │ │ ├── bordered-pulled.less │ │ │ ├── core.less │ │ │ ├── fixed-width.less │ │ │ ├── font-awesome.less │ │ │ ├── icons.less │ │ │ ├── larger.less │ │ │ ├── list.less │ │ │ ├── mixins.less │ │ │ ├── path.less │ │ │ ├── rotated-flipped.less │ │ │ ├── spinning.less │ │ │ ├── stacked.less │ │ │ └── variables.less │ │ └── scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _core.scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _icons.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _mixins.scss │ │ │ ├── _path.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _spinning.scss │ │ │ ├── _stacked.scss │ │ │ ├── _variables.scss │ │ │ └── font-awesome.scss │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── jquery.js │ │ └── plugins │ │ ├── flot │ │ ├── excanvas.min.js │ │ ├── flot-data.js │ │ ├── jquery.flot.js │ │ ├── jquery.flot.pie.js │ │ ├── jquery.flot.resize.js │ │ └── jquery.flot.tooltip.min.js │ │ ├── morris │ │ ├── morris-data.js │ │ ├── morris.js │ │ ├── morris.min.js │ │ └── raphael.min.js │ │ └── typeahead │ │ ├── bloodhound.min.js │ │ └── bootstrap3-typeahead.min.js ├── strategy.js ├── utils │ └── paginate.js └── views │ ├── .DS_Store │ ├── collection.jade │ ├── doc.jade │ ├── fields │ ├── array.jade │ ├── checkbox.jade │ ├── date.jade │ ├── fields.jade │ ├── object.jade │ ├── ref.jade │ ├── text.jade │ └── textarea.jade │ ├── index.jade │ ├── layout.jade │ ├── login.jade │ ├── messages.jade │ └── nav.jade ├── package.json └── tests ├── advanced-e2e.js ├── controllers └── main.js ├── index.js ├── models ├── Collection.js └── Document.js ├── simple-e2e.js ├── tests.md └── utils.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "components" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | coverage 4 | components -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - npm install -g gulp 3 | script: "npm run-script test-ci" 4 | after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls" 5 | language: node_js 6 | node_js: 7 | - "4.2.1" 8 | services: mongodb -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | #Contributing 2 | TODO - Clean Up 3 | ``` 4 | # fork this repo 5 | git clone 6 | cd 7 | git checkout -b 8 | git branch -d master 9 | 10 | # You can serve the "simple" app in development by running 11 | # gulp simple 12 | # in all likelihood, you should add your feature and associated options 13 | # to the advanced app 14 | # serve the "advanced" app in development 15 | gulp advanced 16 | # add your awesome feature 17 | # write some tests 18 | # run those tests 19 | gulp test 20 | # debug those tests 21 | mocha --debug-brk tests 22 | # in another tab 23 | node-inspector 24 | 25 | # tests pass 26 | # submit a pull request! 27 | ``` 28 | Read more about tests in [tests/tests.md](./tests/tests.md). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015 Huston Hedinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Sriracha 2 | A super spicy admin backend for Express and Mongoose. 3 | 4 | ![Image of Sriracha](_img/sriracha.jpg) 5 | 6 | [![Build Status](https://travis-ci.org/hdngr/sriracha.svg?branch=master)](https://travis-ci.org/hdngr/sriracha) 7 | [![Coverage Status](https://coveralls.io/repos/hdngr/sriracha/badge.svg?branch=master&service=github)](https://coveralls.io/github/hdngr/sriracha?branch=master) 8 | [![Dependency Status](https://david-dm.org/hdngr/sriracha.svg)](https://david-dm.org/hdngr/sriracha) 9 | [![devDependency Status](https://david-dm.org/hdngr/sriracha/dev-status.svg)](https://david-dm.org/hdngr/sriracha#info=devDependencies) 10 | [![npm version](https://badge.fury.io/js/sriracha.svg)](https://badge.fury.io/js/sriracha) 11 | 12 | Sriracha is an Express app that can be mounted as middleware to any url in your application. The admin site's routes and editing interface are generated dynamically based on your Mongoose Models. Options are available to control the look and feel of the admin site. 13 | 14 | ## Quick Start 15 | 1. Install Sriracha: 16 | 17 | ``` 18 | npm install --save sriracha 19 | ``` 20 | 21 | 2. Include Sriracha in your express app and mount it to a url. 22 | 23 | ``` 24 | var express = require('express'); 25 | var admin = require('sriracha'); 26 | 27 | app = express(); 28 | ... 29 | app.use('/admin', admin()); 30 | ``` 31 | 32 | 3. Login with username `admin` and password `admin`. 33 | 34 | Sriracha is running at `yourapp/admin`! 35 | 36 | ![Image of Sriracha Landing Page](_img/landing.png) 37 | 38 | ## Setting Options Globally 39 | Options can be set globally through the options object passed to the middleware. 40 | 41 | ``` 42 | var options = {...}; 43 | app.use('/admin', admin(options)); 44 | ``` 45 | 46 | **username**
47 | *default*: `'admin'` User name used to access admin backend. 48 | 49 | **password**
50 | *default*: `'admin'` Password used to access the admin backend. 51 | 52 | **hideFields**:
53 | *default*: `['_id', '_v']` Fields that are hidden in all documents and collections. 54 | 55 | **\.searchField**:
56 | *default*: `undefined` Sriracha implements a simple (for now) autocomplete query against the specified field. 57 | 58 | For instance, to search against the *email* field in the *User* model, you would supply the following option: 59 | 60 | ``` 61 | var options = { 62 | ..., 63 | User: { 64 | searchField: 'email' 65 | } 66 | ... 67 | } 68 | ``` 69 | 70 | **\.admin**
71 | *default*: `undefined` A setting of false will hide this field from the admin. 72 | 73 | ## Field Types 74 | Field types are set automatically by Sriracha based on the Mongo schema type. However, they can also be customized. Using the 'adminFieldType' option. See the [setting options on a schema](#setting-options-on-a-schema) for examples of how to set custom field types. 75 | 76 | Sriracha currently supports the following field types: 77 | 78 | **text**
79 | *default*: String and ObjectId schema types. 80 | A simple string input field. 81 | 82 | ![text field](_img/fields/text.png) 83 | 84 | **textarea**
85 | *default*: none 86 | The text area field allows easy inline editing of larger portions of text. The textarea field uses [TinyMCE](https://www.tinymce.com/) and stores it's results as HTML. 87 | 88 | ![textarea field](_img/fields/textarea.png) 89 | 90 | **date**
91 | *default*: Date schema type. 92 | A date picker field using the [datepicker jquery plugin](https://eonasdan.github.io/bootstrap-datetimepicker). 93 | 94 | ![date field](_img/fields/date.png) 95 | 96 | **array**
97 | *default*: Array schema type. 98 | An input that accepts a comma separated list of values. 99 | 100 | ![date field](_img/fields/array.png) 101 | 102 | **checkbox**
103 | *default*: Boolean schema type. 104 | A checkbox that setts a boolean field to `true` or `false.` 105 | 106 | **ref**
107 | *default*: Reference to other documents. 108 | An input of tags representing references to other documents. 109 | 110 | ![date field](_img/fields/ref.png) 111 | 112 | ## Setting Options on a Schema 113 | All `` level options can be set on an individual schema as well. They will take precedence over the same options if they are also defined globally. 114 | 115 | To set schema level options, provide the option, prefixed with `admin`. 116 | 117 | For example, the following schema would set the `lastName` to the search field for users, and would hide the `email` and `onboarding.signupDate` fields. 118 | 119 | ``` 120 | ... 121 | var Schema = mongoose.Schema; 122 | 123 | var UserSchema = new Schema({ 124 | lastName: { 125 | type: String, 126 | default: '', 127 | adminSearchField: true 128 | }, 129 | ..., 130 | email: { 131 | type: String, 132 | admin: false 133 | } 134 | onboarding: { 135 | signupDate: { 136 | type: Date, 137 | admin: false 138 | }, 139 | hasLoggedIn: { 140 | type: Boolean, 141 | default: false 142 | } 143 | }, 144 | }); 145 | ... 146 | ``` 147 | 148 | 149 | ## Examples 150 | Examples can be found in the `./examples` directory. To run them: 151 | 152 | ``` 153 | git clone 154 | cd 155 | npm install 156 | # run the app with simple setup 157 | gulp simple 158 | # run the app with advanced setup 159 | gulp advanced 160 | ``` 161 | 162 | ## Contributing 163 | Contributing is anything from filing bugs, to requesting new features, to building features and their tests. Read the [Contributing](./Contributing.md) doc to find out more. 164 | 165 | ## Acknowledgments 166 | Thanks [Iron Summit Media Strategies](http://www.ironsummitmedia.com/) for the awesome [Start Bootstrap Themes](http://startbootstrap.com/). 167 | 168 | Siracha started with [SB Admin](http://startbootstrap.com/template-overviews/sb-admin/) and I used [Jade Converter](http://html2jade.org/) to turn it into Jade. 169 | -------------------------------------------------------------------------------- /_img/fields/array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/fields/array.png -------------------------------------------------------------------------------- /_img/fields/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/fields/date.png -------------------------------------------------------------------------------- /_img/fields/ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/fields/ref.png -------------------------------------------------------------------------------- /_img/fields/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/fields/text.png -------------------------------------------------------------------------------- /_img/fields/textarea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/fields/textarea.png -------------------------------------------------------------------------------- /_img/landing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/landing.png -------------------------------------------------------------------------------- /_img/sriracha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/_img/sriracha.jpg -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sriracha-admin", 3 | "description": "An admin middleware for mongoose and express.", 4 | "main": "index.js", 5 | "authors": [ 6 | "hdngr " 7 | ], 8 | "license": "ISC", 9 | "homepage": "https://github.com/hdngr/siracha", 10 | "moduleType": [ 11 | "node" 12 | ], 13 | "ignore": [ 14 | "**/.*", 15 | "node_modules", 16 | "bower_components", 17 | "components", 18 | "test", 19 | "tests" 20 | ], 21 | "dependencies": { 22 | "tinymce": "~4.3.0", 23 | "eonasdan-bootstrap-datetimepicker": "~4.17.37" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/advanced/mock.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var User = require('./models/User'); 4 | var Post = require('./models/Post'); 5 | var async = require('async'); 6 | var _ = require('lodash'); 7 | 8 | module.exports = { 9 | init: function(done) { 10 | function createDocs() { 11 | var firstNames = ["John", "Kellen", "Jim", "Susan", "Chantelle", "Siracha"]; 12 | var lastNames = ["Sartre", "Johnson", "Richardson", "Gerrard"]; 13 | for (var i = 0; i < 20; i++) { 14 | var firstName = _.sample(firstNames); 15 | var author = new User({ 16 | firstName: firstName, 17 | lastName: _.sample(lastNames), 18 | email: firstName + "@gmail.com" 19 | }).save(function(err, doc) { 20 | if (!doc) return doc; 21 | var post = new Post({ 22 | title: doc.firstName + " wrote a Post on " + new Date(), 23 | author: doc._id 24 | }).save(); 25 | }); 26 | } 27 | if (done) done(); 28 | }; 29 | createDocs(); 30 | }, 31 | destroy: function(done) { 32 | // mongoose creates a database when a connection is made 33 | // it is unclear how to 'abort' if database already exists 34 | // this is a dirty check to make sure developer has not already 35 | // made other collections in the database before we destroy it 36 | var db = User.db; 37 | if (Object.keys(db.collections).length > 2) { 38 | console.warn("Other collections exist in " + db.name + "!"); 39 | return done 40 | }; 41 | db.db.dropDatabase(function(err, result) { 42 | if (err) console.dir(err); 43 | return done(); 44 | }); 45 | } 46 | } -------------------------------------------------------------------------------- /examples/advanced/models/Post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | 5 | var Schema = mongoose.Schema; 6 | 7 | var PostSchema = new Schema({ 8 | title: { 9 | type: String, 10 | default: '' 11 | // adminSearchField: true 12 | }, 13 | createdOn: { 14 | type: Date, 15 | default: new Date(), 16 | admin: false, 17 | adminFieldType: 'date' 18 | }, 19 | author: { 20 | type: Schema.Types.ObjectId, 21 | ref: 'User' 22 | }, 23 | published: { 24 | type: Boolean, 25 | default: false 26 | }, 27 | campaignHash: { 28 | type: String 29 | }, 30 | body: { 31 | type: String, 32 | adminFieldType: 'textarea' 33 | }, 34 | tags: { 35 | adminFieldType: 'array', 36 | type: Array 37 | } 38 | }); 39 | 40 | 41 | module.exports = mongoose.model('Post', PostSchema); 42 | -------------------------------------------------------------------------------- /examples/advanced/models/User.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | 5 | var Schema = mongoose.Schema; 6 | 7 | var UserSchema = new Schema({ 8 | firstName: { 9 | type: String, 10 | default: '' 11 | }, 12 | lastName: { 13 | type: String, 14 | default: '' 15 | }, 16 | email: { 17 | type: String, 18 | default: '', 19 | unique: true 20 | }, 21 | onboarding: { 22 | signupDate: { 23 | type: String, 24 | adminFieldType: 'date' 25 | }, 26 | hasLoggedIn: { 27 | type: Boolean, 28 | default: false 29 | } 30 | }, 31 | roles: [] 32 | }); 33 | 34 | UserSchema.path('email').validate(function(email) { 35 | return email.length; 36 | }, 'Email cannot be blank'); 37 | 38 | var User = mongoose.model('User', UserSchema); 39 | module.exports = User; 40 | -------------------------------------------------------------------------------- /examples/advanced/passport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var passport = require('passport'), 4 | LocalStrategy = require('passport-local').Strategy, 5 | mongoose = require('mongoose'), 6 | User = require('./models/User'); 7 | 8 | passport.use(new LocalStrategy({ 9 | usernameField: 'email', 10 | passwordField: 'password' 11 | }, 12 | function(email, password, done) { 13 | User.findOne({ 14 | email: email 15 | }, function(err, user) { 16 | if (err) { 17 | return done(err); 18 | } 19 | if (!user) { 20 | return done(null, false, { 21 | message: 'Incorrect username.' 22 | }); 23 | } 24 | if (!user.authenticate(password)) { 25 | return done(null, false, { 26 | message: 'Incorrect password.' 27 | }); 28 | } 29 | return done(null, user); 30 | }); 31 | } 32 | )); 33 | 34 | passport.serializeUser(function(user, done) { 35 | done(null, user.id) 36 | }); 37 | 38 | passport.deserializeUser(function(id, done) { 39 | User.load({ 40 | criteria: { 41 | _id: id 42 | } 43 | }, function(err, user) { 44 | done(err, user) 45 | }); 46 | }); 47 | 48 | 49 | module.exports = passport; -------------------------------------------------------------------------------- /examples/advanced/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'), 4 | mongoose = require('mongoose'), 5 | async = require('async'), 6 | mock = require('./mock'), 7 | User = require('./models/User'), 8 | Post = require('./models/Post'), 9 | admin = require('../../'); 10 | 11 | var dbURL = 'mongodb://localhost/siracha-advanced-example'; 12 | 13 | try { 14 | mongoose.connect(dbURL); 15 | } catch(e) { 16 | console.log(e); 17 | } 18 | 19 | var app = express(); 20 | 21 | app.use('/crazy-mount-path', admin({ 22 | User: { 23 | searchField: 'email', 24 | }, 25 | hideFields: ['__v'], 26 | Post: { 27 | searchField: 'title', 28 | campaignHash: { 29 | admin: false 30 | } 31 | } 32 | })); 33 | 34 | app.get('/', function(req, res) { 35 | res.send('This is the index route. You are probably looking for the Advanced App route!'); 36 | }) 37 | 38 | var server = app.listen(3000, function() { 39 | mock.init(); 40 | console.log('Advanced example app listening at port %s', 3000); 41 | }) 42 | 43 | server.on('close', function(done) { 44 | console.log('Closing advanced example app port %s', 3000); 45 | }); 46 | 47 | process.on('SIGINT', function() { 48 | mock.destroy(function() { 49 | server.close(); 50 | process.kill(0); 51 | }); 52 | }); 53 | 54 | module.exports = server; 55 | -------------------------------------------------------------------------------- /examples/simple/mock.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var async = require('async'); 4 | var User = require('./models/User'); 5 | var Post = require('./models/Post'); 6 | 7 | module.exports = { 8 | createUsers: function() { 9 | var raw = [{ 10 | firstName: "John", 11 | lastName: "Snow", 12 | email: "John@example.com" 13 | }, { 14 | firstName: "Jack", 15 | lastName: "In The Box", 16 | email: "Jack@another.com" 17 | }]; 18 | var users = []; 19 | for (var i = 0; i < raw.length; i++) { 20 | var user = new User(raw[i]); 21 | user.save(); 22 | users.push(user); 23 | } 24 | new Post({ 25 | title: "A Blog Post!", 26 | author: { 27 | firstName: 'John' 28 | } 29 | }).save() 30 | return users; 31 | }, 32 | init: function() { 33 | this.createUsers(); 34 | }, 35 | destroy: function(done) { 36 | // mongoose creates a database when a connection is made 37 | // it is unclear how to 'abort' if database already exists 38 | // this is a dirty check to make sure developer has not already 39 | // made other collections in the database before we destroy it 40 | var db = User.db; 41 | if (Object.keys(db.collections).length > 2) { 42 | console.warn("Other collections exist in " + db.name + "!"); 43 | return done 44 | }; 45 | db.db.dropDatabase(function(err, result) { 46 | if (err) console.dir(err); 47 | console.log("Removing database.") 48 | return done(); 49 | }); 50 | } 51 | } -------------------------------------------------------------------------------- /examples/simple/models/Post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | 5 | var Schema = mongoose.Schema; 6 | 7 | var PostSchema = new Schema({ 8 | title: { 9 | type: String, 10 | default: '' 11 | }, 12 | date: { 13 | type: Date, 14 | default: new Date(), 15 | admin: false 16 | }, 17 | author: { 18 | type: Schema.Types.ObjectId, 19 | ref: 'User' 20 | } 21 | }); 22 | 23 | 24 | module.exports = mongoose.model('Post', PostSchema); 25 | -------------------------------------------------------------------------------- /examples/simple/models/User.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | 5 | var Schema = mongoose.Schema; 6 | 7 | var UserSchema = new Schema({ 8 | firstName: { 9 | type: String, 10 | default: '' 11 | }, 12 | lastName: { 13 | type: String, 14 | default: '' 15 | }, 16 | email: { 17 | type: String, 18 | default: '', 19 | unique: true 20 | } 21 | }); 22 | 23 | var User = mongoose.model('User', UserSchema); 24 | 25 | module.exports = User; 26 | -------------------------------------------------------------------------------- /examples/simple/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'), 4 | mongoose = require('mongoose'), 5 | async = require('async'), 6 | mock = require('./mock'), 7 | User = require('./models/User'), 8 | Post = require('./models/Post'), 9 | admin = require('../../'); 10 | 11 | 12 | var dbURL = 'mongodb://localhost/siracha-simple-example'; 13 | 14 | mongoose.connect(dbURL); 15 | 16 | var app = express(); 17 | 18 | app.use('/admin', admin()); 19 | 20 | app.get('/', function(req, res) { 21 | res.send('This is the index route. You are probably looking for the admin route!'); 22 | }) 23 | 24 | var server = app.listen(3000, function() { 25 | mock.init(); 26 | console.log('Simple example app listening at port %s', 3000); 27 | }) 28 | 29 | server.on('close', function(done) { 30 | console.log('Closing simple example app port %s', 3000); 31 | }); 32 | 33 | process.on('SIGINT', function() { 34 | server.close(); 35 | mock.destroy(function() { 36 | process.kill(0); 37 | }); 38 | }); 39 | 40 | module.exports = server; 41 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | nodemon = require('gulp-nodemon'), 5 | mocha = require('gulp-mocha'), 6 | livereload = require('gulp-livereload'), 7 | args = require('minimist')(process.argv); 8 | 9 | gulp.task('simple', function() { 10 | livereload.listen(); 11 | var nodeArgs = []; 12 | // used to run nodemon in --debug-brk mode 13 | if (args.dev || args.d) { 14 | nodeArgs.push('--debug'); 15 | } 16 | if (args['debug-brk']) { 17 | nodeArgs.push('--debug-brk'); 18 | } 19 | return nodemon({ 20 | script: 'examples/simple/server.js', 21 | ext: 'js', 22 | nodeArgs: nodeArgs, 23 | verbose: true 24 | }).on('restart', function() { 25 | livereload.reload(); 26 | }); 27 | }); 28 | 29 | gulp.task('advanced', function() { 30 | livereload.listen(); 31 | var nodeArgs = []; 32 | // used to run nodemon in --debug-brk mode 33 | if (args.dev || args.d) { 34 | nodeArgs.push('--debug'); 35 | } 36 | if (args['debug-brk']) { 37 | nodeArgs.push('--debug-brk'); 38 | } 39 | return nodemon({ 40 | script: 'examples/advanced/server.js', 41 | ext: 'js', 42 | nodeArgs: nodeArgs, 43 | verbose: true 44 | }).on('restart', function() { 45 | livereload.reload(); 46 | }) 47 | }); 48 | 49 | gulp.task('test', function() { 50 | return gulp.src('tests', { 51 | read: false 52 | }) 53 | // gulp-mocha needs filepaths so you can't have any plugins before it 54 | .pipe(mocha({ 55 | reporter: 'spec', 56 | bail: false 57 | })) 58 | .once('end', function() { 59 | process.exit(); 60 | }); 61 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'), 4 | session = require('express-session'), 5 | mongoose = require('mongoose'), 6 | bodyParser = require('body-parser'), 7 | methodOverride = require('method-override'), 8 | routes = require('./lib/controllers/main'), 9 | _ = require('lodash'), 10 | Collection = require('./lib/models/Collection'), 11 | Strategy = require('./lib/strategy'), 12 | Options = require('./lib/options'); 13 | 14 | var admin = express(); 15 | 16 | // this session store is only used if parent app 17 | // DOES NOT already have a session store. 18 | admin.use(session({ 19 | secret: 'Siracha!', 20 | saveUninitialized: true, 21 | resave: false 22 | })); 23 | 24 | admin.set('view engine', 'jade'); 25 | admin.set('views', __dirname + '/lib/views'); 26 | admin.use(bodyParser.urlencoded({ 27 | extended: false 28 | })); 29 | 30 | // allow delete method 31 | admin.use(methodOverride(function(req, res) { 32 | if (req.body && typeof req.body === 'object' && '_method' in req.body) { 33 | // look in urlencoded POST bodies and delete it 34 | var method = req.body._method 35 | delete req.body._method 36 | return method 37 | } 38 | })); 39 | 40 | admin.use('/static', express.static(__dirname + '/lib/static')); 41 | admin.use('/components', express.static(__dirname + '/components')); 42 | 43 | 44 | module.exports = function(userDefined) { 45 | var userDefined = userDefined || {}; 46 | 47 | var options = Options(userDefined); 48 | 49 | var Models; 50 | var collectionNames; 51 | var collections; 52 | var docs; 53 | var strategy; 54 | 55 | // models default to all models in a Mongoose connection 56 | if (options.models.length > 0) { 57 | Models = options.models.map(function(name) { 58 | return mongoose.models[name]; 59 | }); 60 | } else { 61 | Models = mongoose.models; 62 | }; 63 | 64 | // create map of plural collection names for easy lookup 65 | // e.g. {'users': 'User', ...} 66 | collectionNames = _.mapValues(_.mapKeys(Models, function(model) { 67 | return model.collection.name; 68 | }), function(model) { 69 | return model.modelName; 70 | }); 71 | 72 | // extend models with methods for use with Siracha 73 | collections = _.mapValues(Models, function(model) { 74 | return Collection(model, options); 75 | }); 76 | 77 | admin.locals._mongooseModels = Models; 78 | admin.locals.collectionNames = collectionNames; 79 | admin.locals.collections = collections; 80 | 81 | // get mount path for use in routing static 82 | admin.use(function(req, res, next) { 83 | debugger; 84 | var mountpath = admin.mountpath; 85 | if(mountpath === '/') { 86 | mountpath = ''; 87 | } 88 | admin.locals.appPath = mountpath; 89 | next(); 90 | }); 91 | 92 | admin.use(function(req, res, next) { 93 | req.session.message = req.session.message || { 94 | error: [], 95 | success: [], 96 | info: [] 97 | }; 98 | admin.locals.message = req.session.message; 99 | next(); 100 | }); 101 | 102 | strategy = new Strategy(options); 103 | 104 | admin.use(strategy.middleware.bind(strategy)); 105 | 106 | admin.get('/', routes.main); 107 | 108 | admin.post('/login', strategy.login.bind(strategy)); 109 | admin.post('/logout', strategy.logout); 110 | 111 | admin.get('/:collection', routes.collection); 112 | admin.post('/:collection/suggest', routes.suggest); 113 | admin.all('/:collection/new', routes.newDoc); 114 | admin.all('/:collection/:doc', routes.doc); 115 | 116 | return admin; 117 | }; 118 | -------------------------------------------------------------------------------- /lib/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'), 4 | Doc = require('../models/Document'), 5 | paginate = require('../utils/paginate'); 6 | 7 | function handleMessage(session, type, message) { 8 | session.message[type].push(message); 9 | }; 10 | 11 | module.exports = { 12 | main: function(req, res, next) { 13 | res.render('index', {pageId: 'index'}); 14 | }, 15 | collection: function(req, res) { 16 | var collection = req.params.collection; 17 | var collections = req.app.locals.collections; 18 | var collectionNames = req.app.locals.collectionNames; 19 | var collectionName = collectionNames[collection]; 20 | var Collection = collections[collectionName]; 21 | 22 | var perPage = 10 23 | var page = req.query.page > 0 ? req.query.page : 0 24 | 25 | var sortField = req.query.sortField; 26 | var criteria = req.query.criteria; 27 | if (!criteria) { 28 | res.locals.criteria = criteria = 1 29 | } else { 30 | res.locals.criteria = criteria = -criteria 31 | } 32 | 33 | var sort; 34 | if (sortField) { 35 | sort = {}; 36 | sort[sortField] = criteria; 37 | } 38 | 39 | Collection.find({}) 40 | .limit(perPage) 41 | .skip(perPage * page) 42 | .sort(sort) // default sorting parameter from options... 43 | .exec(function(err, docs) { 44 | Collection.count().exec(function(err, count) { 45 | if(err) handleMessage(req.session, 'error', err.errors) 46 | // handleMessage('error', err.errors) 47 | res.render('collection', { 48 | pageId: 'collection', 49 | docs: docs, 50 | Collection: Collection, 51 | page: page, 52 | pages: count / perPage, 53 | paginate: paginate(req, res) 54 | }); 55 | }); 56 | }); 57 | }, 58 | doc: function(req, res) { 59 | var collections = req.app.locals.collections; 60 | var collection = req.app.locals.collectionNames[req.params.collection]; 61 | var id = req.params.doc; 62 | var Collection = collections[collection]; 63 | var appPath = req.app.locals.appPath; 64 | 65 | Collection.findById(id, function(err, doc) { 66 | doc = Doc(doc, null, collections); 67 | res.locals.pageId = 'doc'; 68 | switch (req.method) { 69 | case "GET": 70 | if (!doc) { 71 | handleMessage(req.session, 'error', "It doesn't look like there is a document with that id."); 72 | }; 73 | res.render('doc', { 74 | doc: doc, 75 | Collection: Collection, 76 | errors: {}, 77 | id: id 78 | }); 79 | break; 80 | case "POST": 81 | Object.keys(req.body).forEach(function(path) { 82 | var type = Collection.getPathType(path); 83 | var val = req.body[path]; 84 | if(type === 'array') { 85 | val = val.split(','); 86 | } 87 | doc.set(path, val); 88 | }); 89 | doc.save(function(err) { 90 | err = err || {}; 91 | res.render('doc', { 92 | doc: doc, 93 | Collection: Collection, 94 | errors: err.errors || {} 95 | }); 96 | }); 97 | break; 98 | case "DELETE": 99 | doc.remove(function(err) { 100 | if (err) { 101 | err = err || {}; 102 | handleMessage(req.session, 'error', "Oops! Looks like there was a problem deleting the document!"); 103 | return res.render('doc', { 104 | doc: doc, 105 | Collection: Collection, 106 | errors: err.errors || {} 107 | }); 108 | }; 109 | var message = "Doc " + doc.id + " deleted successfully!"; 110 | handleMessage(req.session, 'success', message); 111 | res.redirect(appPath + '/' + doc.collection.name); 112 | }) 113 | break; 114 | default: 115 | res.render('doc', { 116 | doc: doc, 117 | Collection: Collection, 118 | errors: {} 119 | }); 120 | } 121 | }); 122 | }, 123 | suggest: function(req, res) { 124 | var term = req.body.term; 125 | 126 | var collection = req.params.collection; 127 | var collections = req.app.locals.collections; 128 | var collectionNames = req.app.locals.collectionNames; 129 | var collectionName = collectionNames[collection]; 130 | var Collection = collections[collectionName]; 131 | 132 | var query = {}; 133 | query[Collection.searchField] = new RegExp(term, 'i'); 134 | Collection.find(query) 135 | .select(Collection.searchField + ' id') 136 | .limit(10) 137 | .exec(function(err, docs) { 138 | var results = _.map(docs, function(doc) { 139 | return { 140 | value: doc[Collection.searchField], 141 | id: doc.id 142 | } 143 | }); 144 | res.json(results); 145 | }); 146 | }, 147 | newDoc: function(req, res) { 148 | var collections = req.app.locals.collections; 149 | var collection = req.app.locals.collectionNames[req.params.collection]; 150 | var id = req.params.doc; 151 | var Collection = collections[collection]; 152 | var appPath = req.app.locals.appPath; 153 | var doc = new Collection(); 154 | switch (req.method) { 155 | case "POST": 156 | Object.keys(req.body).forEach(function(path) { 157 | var type = Collection.getPathType(path); 158 | var val = req.body[path]; 159 | if(type === 'array') { 160 | val = val.split(','); 161 | } 162 | doc.set(path, val); 163 | }); 164 | doc.save(function(err) { 165 | if (err) { 166 | handleMessage(req.session, 'error', 'There was a problem saving the document! Try again.'); 167 | return res.render('doc', { 168 | doc: doc, 169 | Collection: Collection, 170 | errors: err.errors || {} 171 | }); 172 | } 173 | handleMessage(req.session, 'success', Collection.modelName + " created successfully!"); 174 | res.redirect(doc.id); 175 | }); 176 | break; 177 | default: 178 | // e.g. GET and DELETE 179 | res.render('doc', { 180 | pageId: 'new-doc', 181 | doc: doc, 182 | Collection: Collection, 183 | errors: {}, 184 | id: id 185 | }); 186 | break; 187 | 188 | }; 189 | } 190 | } -------------------------------------------------------------------------------- /lib/models/Collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | module.exports = function(MongooseModel, options) { 6 | 7 | var Collection = MongooseModel; 8 | 9 | Collection.sirachaOptions = options; 10 | 11 | options[Collection.modelName] = options[Collection.modelName] || {} 12 | 13 | // move options to their own options property to make 14 | // managing multiple options easier 15 | var statics = { 16 | pluralName: Collection.modelName + 's', 17 | adminPaths: [], 18 | searchField: options[Collection.modelName].searchField || false 19 | }; 20 | 21 | Collection.schema.paths; 22 | 23 | for (var path in Collection.schema.paths) { 24 | options[path] = options[path] || {}; 25 | if((Collection.schema.paths[path].options.admin !== false || options[path].admin !== false) && options.hideFields.indexOf(path) === -1) { 26 | statics.adminPaths.push(path); 27 | } 28 | if(Collection.schema.paths[path].options.adminSearchField) { 29 | statics.searchField = path; 30 | } 31 | } 32 | 33 | var methods = { 34 | getPathType: function(path) { 35 | // var branch = path.split('.'); 36 | // if(branch.length > 1) { 37 | // return 'object' 38 | // }; 39 | var leaf = Collection.schema.path(path); 40 | 41 | if(!leaf.options.adminFieldType) { 42 | switch (leaf.instance) { 43 | case 'String': 44 | return 'text'; 45 | break; 46 | case 'Boolean': 47 | return 'checkbox'; 48 | break; 49 | case 'ObjectID': 50 | if(leaf.options.ref) { 51 | return 'ref'; 52 | } else { 53 | return 'text'; 54 | } 55 | break; 56 | case 'Array': 57 | return 'array'; 58 | break; 59 | case 'Date': 60 | return 'date'; 61 | break; 62 | default: 63 | return 'text'; 64 | break; 65 | } 66 | }; 67 | return leaf.options.adminFieldType 68 | } 69 | }; 70 | 71 | _.extend(Collection, statics, methods); 72 | 73 | return Collection 74 | 75 | }; -------------------------------------------------------------------------------- /lib/models/Document.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | module.exports = function(doc, options, Collections) { 6 | if(doc === null) return doc; 7 | options = options || {}; 8 | var methods = {}; 9 | var Document = doc; 10 | var paths = Document.schema.paths; 11 | 12 | for(var path in paths) { 13 | // add ability to control population in options 14 | var ref = paths[path].options.ref 15 | if(ref) { 16 | Document[path]._collectionName = Collections[ref].collection.name; 17 | } 18 | }; 19 | 20 | _.extend(doc, methods); 21 | 22 | return doc 23 | }; -------------------------------------------------------------------------------- /lib/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | module.exports = function(options) { 6 | 7 | var defaults = { 8 | username: 'admin', 9 | password: 'admin', 10 | passport: undefined, // not implemented 11 | adminUsersModel: 'User', // not implemented 12 | hideFields: ['_id', '__v'], 13 | models: [] // not implemented 14 | //collection: { 15 | // searchField: undefined 16 | //} 17 | }; 18 | 19 | return _.merge(defaults, options); 20 | 21 | }; -------------------------------------------------------------------------------- /lib/static/css/plugins/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255,255,255,0.8);border:solid 2px rgba(230,230,230,0.8);font-family:sans-serif;font-size:12px;text-align:center}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0} 3 | -------------------------------------------------------------------------------- /lib/static/css/sb-admin-rtl.css: -------------------------------------------------------------------------------- 1 | 2 | @media (min-width: 768px){ 3 | #wrapper {padding-right: 225px; padding-left: 0;} 4 | .side-nav{right: 0;left: auto;} 5 | } -------------------------------------------------------------------------------- /lib/static/css/sb-admin.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - SB Admin Bootstrap Admin Template (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | /* Global Styles */ 8 | 9 | body { 10 | margin-top: 100px; 11 | background-color: #222; 12 | } 13 | 14 | @media(min-width:768px) { 15 | body { 16 | margin-top: 50px; 17 | } 18 | } 19 | 20 | #wrapper { 21 | padding-left: 0; 22 | } 23 | 24 | #page-wrapper { 25 | width: 100%; 26 | padding: 0; 27 | background-color: #fff; 28 | } 29 | 30 | .huge { 31 | font-size: 50px; 32 | line-height: normal; 33 | } 34 | 35 | @media(min-width:768px) { 36 | #wrapper { 37 | padding-left: 225px; 38 | } 39 | 40 | #page-wrapper { 41 | padding: 10px; 42 | } 43 | } 44 | 45 | /* Top Navigation */ 46 | 47 | .top-nav { 48 | padding: 0 15px; 49 | } 50 | 51 | .top-nav>li { 52 | display: inline-block; 53 | float: left; 54 | } 55 | 56 | .top-nav>li>a { 57 | padding-top: 15px; 58 | padding-bottom: 15px; 59 | line-height: 20px; 60 | color: #999; 61 | } 62 | 63 | .top-nav>li>a:hover, 64 | .top-nav>li>a:focus, 65 | .top-nav>.open>a, 66 | .top-nav>.open>a:hover, 67 | .top-nav>.open>a:focus { 68 | color: #fff; 69 | background-color: #000; 70 | } 71 | 72 | .top-nav>.open>.dropdown-menu { 73 | float: left; 74 | position: absolute; 75 | margin-top: 0; 76 | border: 1px solid rgba(0,0,0,.15); 77 | border-top-left-radius: 0; 78 | border-top-right-radius: 0; 79 | background-color: #fff; 80 | -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175); 81 | box-shadow: 0 6px 12px rgba(0,0,0,.175); 82 | } 83 | 84 | .top-nav>.open>.dropdown-menu>li>a { 85 | white-space: normal; 86 | } 87 | 88 | ul.message-dropdown { 89 | padding: 0; 90 | max-height: 250px; 91 | overflow-x: hidden; 92 | overflow-y: auto; 93 | } 94 | 95 | li.message-preview { 96 | width: 275px; 97 | border-bottom: 1px solid rgba(0,0,0,.15); 98 | } 99 | 100 | li.message-preview>a { 101 | padding-top: 15px; 102 | padding-bottom: 15px; 103 | } 104 | 105 | li.message-footer { 106 | margin: 5px 0; 107 | } 108 | 109 | ul.alert-dropdown { 110 | width: 200px; 111 | } 112 | 113 | /* Side Navigation */ 114 | 115 | @media(min-width:768px) { 116 | .side-nav { 117 | position: fixed; 118 | top: 51px; 119 | left: 225px; 120 | width: 225px; 121 | margin-left: -225px; 122 | border: none; 123 | border-radius: 0; 124 | overflow-y: auto; 125 | background-color: #222; 126 | bottom: 0; 127 | overflow-x: hidden; 128 | padding-bottom: 40px; 129 | } 130 | 131 | .side-nav>li>a { 132 | width: 225px; 133 | } 134 | 135 | .side-nav li a:hover, 136 | .side-nav li a:focus { 137 | outline: none; 138 | background-color: #000 !important; 139 | } 140 | } 141 | 142 | .side-nav>li>ul { 143 | padding: 0; 144 | } 145 | 146 | .side-nav>li>ul>li>a { 147 | display: block; 148 | padding: 10px 15px 10px 38px; 149 | text-decoration: none; 150 | color: #999; 151 | } 152 | 153 | .side-nav>li>ul>li>a:hover { 154 | color: #fff; 155 | } 156 | 157 | /* Flot Chart Containers */ 158 | 159 | .flot-chart { 160 | display: block; 161 | height: 400px; 162 | } 163 | 164 | .flot-chart-content { 165 | width: 100%; 166 | height: 100%; 167 | } 168 | 169 | /* Custom Colored Panels */ 170 | 171 | .huge { 172 | font-size: 40px; 173 | } 174 | 175 | .panel-green { 176 | border-color: #5cb85c; 177 | } 178 | 179 | .panel-green > .panel-heading { 180 | border-color: #5cb85c; 181 | color: #fff; 182 | background-color: #5cb85c; 183 | } 184 | 185 | .panel-green > a { 186 | color: #5cb85c; 187 | } 188 | 189 | .panel-green > a:hover { 190 | color: #3d8b3d; 191 | } 192 | 193 | .panel-red { 194 | border-color: #d9534f; 195 | } 196 | 197 | .panel-red > .panel-heading { 198 | border-color: #d9534f; 199 | color: #fff; 200 | background-color: #d9534f; 201 | } 202 | 203 | .panel-red > a { 204 | color: #d9534f; 205 | } 206 | 207 | .panel-red > a:hover { 208 | color: #b52b27; 209 | } 210 | 211 | .panel-yellow { 212 | border-color: #f0ad4e; 213 | } 214 | 215 | .panel-yellow > .panel-heading { 216 | border-color: #f0ad4e; 217 | color: #fff; 218 | background-color: #f0ad4e; 219 | } 220 | 221 | .panel-yellow > a { 222 | color: #f0ad4e; 223 | } 224 | 225 | .panel-yellow > a:hover { 226 | color: #df8a13; 227 | } 228 | 229 | .ref-host { 230 | -moz-appearance: textfield; 231 | -webkit-appearance: textfield; 232 | padding: 1px; 233 | overflow: hidden; 234 | word-wrap: break-word; 235 | cursor: text; 236 | background-color: #fff; 237 | border: 1px solid #ccc; 238 | border-radius: 0 4px 4px 0; 239 | box-shadow: 1px 1px 1px 0 #d3d3d3 inset; 240 | height: 100%; 241 | } 242 | 243 | .ref-host ul { 244 | list-style: none; 245 | margin: 0px 0px 0px 0px; 246 | } -------------------------------------------------------------------------------- /lib/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/favicon.ico -------------------------------------------------------------------------------- /lib/static/font-awesome/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} -------------------------------------------------------------------------------- /lib/static/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /lib/static/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /lib/static/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /lib/static/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /lib/static/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "spinning.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | .fa-icon-rotate(@degrees, @rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 15 | -webkit-transform: rotate(@degrees); 16 | -ms-transform: rotate(@degrees); 17 | transform: rotate(@degrees); 18 | } 19 | 20 | .fa-icon-flip(@horiz, @vert, @rotation) { 21 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 22 | -webkit-transform: scale(@horiz, @vert); 23 | -ms-transform: scale(@horiz, @vert); 24 | transform: scale(@horiz, @vert); 25 | } 26 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 9 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 10 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 11 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/spinning.less: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /lib/static/font-awesome/less/variables.less: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | @fa-font-path: "../fonts"; 5 | //@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts"; // for referencing Bootstrap CDN font files directly 6 | @fa-css-prefix: fa; 7 | @fa-version: "4.2.0"; 8 | @fa-border-color: #eee; 9 | @fa-inverse: #fff; 10 | @fa-li-width: (30em / 14); 11 | 12 | @fa-var-adjust: "\f042"; 13 | @fa-var-adn: "\f170"; 14 | @fa-var-align-center: "\f037"; 15 | @fa-var-align-justify: "\f039"; 16 | @fa-var-align-left: "\f036"; 17 | @fa-var-align-right: "\f038"; 18 | @fa-var-ambulance: "\f0f9"; 19 | @fa-var-anchor: "\f13d"; 20 | @fa-var-android: "\f17b"; 21 | @fa-var-angellist: "\f209"; 22 | @fa-var-angle-double-down: "\f103"; 23 | @fa-var-angle-double-left: "\f100"; 24 | @fa-var-angle-double-right: "\f101"; 25 | @fa-var-angle-double-up: "\f102"; 26 | @fa-var-angle-down: "\f107"; 27 | @fa-var-angle-left: "\f104"; 28 | @fa-var-angle-right: "\f105"; 29 | @fa-var-angle-up: "\f106"; 30 | @fa-var-apple: "\f179"; 31 | @fa-var-archive: "\f187"; 32 | @fa-var-area-chart: "\f1fe"; 33 | @fa-var-arrow-circle-down: "\f0ab"; 34 | @fa-var-arrow-circle-left: "\f0a8"; 35 | @fa-var-arrow-circle-o-down: "\f01a"; 36 | @fa-var-arrow-circle-o-left: "\f190"; 37 | @fa-var-arrow-circle-o-right: "\f18e"; 38 | @fa-var-arrow-circle-o-up: "\f01b"; 39 | @fa-var-arrow-circle-right: "\f0a9"; 40 | @fa-var-arrow-circle-up: "\f0aa"; 41 | @fa-var-arrow-down: "\f063"; 42 | @fa-var-arrow-left: "\f060"; 43 | @fa-var-arrow-right: "\f061"; 44 | @fa-var-arrow-up: "\f062"; 45 | @fa-var-arrows: "\f047"; 46 | @fa-var-arrows-alt: "\f0b2"; 47 | @fa-var-arrows-h: "\f07e"; 48 | @fa-var-arrows-v: "\f07d"; 49 | @fa-var-asterisk: "\f069"; 50 | @fa-var-at: "\f1fa"; 51 | @fa-var-automobile: "\f1b9"; 52 | @fa-var-backward: "\f04a"; 53 | @fa-var-ban: "\f05e"; 54 | @fa-var-bank: "\f19c"; 55 | @fa-var-bar-chart: "\f080"; 56 | @fa-var-bar-chart-o: "\f080"; 57 | @fa-var-barcode: "\f02a"; 58 | @fa-var-bars: "\f0c9"; 59 | @fa-var-beer: "\f0fc"; 60 | @fa-var-behance: "\f1b4"; 61 | @fa-var-behance-square: "\f1b5"; 62 | @fa-var-bell: "\f0f3"; 63 | @fa-var-bell-o: "\f0a2"; 64 | @fa-var-bell-slash: "\f1f6"; 65 | @fa-var-bell-slash-o: "\f1f7"; 66 | @fa-var-bicycle: "\f206"; 67 | @fa-var-binoculars: "\f1e5"; 68 | @fa-var-birthday-cake: "\f1fd"; 69 | @fa-var-bitbucket: "\f171"; 70 | @fa-var-bitbucket-square: "\f172"; 71 | @fa-var-bitcoin: "\f15a"; 72 | @fa-var-bold: "\f032"; 73 | @fa-var-bolt: "\f0e7"; 74 | @fa-var-bomb: "\f1e2"; 75 | @fa-var-book: "\f02d"; 76 | @fa-var-bookmark: "\f02e"; 77 | @fa-var-bookmark-o: "\f097"; 78 | @fa-var-briefcase: "\f0b1"; 79 | @fa-var-btc: "\f15a"; 80 | @fa-var-bug: "\f188"; 81 | @fa-var-building: "\f1ad"; 82 | @fa-var-building-o: "\f0f7"; 83 | @fa-var-bullhorn: "\f0a1"; 84 | @fa-var-bullseye: "\f140"; 85 | @fa-var-bus: "\f207"; 86 | @fa-var-cab: "\f1ba"; 87 | @fa-var-calculator: "\f1ec"; 88 | @fa-var-calendar: "\f073"; 89 | @fa-var-calendar-o: "\f133"; 90 | @fa-var-camera: "\f030"; 91 | @fa-var-camera-retro: "\f083"; 92 | @fa-var-car: "\f1b9"; 93 | @fa-var-caret-down: "\f0d7"; 94 | @fa-var-caret-left: "\f0d9"; 95 | @fa-var-caret-right: "\f0da"; 96 | @fa-var-caret-square-o-down: "\f150"; 97 | @fa-var-caret-square-o-left: "\f191"; 98 | @fa-var-caret-square-o-right: "\f152"; 99 | @fa-var-caret-square-o-up: "\f151"; 100 | @fa-var-caret-up: "\f0d8"; 101 | @fa-var-cc: "\f20a"; 102 | @fa-var-cc-amex: "\f1f3"; 103 | @fa-var-cc-discover: "\f1f2"; 104 | @fa-var-cc-mastercard: "\f1f1"; 105 | @fa-var-cc-paypal: "\f1f4"; 106 | @fa-var-cc-stripe: "\f1f5"; 107 | @fa-var-cc-visa: "\f1f0"; 108 | @fa-var-certificate: "\f0a3"; 109 | @fa-var-chain: "\f0c1"; 110 | @fa-var-chain-broken: "\f127"; 111 | @fa-var-check: "\f00c"; 112 | @fa-var-check-circle: "\f058"; 113 | @fa-var-check-circle-o: "\f05d"; 114 | @fa-var-check-square: "\f14a"; 115 | @fa-var-check-square-o: "\f046"; 116 | @fa-var-chevron-circle-down: "\f13a"; 117 | @fa-var-chevron-circle-left: "\f137"; 118 | @fa-var-chevron-circle-right: "\f138"; 119 | @fa-var-chevron-circle-up: "\f139"; 120 | @fa-var-chevron-down: "\f078"; 121 | @fa-var-chevron-left: "\f053"; 122 | @fa-var-chevron-right: "\f054"; 123 | @fa-var-chevron-up: "\f077"; 124 | @fa-var-child: "\f1ae"; 125 | @fa-var-circle: "\f111"; 126 | @fa-var-circle-o: "\f10c"; 127 | @fa-var-circle-o-notch: "\f1ce"; 128 | @fa-var-circle-thin: "\f1db"; 129 | @fa-var-clipboard: "\f0ea"; 130 | @fa-var-clock-o: "\f017"; 131 | @fa-var-close: "\f00d"; 132 | @fa-var-cloud: "\f0c2"; 133 | @fa-var-cloud-download: "\f0ed"; 134 | @fa-var-cloud-upload: "\f0ee"; 135 | @fa-var-cny: "\f157"; 136 | @fa-var-code: "\f121"; 137 | @fa-var-code-fork: "\f126"; 138 | @fa-var-codepen: "\f1cb"; 139 | @fa-var-coffee: "\f0f4"; 140 | @fa-var-cog: "\f013"; 141 | @fa-var-cogs: "\f085"; 142 | @fa-var-columns: "\f0db"; 143 | @fa-var-comment: "\f075"; 144 | @fa-var-comment-o: "\f0e5"; 145 | @fa-var-comments: "\f086"; 146 | @fa-var-comments-o: "\f0e6"; 147 | @fa-var-compass: "\f14e"; 148 | @fa-var-compress: "\f066"; 149 | @fa-var-copy: "\f0c5"; 150 | @fa-var-copyright: "\f1f9"; 151 | @fa-var-credit-card: "\f09d"; 152 | @fa-var-crop: "\f125"; 153 | @fa-var-crosshairs: "\f05b"; 154 | @fa-var-css3: "\f13c"; 155 | @fa-var-cube: "\f1b2"; 156 | @fa-var-cubes: "\f1b3"; 157 | @fa-var-cut: "\f0c4"; 158 | @fa-var-cutlery: "\f0f5"; 159 | @fa-var-dashboard: "\f0e4"; 160 | @fa-var-database: "\f1c0"; 161 | @fa-var-dedent: "\f03b"; 162 | @fa-var-delicious: "\f1a5"; 163 | @fa-var-desktop: "\f108"; 164 | @fa-var-deviantart: "\f1bd"; 165 | @fa-var-digg: "\f1a6"; 166 | @fa-var-dollar: "\f155"; 167 | @fa-var-dot-circle-o: "\f192"; 168 | @fa-var-download: "\f019"; 169 | @fa-var-dribbble: "\f17d"; 170 | @fa-var-dropbox: "\f16b"; 171 | @fa-var-drupal: "\f1a9"; 172 | @fa-var-edit: "\f044"; 173 | @fa-var-eject: "\f052"; 174 | @fa-var-ellipsis-h: "\f141"; 175 | @fa-var-ellipsis-v: "\f142"; 176 | @fa-var-empire: "\f1d1"; 177 | @fa-var-envelope: "\f0e0"; 178 | @fa-var-envelope-o: "\f003"; 179 | @fa-var-envelope-square: "\f199"; 180 | @fa-var-eraser: "\f12d"; 181 | @fa-var-eur: "\f153"; 182 | @fa-var-euro: "\f153"; 183 | @fa-var-exchange: "\f0ec"; 184 | @fa-var-exclamation: "\f12a"; 185 | @fa-var-exclamation-circle: "\f06a"; 186 | @fa-var-exclamation-triangle: "\f071"; 187 | @fa-var-expand: "\f065"; 188 | @fa-var-external-link: "\f08e"; 189 | @fa-var-external-link-square: "\f14c"; 190 | @fa-var-eye: "\f06e"; 191 | @fa-var-eye-slash: "\f070"; 192 | @fa-var-eyedropper: "\f1fb"; 193 | @fa-var-facebook: "\f09a"; 194 | @fa-var-facebook-square: "\f082"; 195 | @fa-var-fast-backward: "\f049"; 196 | @fa-var-fast-forward: "\f050"; 197 | @fa-var-fax: "\f1ac"; 198 | @fa-var-female: "\f182"; 199 | @fa-var-fighter-jet: "\f0fb"; 200 | @fa-var-file: "\f15b"; 201 | @fa-var-file-archive-o: "\f1c6"; 202 | @fa-var-file-audio-o: "\f1c7"; 203 | @fa-var-file-code-o: "\f1c9"; 204 | @fa-var-file-excel-o: "\f1c3"; 205 | @fa-var-file-image-o: "\f1c5"; 206 | @fa-var-file-movie-o: "\f1c8"; 207 | @fa-var-file-o: "\f016"; 208 | @fa-var-file-pdf-o: "\f1c1"; 209 | @fa-var-file-photo-o: "\f1c5"; 210 | @fa-var-file-picture-o: "\f1c5"; 211 | @fa-var-file-powerpoint-o: "\f1c4"; 212 | @fa-var-file-sound-o: "\f1c7"; 213 | @fa-var-file-text: "\f15c"; 214 | @fa-var-file-text-o: "\f0f6"; 215 | @fa-var-file-video-o: "\f1c8"; 216 | @fa-var-file-word-o: "\f1c2"; 217 | @fa-var-file-zip-o: "\f1c6"; 218 | @fa-var-files-o: "\f0c5"; 219 | @fa-var-film: "\f008"; 220 | @fa-var-filter: "\f0b0"; 221 | @fa-var-fire: "\f06d"; 222 | @fa-var-fire-extinguisher: "\f134"; 223 | @fa-var-flag: "\f024"; 224 | @fa-var-flag-checkered: "\f11e"; 225 | @fa-var-flag-o: "\f11d"; 226 | @fa-var-flash: "\f0e7"; 227 | @fa-var-flask: "\f0c3"; 228 | @fa-var-flickr: "\f16e"; 229 | @fa-var-floppy-o: "\f0c7"; 230 | @fa-var-folder: "\f07b"; 231 | @fa-var-folder-o: "\f114"; 232 | @fa-var-folder-open: "\f07c"; 233 | @fa-var-folder-open-o: "\f115"; 234 | @fa-var-font: "\f031"; 235 | @fa-var-forward: "\f04e"; 236 | @fa-var-foursquare: "\f180"; 237 | @fa-var-frown-o: "\f119"; 238 | @fa-var-futbol-o: "\f1e3"; 239 | @fa-var-gamepad: "\f11b"; 240 | @fa-var-gavel: "\f0e3"; 241 | @fa-var-gbp: "\f154"; 242 | @fa-var-ge: "\f1d1"; 243 | @fa-var-gear: "\f013"; 244 | @fa-var-gears: "\f085"; 245 | @fa-var-gift: "\f06b"; 246 | @fa-var-git: "\f1d3"; 247 | @fa-var-git-square: "\f1d2"; 248 | @fa-var-github: "\f09b"; 249 | @fa-var-github-alt: "\f113"; 250 | @fa-var-github-square: "\f092"; 251 | @fa-var-gittip: "\f184"; 252 | @fa-var-glass: "\f000"; 253 | @fa-var-globe: "\f0ac"; 254 | @fa-var-google: "\f1a0"; 255 | @fa-var-google-plus: "\f0d5"; 256 | @fa-var-google-plus-square: "\f0d4"; 257 | @fa-var-google-wallet: "\f1ee"; 258 | @fa-var-graduation-cap: "\f19d"; 259 | @fa-var-group: "\f0c0"; 260 | @fa-var-h-square: "\f0fd"; 261 | @fa-var-hacker-news: "\f1d4"; 262 | @fa-var-hand-o-down: "\f0a7"; 263 | @fa-var-hand-o-left: "\f0a5"; 264 | @fa-var-hand-o-right: "\f0a4"; 265 | @fa-var-hand-o-up: "\f0a6"; 266 | @fa-var-hdd-o: "\f0a0"; 267 | @fa-var-header: "\f1dc"; 268 | @fa-var-headphones: "\f025"; 269 | @fa-var-heart: "\f004"; 270 | @fa-var-heart-o: "\f08a"; 271 | @fa-var-history: "\f1da"; 272 | @fa-var-home: "\f015"; 273 | @fa-var-hospital-o: "\f0f8"; 274 | @fa-var-html5: "\f13b"; 275 | @fa-var-ils: "\f20b"; 276 | @fa-var-image: "\f03e"; 277 | @fa-var-inbox: "\f01c"; 278 | @fa-var-indent: "\f03c"; 279 | @fa-var-info: "\f129"; 280 | @fa-var-info-circle: "\f05a"; 281 | @fa-var-inr: "\f156"; 282 | @fa-var-instagram: "\f16d"; 283 | @fa-var-institution: "\f19c"; 284 | @fa-var-ioxhost: "\f208"; 285 | @fa-var-italic: "\f033"; 286 | @fa-var-joomla: "\f1aa"; 287 | @fa-var-jpy: "\f157"; 288 | @fa-var-jsfiddle: "\f1cc"; 289 | @fa-var-key: "\f084"; 290 | @fa-var-keyboard-o: "\f11c"; 291 | @fa-var-krw: "\f159"; 292 | @fa-var-language: "\f1ab"; 293 | @fa-var-laptop: "\f109"; 294 | @fa-var-lastfm: "\f202"; 295 | @fa-var-lastfm-square: "\f203"; 296 | @fa-var-leaf: "\f06c"; 297 | @fa-var-legal: "\f0e3"; 298 | @fa-var-lemon-o: "\f094"; 299 | @fa-var-level-down: "\f149"; 300 | @fa-var-level-up: "\f148"; 301 | @fa-var-life-bouy: "\f1cd"; 302 | @fa-var-life-buoy: "\f1cd"; 303 | @fa-var-life-ring: "\f1cd"; 304 | @fa-var-life-saver: "\f1cd"; 305 | @fa-var-lightbulb-o: "\f0eb"; 306 | @fa-var-line-chart: "\f201"; 307 | @fa-var-link: "\f0c1"; 308 | @fa-var-linkedin: "\f0e1"; 309 | @fa-var-linkedin-square: "\f08c"; 310 | @fa-var-linux: "\f17c"; 311 | @fa-var-list: "\f03a"; 312 | @fa-var-list-alt: "\f022"; 313 | @fa-var-list-ol: "\f0cb"; 314 | @fa-var-list-ul: "\f0ca"; 315 | @fa-var-location-arrow: "\f124"; 316 | @fa-var-lock: "\f023"; 317 | @fa-var-long-arrow-down: "\f175"; 318 | @fa-var-long-arrow-left: "\f177"; 319 | @fa-var-long-arrow-right: "\f178"; 320 | @fa-var-long-arrow-up: "\f176"; 321 | @fa-var-magic: "\f0d0"; 322 | @fa-var-magnet: "\f076"; 323 | @fa-var-mail-forward: "\f064"; 324 | @fa-var-mail-reply: "\f112"; 325 | @fa-var-mail-reply-all: "\f122"; 326 | @fa-var-male: "\f183"; 327 | @fa-var-map-marker: "\f041"; 328 | @fa-var-maxcdn: "\f136"; 329 | @fa-var-meanpath: "\f20c"; 330 | @fa-var-medkit: "\f0fa"; 331 | @fa-var-meh-o: "\f11a"; 332 | @fa-var-microphone: "\f130"; 333 | @fa-var-microphone-slash: "\f131"; 334 | @fa-var-minus: "\f068"; 335 | @fa-var-minus-circle: "\f056"; 336 | @fa-var-minus-square: "\f146"; 337 | @fa-var-minus-square-o: "\f147"; 338 | @fa-var-mobile: "\f10b"; 339 | @fa-var-mobile-phone: "\f10b"; 340 | @fa-var-money: "\f0d6"; 341 | @fa-var-moon-o: "\f186"; 342 | @fa-var-mortar-board: "\f19d"; 343 | @fa-var-music: "\f001"; 344 | @fa-var-navicon: "\f0c9"; 345 | @fa-var-newspaper-o: "\f1ea"; 346 | @fa-var-openid: "\f19b"; 347 | @fa-var-outdent: "\f03b"; 348 | @fa-var-pagelines: "\f18c"; 349 | @fa-var-paint-brush: "\f1fc"; 350 | @fa-var-paper-plane: "\f1d8"; 351 | @fa-var-paper-plane-o: "\f1d9"; 352 | @fa-var-paperclip: "\f0c6"; 353 | @fa-var-paragraph: "\f1dd"; 354 | @fa-var-paste: "\f0ea"; 355 | @fa-var-pause: "\f04c"; 356 | @fa-var-paw: "\f1b0"; 357 | @fa-var-paypal: "\f1ed"; 358 | @fa-var-pencil: "\f040"; 359 | @fa-var-pencil-square: "\f14b"; 360 | @fa-var-pencil-square-o: "\f044"; 361 | @fa-var-phone: "\f095"; 362 | @fa-var-phone-square: "\f098"; 363 | @fa-var-photo: "\f03e"; 364 | @fa-var-picture-o: "\f03e"; 365 | @fa-var-pie-chart: "\f200"; 366 | @fa-var-pied-piper: "\f1a7"; 367 | @fa-var-pied-piper-alt: "\f1a8"; 368 | @fa-var-pinterest: "\f0d2"; 369 | @fa-var-pinterest-square: "\f0d3"; 370 | @fa-var-plane: "\f072"; 371 | @fa-var-play: "\f04b"; 372 | @fa-var-play-circle: "\f144"; 373 | @fa-var-play-circle-o: "\f01d"; 374 | @fa-var-plug: "\f1e6"; 375 | @fa-var-plus: "\f067"; 376 | @fa-var-plus-circle: "\f055"; 377 | @fa-var-plus-square: "\f0fe"; 378 | @fa-var-plus-square-o: "\f196"; 379 | @fa-var-power-off: "\f011"; 380 | @fa-var-print: "\f02f"; 381 | @fa-var-puzzle-piece: "\f12e"; 382 | @fa-var-qq: "\f1d6"; 383 | @fa-var-qrcode: "\f029"; 384 | @fa-var-question: "\f128"; 385 | @fa-var-question-circle: "\f059"; 386 | @fa-var-quote-left: "\f10d"; 387 | @fa-var-quote-right: "\f10e"; 388 | @fa-var-ra: "\f1d0"; 389 | @fa-var-random: "\f074"; 390 | @fa-var-rebel: "\f1d0"; 391 | @fa-var-recycle: "\f1b8"; 392 | @fa-var-reddit: "\f1a1"; 393 | @fa-var-reddit-square: "\f1a2"; 394 | @fa-var-refresh: "\f021"; 395 | @fa-var-remove: "\f00d"; 396 | @fa-var-renren: "\f18b"; 397 | @fa-var-reorder: "\f0c9"; 398 | @fa-var-repeat: "\f01e"; 399 | @fa-var-reply: "\f112"; 400 | @fa-var-reply-all: "\f122"; 401 | @fa-var-retweet: "\f079"; 402 | @fa-var-rmb: "\f157"; 403 | @fa-var-road: "\f018"; 404 | @fa-var-rocket: "\f135"; 405 | @fa-var-rotate-left: "\f0e2"; 406 | @fa-var-rotate-right: "\f01e"; 407 | @fa-var-rouble: "\f158"; 408 | @fa-var-rss: "\f09e"; 409 | @fa-var-rss-square: "\f143"; 410 | @fa-var-rub: "\f158"; 411 | @fa-var-ruble: "\f158"; 412 | @fa-var-rupee: "\f156"; 413 | @fa-var-save: "\f0c7"; 414 | @fa-var-scissors: "\f0c4"; 415 | @fa-var-search: "\f002"; 416 | @fa-var-search-minus: "\f010"; 417 | @fa-var-search-plus: "\f00e"; 418 | @fa-var-send: "\f1d8"; 419 | @fa-var-send-o: "\f1d9"; 420 | @fa-var-share: "\f064"; 421 | @fa-var-share-alt: "\f1e0"; 422 | @fa-var-share-alt-square: "\f1e1"; 423 | @fa-var-share-square: "\f14d"; 424 | @fa-var-share-square-o: "\f045"; 425 | @fa-var-shekel: "\f20b"; 426 | @fa-var-sheqel: "\f20b"; 427 | @fa-var-shield: "\f132"; 428 | @fa-var-shopping-cart: "\f07a"; 429 | @fa-var-sign-in: "\f090"; 430 | @fa-var-sign-out: "\f08b"; 431 | @fa-var-signal: "\f012"; 432 | @fa-var-sitemap: "\f0e8"; 433 | @fa-var-skype: "\f17e"; 434 | @fa-var-slack: "\f198"; 435 | @fa-var-sliders: "\f1de"; 436 | @fa-var-slideshare: "\f1e7"; 437 | @fa-var-smile-o: "\f118"; 438 | @fa-var-soccer-ball-o: "\f1e3"; 439 | @fa-var-sort: "\f0dc"; 440 | @fa-var-sort-alpha-asc: "\f15d"; 441 | @fa-var-sort-alpha-desc: "\f15e"; 442 | @fa-var-sort-amount-asc: "\f160"; 443 | @fa-var-sort-amount-desc: "\f161"; 444 | @fa-var-sort-asc: "\f0de"; 445 | @fa-var-sort-desc: "\f0dd"; 446 | @fa-var-sort-down: "\f0dd"; 447 | @fa-var-sort-numeric-asc: "\f162"; 448 | @fa-var-sort-numeric-desc: "\f163"; 449 | @fa-var-sort-up: "\f0de"; 450 | @fa-var-soundcloud: "\f1be"; 451 | @fa-var-space-shuttle: "\f197"; 452 | @fa-var-spinner: "\f110"; 453 | @fa-var-spoon: "\f1b1"; 454 | @fa-var-spotify: "\f1bc"; 455 | @fa-var-square: "\f0c8"; 456 | @fa-var-square-o: "\f096"; 457 | @fa-var-stack-exchange: "\f18d"; 458 | @fa-var-stack-overflow: "\f16c"; 459 | @fa-var-star: "\f005"; 460 | @fa-var-star-half: "\f089"; 461 | @fa-var-star-half-empty: "\f123"; 462 | @fa-var-star-half-full: "\f123"; 463 | @fa-var-star-half-o: "\f123"; 464 | @fa-var-star-o: "\f006"; 465 | @fa-var-steam: "\f1b6"; 466 | @fa-var-steam-square: "\f1b7"; 467 | @fa-var-step-backward: "\f048"; 468 | @fa-var-step-forward: "\f051"; 469 | @fa-var-stethoscope: "\f0f1"; 470 | @fa-var-stop: "\f04d"; 471 | @fa-var-strikethrough: "\f0cc"; 472 | @fa-var-stumbleupon: "\f1a4"; 473 | @fa-var-stumbleupon-circle: "\f1a3"; 474 | @fa-var-subscript: "\f12c"; 475 | @fa-var-suitcase: "\f0f2"; 476 | @fa-var-sun-o: "\f185"; 477 | @fa-var-superscript: "\f12b"; 478 | @fa-var-support: "\f1cd"; 479 | @fa-var-table: "\f0ce"; 480 | @fa-var-tablet: "\f10a"; 481 | @fa-var-tachometer: "\f0e4"; 482 | @fa-var-tag: "\f02b"; 483 | @fa-var-tags: "\f02c"; 484 | @fa-var-tasks: "\f0ae"; 485 | @fa-var-taxi: "\f1ba"; 486 | @fa-var-tencent-weibo: "\f1d5"; 487 | @fa-var-terminal: "\f120"; 488 | @fa-var-text-height: "\f034"; 489 | @fa-var-text-width: "\f035"; 490 | @fa-var-th: "\f00a"; 491 | @fa-var-th-large: "\f009"; 492 | @fa-var-th-list: "\f00b"; 493 | @fa-var-thumb-tack: "\f08d"; 494 | @fa-var-thumbs-down: "\f165"; 495 | @fa-var-thumbs-o-down: "\f088"; 496 | @fa-var-thumbs-o-up: "\f087"; 497 | @fa-var-thumbs-up: "\f164"; 498 | @fa-var-ticket: "\f145"; 499 | @fa-var-times: "\f00d"; 500 | @fa-var-times-circle: "\f057"; 501 | @fa-var-times-circle-o: "\f05c"; 502 | @fa-var-tint: "\f043"; 503 | @fa-var-toggle-down: "\f150"; 504 | @fa-var-toggle-left: "\f191"; 505 | @fa-var-toggle-off: "\f204"; 506 | @fa-var-toggle-on: "\f205"; 507 | @fa-var-toggle-right: "\f152"; 508 | @fa-var-toggle-up: "\f151"; 509 | @fa-var-trash: "\f1f8"; 510 | @fa-var-trash-o: "\f014"; 511 | @fa-var-tree: "\f1bb"; 512 | @fa-var-trello: "\f181"; 513 | @fa-var-trophy: "\f091"; 514 | @fa-var-truck: "\f0d1"; 515 | @fa-var-try: "\f195"; 516 | @fa-var-tty: "\f1e4"; 517 | @fa-var-tumblr: "\f173"; 518 | @fa-var-tumblr-square: "\f174"; 519 | @fa-var-turkish-lira: "\f195"; 520 | @fa-var-twitch: "\f1e8"; 521 | @fa-var-twitter: "\f099"; 522 | @fa-var-twitter-square: "\f081"; 523 | @fa-var-umbrella: "\f0e9"; 524 | @fa-var-underline: "\f0cd"; 525 | @fa-var-undo: "\f0e2"; 526 | @fa-var-university: "\f19c"; 527 | @fa-var-unlink: "\f127"; 528 | @fa-var-unlock: "\f09c"; 529 | @fa-var-unlock-alt: "\f13e"; 530 | @fa-var-unsorted: "\f0dc"; 531 | @fa-var-upload: "\f093"; 532 | @fa-var-usd: "\f155"; 533 | @fa-var-user: "\f007"; 534 | @fa-var-user-md: "\f0f0"; 535 | @fa-var-users: "\f0c0"; 536 | @fa-var-video-camera: "\f03d"; 537 | @fa-var-vimeo-square: "\f194"; 538 | @fa-var-vine: "\f1ca"; 539 | @fa-var-vk: "\f189"; 540 | @fa-var-volume-down: "\f027"; 541 | @fa-var-volume-off: "\f026"; 542 | @fa-var-volume-up: "\f028"; 543 | @fa-var-warning: "\f071"; 544 | @fa-var-wechat: "\f1d7"; 545 | @fa-var-weibo: "\f18a"; 546 | @fa-var-weixin: "\f1d7"; 547 | @fa-var-wheelchair: "\f193"; 548 | @fa-var-wifi: "\f1eb"; 549 | @fa-var-windows: "\f17a"; 550 | @fa-var-won: "\f159"; 551 | @fa-var-wordpress: "\f19a"; 552 | @fa-var-wrench: "\f0ad"; 553 | @fa-var-xing: "\f168"; 554 | @fa-var-xing-square: "\f169"; 555 | @fa-var-yahoo: "\f19e"; 556 | @fa-var-yelp: "\f1e9"; 557 | @fa-var-yen: "\f157"; 558 | @fa-var-youtube: "\f167"; 559 | @fa-var-youtube-play: "\f16a"; 560 | @fa-var-youtube-square: "\f166"; 561 | 562 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | @mixin fa-icon-rotate($degrees, $rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 15 | -webkit-transform: rotate($degrees); 16 | -ms-transform: rotate($degrees); 17 | transform: rotate($degrees); 18 | } 19 | 20 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 21 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 22 | -webkit-transform: scale($horiz, $vert); 23 | -ms-transform: scale($horiz, $vert); 24 | transform: scale($horiz, $vert); 25 | } 26 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 9 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 10 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 11 | //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_spinning.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | $fa-font-path: "../fonts" !default; 5 | //$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts" !default; // for referencing Bootstrap CDN font files directly 6 | $fa-css-prefix: fa !default; 7 | $fa-version: "4.2.0" !default; 8 | $fa-border-color: #eee !default; 9 | $fa-inverse: #fff !default; 10 | $fa-li-width: (30em / 14) !default; 11 | 12 | $fa-var-adjust: "\f042"; 13 | $fa-var-adn: "\f170"; 14 | $fa-var-align-center: "\f037"; 15 | $fa-var-align-justify: "\f039"; 16 | $fa-var-align-left: "\f036"; 17 | $fa-var-align-right: "\f038"; 18 | $fa-var-ambulance: "\f0f9"; 19 | $fa-var-anchor: "\f13d"; 20 | $fa-var-android: "\f17b"; 21 | $fa-var-angellist: "\f209"; 22 | $fa-var-angle-double-down: "\f103"; 23 | $fa-var-angle-double-left: "\f100"; 24 | $fa-var-angle-double-right: "\f101"; 25 | $fa-var-angle-double-up: "\f102"; 26 | $fa-var-angle-down: "\f107"; 27 | $fa-var-angle-left: "\f104"; 28 | $fa-var-angle-right: "\f105"; 29 | $fa-var-angle-up: "\f106"; 30 | $fa-var-apple: "\f179"; 31 | $fa-var-archive: "\f187"; 32 | $fa-var-area-chart: "\f1fe"; 33 | $fa-var-arrow-circle-down: "\f0ab"; 34 | $fa-var-arrow-circle-left: "\f0a8"; 35 | $fa-var-arrow-circle-o-down: "\f01a"; 36 | $fa-var-arrow-circle-o-left: "\f190"; 37 | $fa-var-arrow-circle-o-right: "\f18e"; 38 | $fa-var-arrow-circle-o-up: "\f01b"; 39 | $fa-var-arrow-circle-right: "\f0a9"; 40 | $fa-var-arrow-circle-up: "\f0aa"; 41 | $fa-var-arrow-down: "\f063"; 42 | $fa-var-arrow-left: "\f060"; 43 | $fa-var-arrow-right: "\f061"; 44 | $fa-var-arrow-up: "\f062"; 45 | $fa-var-arrows: "\f047"; 46 | $fa-var-arrows-alt: "\f0b2"; 47 | $fa-var-arrows-h: "\f07e"; 48 | $fa-var-arrows-v: "\f07d"; 49 | $fa-var-asterisk: "\f069"; 50 | $fa-var-at: "\f1fa"; 51 | $fa-var-automobile: "\f1b9"; 52 | $fa-var-backward: "\f04a"; 53 | $fa-var-ban: "\f05e"; 54 | $fa-var-bank: "\f19c"; 55 | $fa-var-bar-chart: "\f080"; 56 | $fa-var-bar-chart-o: "\f080"; 57 | $fa-var-barcode: "\f02a"; 58 | $fa-var-bars: "\f0c9"; 59 | $fa-var-beer: "\f0fc"; 60 | $fa-var-behance: "\f1b4"; 61 | $fa-var-behance-square: "\f1b5"; 62 | $fa-var-bell: "\f0f3"; 63 | $fa-var-bell-o: "\f0a2"; 64 | $fa-var-bell-slash: "\f1f6"; 65 | $fa-var-bell-slash-o: "\f1f7"; 66 | $fa-var-bicycle: "\f206"; 67 | $fa-var-binoculars: "\f1e5"; 68 | $fa-var-birthday-cake: "\f1fd"; 69 | $fa-var-bitbucket: "\f171"; 70 | $fa-var-bitbucket-square: "\f172"; 71 | $fa-var-bitcoin: "\f15a"; 72 | $fa-var-bold: "\f032"; 73 | $fa-var-bolt: "\f0e7"; 74 | $fa-var-bomb: "\f1e2"; 75 | $fa-var-book: "\f02d"; 76 | $fa-var-bookmark: "\f02e"; 77 | $fa-var-bookmark-o: "\f097"; 78 | $fa-var-briefcase: "\f0b1"; 79 | $fa-var-btc: "\f15a"; 80 | $fa-var-bug: "\f188"; 81 | $fa-var-building: "\f1ad"; 82 | $fa-var-building-o: "\f0f7"; 83 | $fa-var-bullhorn: "\f0a1"; 84 | $fa-var-bullseye: "\f140"; 85 | $fa-var-bus: "\f207"; 86 | $fa-var-cab: "\f1ba"; 87 | $fa-var-calculator: "\f1ec"; 88 | $fa-var-calendar: "\f073"; 89 | $fa-var-calendar-o: "\f133"; 90 | $fa-var-camera: "\f030"; 91 | $fa-var-camera-retro: "\f083"; 92 | $fa-var-car: "\f1b9"; 93 | $fa-var-caret-down: "\f0d7"; 94 | $fa-var-caret-left: "\f0d9"; 95 | $fa-var-caret-right: "\f0da"; 96 | $fa-var-caret-square-o-down: "\f150"; 97 | $fa-var-caret-square-o-left: "\f191"; 98 | $fa-var-caret-square-o-right: "\f152"; 99 | $fa-var-caret-square-o-up: "\f151"; 100 | $fa-var-caret-up: "\f0d8"; 101 | $fa-var-cc: "\f20a"; 102 | $fa-var-cc-amex: "\f1f3"; 103 | $fa-var-cc-discover: "\f1f2"; 104 | $fa-var-cc-mastercard: "\f1f1"; 105 | $fa-var-cc-paypal: "\f1f4"; 106 | $fa-var-cc-stripe: "\f1f5"; 107 | $fa-var-cc-visa: "\f1f0"; 108 | $fa-var-certificate: "\f0a3"; 109 | $fa-var-chain: "\f0c1"; 110 | $fa-var-chain-broken: "\f127"; 111 | $fa-var-check: "\f00c"; 112 | $fa-var-check-circle: "\f058"; 113 | $fa-var-check-circle-o: "\f05d"; 114 | $fa-var-check-square: "\f14a"; 115 | $fa-var-check-square-o: "\f046"; 116 | $fa-var-chevron-circle-down: "\f13a"; 117 | $fa-var-chevron-circle-left: "\f137"; 118 | $fa-var-chevron-circle-right: "\f138"; 119 | $fa-var-chevron-circle-up: "\f139"; 120 | $fa-var-chevron-down: "\f078"; 121 | $fa-var-chevron-left: "\f053"; 122 | $fa-var-chevron-right: "\f054"; 123 | $fa-var-chevron-up: "\f077"; 124 | $fa-var-child: "\f1ae"; 125 | $fa-var-circle: "\f111"; 126 | $fa-var-circle-o: "\f10c"; 127 | $fa-var-circle-o-notch: "\f1ce"; 128 | $fa-var-circle-thin: "\f1db"; 129 | $fa-var-clipboard: "\f0ea"; 130 | $fa-var-clock-o: "\f017"; 131 | $fa-var-close: "\f00d"; 132 | $fa-var-cloud: "\f0c2"; 133 | $fa-var-cloud-download: "\f0ed"; 134 | $fa-var-cloud-upload: "\f0ee"; 135 | $fa-var-cny: "\f157"; 136 | $fa-var-code: "\f121"; 137 | $fa-var-code-fork: "\f126"; 138 | $fa-var-codepen: "\f1cb"; 139 | $fa-var-coffee: "\f0f4"; 140 | $fa-var-cog: "\f013"; 141 | $fa-var-cogs: "\f085"; 142 | $fa-var-columns: "\f0db"; 143 | $fa-var-comment: "\f075"; 144 | $fa-var-comment-o: "\f0e5"; 145 | $fa-var-comments: "\f086"; 146 | $fa-var-comments-o: "\f0e6"; 147 | $fa-var-compass: "\f14e"; 148 | $fa-var-compress: "\f066"; 149 | $fa-var-copy: "\f0c5"; 150 | $fa-var-copyright: "\f1f9"; 151 | $fa-var-credit-card: "\f09d"; 152 | $fa-var-crop: "\f125"; 153 | $fa-var-crosshairs: "\f05b"; 154 | $fa-var-css3: "\f13c"; 155 | $fa-var-cube: "\f1b2"; 156 | $fa-var-cubes: "\f1b3"; 157 | $fa-var-cut: "\f0c4"; 158 | $fa-var-cutlery: "\f0f5"; 159 | $fa-var-dashboard: "\f0e4"; 160 | $fa-var-database: "\f1c0"; 161 | $fa-var-dedent: "\f03b"; 162 | $fa-var-delicious: "\f1a5"; 163 | $fa-var-desktop: "\f108"; 164 | $fa-var-deviantart: "\f1bd"; 165 | $fa-var-digg: "\f1a6"; 166 | $fa-var-dollar: "\f155"; 167 | $fa-var-dot-circle-o: "\f192"; 168 | $fa-var-download: "\f019"; 169 | $fa-var-dribbble: "\f17d"; 170 | $fa-var-dropbox: "\f16b"; 171 | $fa-var-drupal: "\f1a9"; 172 | $fa-var-edit: "\f044"; 173 | $fa-var-eject: "\f052"; 174 | $fa-var-ellipsis-h: "\f141"; 175 | $fa-var-ellipsis-v: "\f142"; 176 | $fa-var-empire: "\f1d1"; 177 | $fa-var-envelope: "\f0e0"; 178 | $fa-var-envelope-o: "\f003"; 179 | $fa-var-envelope-square: "\f199"; 180 | $fa-var-eraser: "\f12d"; 181 | $fa-var-eur: "\f153"; 182 | $fa-var-euro: "\f153"; 183 | $fa-var-exchange: "\f0ec"; 184 | $fa-var-exclamation: "\f12a"; 185 | $fa-var-exclamation-circle: "\f06a"; 186 | $fa-var-exclamation-triangle: "\f071"; 187 | $fa-var-expand: "\f065"; 188 | $fa-var-external-link: "\f08e"; 189 | $fa-var-external-link-square: "\f14c"; 190 | $fa-var-eye: "\f06e"; 191 | $fa-var-eye-slash: "\f070"; 192 | $fa-var-eyedropper: "\f1fb"; 193 | $fa-var-facebook: "\f09a"; 194 | $fa-var-facebook-square: "\f082"; 195 | $fa-var-fast-backward: "\f049"; 196 | $fa-var-fast-forward: "\f050"; 197 | $fa-var-fax: "\f1ac"; 198 | $fa-var-female: "\f182"; 199 | $fa-var-fighter-jet: "\f0fb"; 200 | $fa-var-file: "\f15b"; 201 | $fa-var-file-archive-o: "\f1c6"; 202 | $fa-var-file-audio-o: "\f1c7"; 203 | $fa-var-file-code-o: "\f1c9"; 204 | $fa-var-file-excel-o: "\f1c3"; 205 | $fa-var-file-image-o: "\f1c5"; 206 | $fa-var-file-movie-o: "\f1c8"; 207 | $fa-var-file-o: "\f016"; 208 | $fa-var-file-pdf-o: "\f1c1"; 209 | $fa-var-file-photo-o: "\f1c5"; 210 | $fa-var-file-picture-o: "\f1c5"; 211 | $fa-var-file-powerpoint-o: "\f1c4"; 212 | $fa-var-file-sound-o: "\f1c7"; 213 | $fa-var-file-text: "\f15c"; 214 | $fa-var-file-text-o: "\f0f6"; 215 | $fa-var-file-video-o: "\f1c8"; 216 | $fa-var-file-word-o: "\f1c2"; 217 | $fa-var-file-zip-o: "\f1c6"; 218 | $fa-var-files-o: "\f0c5"; 219 | $fa-var-film: "\f008"; 220 | $fa-var-filter: "\f0b0"; 221 | $fa-var-fire: "\f06d"; 222 | $fa-var-fire-extinguisher: "\f134"; 223 | $fa-var-flag: "\f024"; 224 | $fa-var-flag-checkered: "\f11e"; 225 | $fa-var-flag-o: "\f11d"; 226 | $fa-var-flash: "\f0e7"; 227 | $fa-var-flask: "\f0c3"; 228 | $fa-var-flickr: "\f16e"; 229 | $fa-var-floppy-o: "\f0c7"; 230 | $fa-var-folder: "\f07b"; 231 | $fa-var-folder-o: "\f114"; 232 | $fa-var-folder-open: "\f07c"; 233 | $fa-var-folder-open-o: "\f115"; 234 | $fa-var-font: "\f031"; 235 | $fa-var-forward: "\f04e"; 236 | $fa-var-foursquare: "\f180"; 237 | $fa-var-frown-o: "\f119"; 238 | $fa-var-futbol-o: "\f1e3"; 239 | $fa-var-gamepad: "\f11b"; 240 | $fa-var-gavel: "\f0e3"; 241 | $fa-var-gbp: "\f154"; 242 | $fa-var-ge: "\f1d1"; 243 | $fa-var-gear: "\f013"; 244 | $fa-var-gears: "\f085"; 245 | $fa-var-gift: "\f06b"; 246 | $fa-var-git: "\f1d3"; 247 | $fa-var-git-square: "\f1d2"; 248 | $fa-var-github: "\f09b"; 249 | $fa-var-github-alt: "\f113"; 250 | $fa-var-github-square: "\f092"; 251 | $fa-var-gittip: "\f184"; 252 | $fa-var-glass: "\f000"; 253 | $fa-var-globe: "\f0ac"; 254 | $fa-var-google: "\f1a0"; 255 | $fa-var-google-plus: "\f0d5"; 256 | $fa-var-google-plus-square: "\f0d4"; 257 | $fa-var-google-wallet: "\f1ee"; 258 | $fa-var-graduation-cap: "\f19d"; 259 | $fa-var-group: "\f0c0"; 260 | $fa-var-h-square: "\f0fd"; 261 | $fa-var-hacker-news: "\f1d4"; 262 | $fa-var-hand-o-down: "\f0a7"; 263 | $fa-var-hand-o-left: "\f0a5"; 264 | $fa-var-hand-o-right: "\f0a4"; 265 | $fa-var-hand-o-up: "\f0a6"; 266 | $fa-var-hdd-o: "\f0a0"; 267 | $fa-var-header: "\f1dc"; 268 | $fa-var-headphones: "\f025"; 269 | $fa-var-heart: "\f004"; 270 | $fa-var-heart-o: "\f08a"; 271 | $fa-var-history: "\f1da"; 272 | $fa-var-home: "\f015"; 273 | $fa-var-hospital-o: "\f0f8"; 274 | $fa-var-html5: "\f13b"; 275 | $fa-var-ils: "\f20b"; 276 | $fa-var-image: "\f03e"; 277 | $fa-var-inbox: "\f01c"; 278 | $fa-var-indent: "\f03c"; 279 | $fa-var-info: "\f129"; 280 | $fa-var-info-circle: "\f05a"; 281 | $fa-var-inr: "\f156"; 282 | $fa-var-instagram: "\f16d"; 283 | $fa-var-institution: "\f19c"; 284 | $fa-var-ioxhost: "\f208"; 285 | $fa-var-italic: "\f033"; 286 | $fa-var-joomla: "\f1aa"; 287 | $fa-var-jpy: "\f157"; 288 | $fa-var-jsfiddle: "\f1cc"; 289 | $fa-var-key: "\f084"; 290 | $fa-var-keyboard-o: "\f11c"; 291 | $fa-var-krw: "\f159"; 292 | $fa-var-language: "\f1ab"; 293 | $fa-var-laptop: "\f109"; 294 | $fa-var-lastfm: "\f202"; 295 | $fa-var-lastfm-square: "\f203"; 296 | $fa-var-leaf: "\f06c"; 297 | $fa-var-legal: "\f0e3"; 298 | $fa-var-lemon-o: "\f094"; 299 | $fa-var-level-down: "\f149"; 300 | $fa-var-level-up: "\f148"; 301 | $fa-var-life-bouy: "\f1cd"; 302 | $fa-var-life-buoy: "\f1cd"; 303 | $fa-var-life-ring: "\f1cd"; 304 | $fa-var-life-saver: "\f1cd"; 305 | $fa-var-lightbulb-o: "\f0eb"; 306 | $fa-var-line-chart: "\f201"; 307 | $fa-var-link: "\f0c1"; 308 | $fa-var-linkedin: "\f0e1"; 309 | $fa-var-linkedin-square: "\f08c"; 310 | $fa-var-linux: "\f17c"; 311 | $fa-var-list: "\f03a"; 312 | $fa-var-list-alt: "\f022"; 313 | $fa-var-list-ol: "\f0cb"; 314 | $fa-var-list-ul: "\f0ca"; 315 | $fa-var-location-arrow: "\f124"; 316 | $fa-var-lock: "\f023"; 317 | $fa-var-long-arrow-down: "\f175"; 318 | $fa-var-long-arrow-left: "\f177"; 319 | $fa-var-long-arrow-right: "\f178"; 320 | $fa-var-long-arrow-up: "\f176"; 321 | $fa-var-magic: "\f0d0"; 322 | $fa-var-magnet: "\f076"; 323 | $fa-var-mail-forward: "\f064"; 324 | $fa-var-mail-reply: "\f112"; 325 | $fa-var-mail-reply-all: "\f122"; 326 | $fa-var-male: "\f183"; 327 | $fa-var-map-marker: "\f041"; 328 | $fa-var-maxcdn: "\f136"; 329 | $fa-var-meanpath: "\f20c"; 330 | $fa-var-medkit: "\f0fa"; 331 | $fa-var-meh-o: "\f11a"; 332 | $fa-var-microphone: "\f130"; 333 | $fa-var-microphone-slash: "\f131"; 334 | $fa-var-minus: "\f068"; 335 | $fa-var-minus-circle: "\f056"; 336 | $fa-var-minus-square: "\f146"; 337 | $fa-var-minus-square-o: "\f147"; 338 | $fa-var-mobile: "\f10b"; 339 | $fa-var-mobile-phone: "\f10b"; 340 | $fa-var-money: "\f0d6"; 341 | $fa-var-moon-o: "\f186"; 342 | $fa-var-mortar-board: "\f19d"; 343 | $fa-var-music: "\f001"; 344 | $fa-var-navicon: "\f0c9"; 345 | $fa-var-newspaper-o: "\f1ea"; 346 | $fa-var-openid: "\f19b"; 347 | $fa-var-outdent: "\f03b"; 348 | $fa-var-pagelines: "\f18c"; 349 | $fa-var-paint-brush: "\f1fc"; 350 | $fa-var-paper-plane: "\f1d8"; 351 | $fa-var-paper-plane-o: "\f1d9"; 352 | $fa-var-paperclip: "\f0c6"; 353 | $fa-var-paragraph: "\f1dd"; 354 | $fa-var-paste: "\f0ea"; 355 | $fa-var-pause: "\f04c"; 356 | $fa-var-paw: "\f1b0"; 357 | $fa-var-paypal: "\f1ed"; 358 | $fa-var-pencil: "\f040"; 359 | $fa-var-pencil-square: "\f14b"; 360 | $fa-var-pencil-square-o: "\f044"; 361 | $fa-var-phone: "\f095"; 362 | $fa-var-phone-square: "\f098"; 363 | $fa-var-photo: "\f03e"; 364 | $fa-var-picture-o: "\f03e"; 365 | $fa-var-pie-chart: "\f200"; 366 | $fa-var-pied-piper: "\f1a7"; 367 | $fa-var-pied-piper-alt: "\f1a8"; 368 | $fa-var-pinterest: "\f0d2"; 369 | $fa-var-pinterest-square: "\f0d3"; 370 | $fa-var-plane: "\f072"; 371 | $fa-var-play: "\f04b"; 372 | $fa-var-play-circle: "\f144"; 373 | $fa-var-play-circle-o: "\f01d"; 374 | $fa-var-plug: "\f1e6"; 375 | $fa-var-plus: "\f067"; 376 | $fa-var-plus-circle: "\f055"; 377 | $fa-var-plus-square: "\f0fe"; 378 | $fa-var-plus-square-o: "\f196"; 379 | $fa-var-power-off: "\f011"; 380 | $fa-var-print: "\f02f"; 381 | $fa-var-puzzle-piece: "\f12e"; 382 | $fa-var-qq: "\f1d6"; 383 | $fa-var-qrcode: "\f029"; 384 | $fa-var-question: "\f128"; 385 | $fa-var-question-circle: "\f059"; 386 | $fa-var-quote-left: "\f10d"; 387 | $fa-var-quote-right: "\f10e"; 388 | $fa-var-ra: "\f1d0"; 389 | $fa-var-random: "\f074"; 390 | $fa-var-rebel: "\f1d0"; 391 | $fa-var-recycle: "\f1b8"; 392 | $fa-var-reddit: "\f1a1"; 393 | $fa-var-reddit-square: "\f1a2"; 394 | $fa-var-refresh: "\f021"; 395 | $fa-var-remove: "\f00d"; 396 | $fa-var-renren: "\f18b"; 397 | $fa-var-reorder: "\f0c9"; 398 | $fa-var-repeat: "\f01e"; 399 | $fa-var-reply: "\f112"; 400 | $fa-var-reply-all: "\f122"; 401 | $fa-var-retweet: "\f079"; 402 | $fa-var-rmb: "\f157"; 403 | $fa-var-road: "\f018"; 404 | $fa-var-rocket: "\f135"; 405 | $fa-var-rotate-left: "\f0e2"; 406 | $fa-var-rotate-right: "\f01e"; 407 | $fa-var-rouble: "\f158"; 408 | $fa-var-rss: "\f09e"; 409 | $fa-var-rss-square: "\f143"; 410 | $fa-var-rub: "\f158"; 411 | $fa-var-ruble: "\f158"; 412 | $fa-var-rupee: "\f156"; 413 | $fa-var-save: "\f0c7"; 414 | $fa-var-scissors: "\f0c4"; 415 | $fa-var-search: "\f002"; 416 | $fa-var-search-minus: "\f010"; 417 | $fa-var-search-plus: "\f00e"; 418 | $fa-var-send: "\f1d8"; 419 | $fa-var-send-o: "\f1d9"; 420 | $fa-var-share: "\f064"; 421 | $fa-var-share-alt: "\f1e0"; 422 | $fa-var-share-alt-square: "\f1e1"; 423 | $fa-var-share-square: "\f14d"; 424 | $fa-var-share-square-o: "\f045"; 425 | $fa-var-shekel: "\f20b"; 426 | $fa-var-sheqel: "\f20b"; 427 | $fa-var-shield: "\f132"; 428 | $fa-var-shopping-cart: "\f07a"; 429 | $fa-var-sign-in: "\f090"; 430 | $fa-var-sign-out: "\f08b"; 431 | $fa-var-signal: "\f012"; 432 | $fa-var-sitemap: "\f0e8"; 433 | $fa-var-skype: "\f17e"; 434 | $fa-var-slack: "\f198"; 435 | $fa-var-sliders: "\f1de"; 436 | $fa-var-slideshare: "\f1e7"; 437 | $fa-var-smile-o: "\f118"; 438 | $fa-var-soccer-ball-o: "\f1e3"; 439 | $fa-var-sort: "\f0dc"; 440 | $fa-var-sort-alpha-asc: "\f15d"; 441 | $fa-var-sort-alpha-desc: "\f15e"; 442 | $fa-var-sort-amount-asc: "\f160"; 443 | $fa-var-sort-amount-desc: "\f161"; 444 | $fa-var-sort-asc: "\f0de"; 445 | $fa-var-sort-desc: "\f0dd"; 446 | $fa-var-sort-down: "\f0dd"; 447 | $fa-var-sort-numeric-asc: "\f162"; 448 | $fa-var-sort-numeric-desc: "\f163"; 449 | $fa-var-sort-up: "\f0de"; 450 | $fa-var-soundcloud: "\f1be"; 451 | $fa-var-space-shuttle: "\f197"; 452 | $fa-var-spinner: "\f110"; 453 | $fa-var-spoon: "\f1b1"; 454 | $fa-var-spotify: "\f1bc"; 455 | $fa-var-square: "\f0c8"; 456 | $fa-var-square-o: "\f096"; 457 | $fa-var-stack-exchange: "\f18d"; 458 | $fa-var-stack-overflow: "\f16c"; 459 | $fa-var-star: "\f005"; 460 | $fa-var-star-half: "\f089"; 461 | $fa-var-star-half-empty: "\f123"; 462 | $fa-var-star-half-full: "\f123"; 463 | $fa-var-star-half-o: "\f123"; 464 | $fa-var-star-o: "\f006"; 465 | $fa-var-steam: "\f1b6"; 466 | $fa-var-steam-square: "\f1b7"; 467 | $fa-var-step-backward: "\f048"; 468 | $fa-var-step-forward: "\f051"; 469 | $fa-var-stethoscope: "\f0f1"; 470 | $fa-var-stop: "\f04d"; 471 | $fa-var-strikethrough: "\f0cc"; 472 | $fa-var-stumbleupon: "\f1a4"; 473 | $fa-var-stumbleupon-circle: "\f1a3"; 474 | $fa-var-subscript: "\f12c"; 475 | $fa-var-suitcase: "\f0f2"; 476 | $fa-var-sun-o: "\f185"; 477 | $fa-var-superscript: "\f12b"; 478 | $fa-var-support: "\f1cd"; 479 | $fa-var-table: "\f0ce"; 480 | $fa-var-tablet: "\f10a"; 481 | $fa-var-tachometer: "\f0e4"; 482 | $fa-var-tag: "\f02b"; 483 | $fa-var-tags: "\f02c"; 484 | $fa-var-tasks: "\f0ae"; 485 | $fa-var-taxi: "\f1ba"; 486 | $fa-var-tencent-weibo: "\f1d5"; 487 | $fa-var-terminal: "\f120"; 488 | $fa-var-text-height: "\f034"; 489 | $fa-var-text-width: "\f035"; 490 | $fa-var-th: "\f00a"; 491 | $fa-var-th-large: "\f009"; 492 | $fa-var-th-list: "\f00b"; 493 | $fa-var-thumb-tack: "\f08d"; 494 | $fa-var-thumbs-down: "\f165"; 495 | $fa-var-thumbs-o-down: "\f088"; 496 | $fa-var-thumbs-o-up: "\f087"; 497 | $fa-var-thumbs-up: "\f164"; 498 | $fa-var-ticket: "\f145"; 499 | $fa-var-times: "\f00d"; 500 | $fa-var-times-circle: "\f057"; 501 | $fa-var-times-circle-o: "\f05c"; 502 | $fa-var-tint: "\f043"; 503 | $fa-var-toggle-down: "\f150"; 504 | $fa-var-toggle-left: "\f191"; 505 | $fa-var-toggle-off: "\f204"; 506 | $fa-var-toggle-on: "\f205"; 507 | $fa-var-toggle-right: "\f152"; 508 | $fa-var-toggle-up: "\f151"; 509 | $fa-var-trash: "\f1f8"; 510 | $fa-var-trash-o: "\f014"; 511 | $fa-var-tree: "\f1bb"; 512 | $fa-var-trello: "\f181"; 513 | $fa-var-trophy: "\f091"; 514 | $fa-var-truck: "\f0d1"; 515 | $fa-var-try: "\f195"; 516 | $fa-var-tty: "\f1e4"; 517 | $fa-var-tumblr: "\f173"; 518 | $fa-var-tumblr-square: "\f174"; 519 | $fa-var-turkish-lira: "\f195"; 520 | $fa-var-twitch: "\f1e8"; 521 | $fa-var-twitter: "\f099"; 522 | $fa-var-twitter-square: "\f081"; 523 | $fa-var-umbrella: "\f0e9"; 524 | $fa-var-underline: "\f0cd"; 525 | $fa-var-undo: "\f0e2"; 526 | $fa-var-university: "\f19c"; 527 | $fa-var-unlink: "\f127"; 528 | $fa-var-unlock: "\f09c"; 529 | $fa-var-unlock-alt: "\f13e"; 530 | $fa-var-unsorted: "\f0dc"; 531 | $fa-var-upload: "\f093"; 532 | $fa-var-usd: "\f155"; 533 | $fa-var-user: "\f007"; 534 | $fa-var-user-md: "\f0f0"; 535 | $fa-var-users: "\f0c0"; 536 | $fa-var-video-camera: "\f03d"; 537 | $fa-var-vimeo-square: "\f194"; 538 | $fa-var-vine: "\f1ca"; 539 | $fa-var-vk: "\f189"; 540 | $fa-var-volume-down: "\f027"; 541 | $fa-var-volume-off: "\f026"; 542 | $fa-var-volume-up: "\f028"; 543 | $fa-var-warning: "\f071"; 544 | $fa-var-wechat: "\f1d7"; 545 | $fa-var-weibo: "\f18a"; 546 | $fa-var-weixin: "\f1d7"; 547 | $fa-var-wheelchair: "\f193"; 548 | $fa-var-wifi: "\f1eb"; 549 | $fa-var-windows: "\f17a"; 550 | $fa-var-won: "\f159"; 551 | $fa-var-wordpress: "\f19a"; 552 | $fa-var-wrench: "\f0ad"; 553 | $fa-var-xing: "\f168"; 554 | $fa-var-xing-square: "\f169"; 555 | $fa-var-yahoo: "\f19e"; 556 | $fa-var-yelp: "\f1e9"; 557 | $fa-var-yen: "\f157"; 558 | $fa-var-youtube: "\f167"; 559 | $fa-var-youtube-play: "\f16a"; 560 | $fa-var-youtube-square: "\f166"; 561 | 562 | -------------------------------------------------------------------------------- /lib/static/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "spinning"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | -------------------------------------------------------------------------------- /lib/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lib/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lib/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lib/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /lib/static/js/plugins/flot/excanvas.min.js: -------------------------------------------------------------------------------- 1 | if(!document.createElement("canvas").getContext){(function(){var z=Math;var K=z.round;var J=z.sin;var U=z.cos;var b=z.abs;var k=z.sqrt;var D=10;var F=D/2;function T(){return this.context_||(this.context_=new W(this))}var O=Array.prototype.slice;function G(i,j,m){var Z=O.call(arguments,2);return function(){return i.apply(j,Z.concat(O.call(arguments)))}}function AD(Z){return String(Z).replace(/&/g,"&").replace(/"/g,""")}function r(i){if(!i.namespaces.g_vml_){i.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML")}if(!i.namespaces.g_o_){i.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML")}if(!i.styleSheets.ex_canvas_){var Z=i.createStyleSheet();Z.owningElement.id="ex_canvas_";Z.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}r(document);var E={init:function(Z){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var i=Z||document;i.createElement("canvas");i.attachEvent("onreadystatechange",G(this.init_,this,i))}},init_:function(m){var j=m.getElementsByTagName("canvas");for(var Z=0;Z1){j--}if(6*j<1){return i+(Z-i)*6*j}else{if(2*j<1){return Z}else{if(3*j<2){return i+(Z-i)*(2/3-j)*6}else{return i}}}}function Y(Z){var AE,p=1;Z=String(Z);if(Z.charAt(0)=="#"){AE=Z}else{if(/^rgb/.test(Z)){var m=g(Z);var AE="#",AF;for(var j=0;j<3;j++){if(m[j].indexOf("%")!=-1){AF=Math.floor(C(m[j])*255)}else{AF=Number(m[j])}AE+=I[N(AF,0,255)]}p=m[3]}else{if(/^hsl/.test(Z)){var m=g(Z);AE=c(m);p=m[3]}else{AE=B[Z]||Z}}}return{color:AE,alpha:p}}var L={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var f={};function X(Z){if(f[Z]){return f[Z]}var m=document.createElement("div");var j=m.style;try{j.font=Z}catch(i){}return f[Z]={style:j.fontStyle||L.style,variant:j.fontVariant||L.variant,weight:j.fontWeight||L.weight,size:j.fontSize||L.size,family:j.fontFamily||L.family}}function P(j,i){var Z={};for(var AF in j){Z[AF]=j[AF]}var AE=parseFloat(i.currentStyle.fontSize),m=parseFloat(j.size);if(typeof j.size=="number"){Z.size=j.size}else{if(j.size.indexOf("px")!=-1){Z.size=m}else{if(j.size.indexOf("em")!=-1){Z.size=AE*m}else{if(j.size.indexOf("%")!=-1){Z.size=(AE/100)*m}else{if(j.size.indexOf("pt")!=-1){Z.size=m/0.75}else{Z.size=AE}}}}}Z.size*=0.981;return Z}function AA(Z){return Z.style+" "+Z.variant+" "+Z.weight+" "+Z.size+"px "+Z.family}function t(Z){switch(Z){case"butt":return"flat";case"round":return"round";case"square":default:return"square"}}function W(i){this.m_=V();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=D*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var Z=i.ownerDocument.createElement("div");Z.style.width=i.clientWidth+"px";Z.style.height=i.clientHeight+"px";Z.style.overflow="hidden";Z.style.position="absolute";i.appendChild(Z);this.element_=Z;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var M=W.prototype;M.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};M.beginPath=function(){this.currentPath_=[]};M.moveTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"moveTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.lineTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"lineTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.bezierCurveTo=function(j,i,AI,AH,AG,AE){var Z=this.getCoords_(AG,AE);var AF=this.getCoords_(j,i);var m=this.getCoords_(AI,AH);e(this,AF,m,Z)};function e(Z,m,j,i){Z.currentPath_.push({type:"bezierCurveTo",cp1x:m.x,cp1y:m.y,cp2x:j.x,cp2y:j.y,x:i.x,y:i.y});Z.currentX_=i.x;Z.currentY_=i.y}M.quadraticCurveTo=function(AG,j,i,Z){var AF=this.getCoords_(AG,j);var AE=this.getCoords_(i,Z);var AH={x:this.currentX_+2/3*(AF.x-this.currentX_),y:this.currentY_+2/3*(AF.y-this.currentY_)};var m={x:AH.x+(AE.x-this.currentX_)/3,y:AH.y+(AE.y-this.currentY_)/3};e(this,AH,m,AE)};M.arc=function(AJ,AH,AI,AE,i,j){AI*=D;var AN=j?"at":"wa";var AK=AJ+U(AE)*AI-F;var AM=AH+J(AE)*AI-F;var Z=AJ+U(i)*AI-F;var AL=AH+J(i)*AI-F;if(AK==Z&&!j){AK+=0.125}var m=this.getCoords_(AJ,AH);var AG=this.getCoords_(AK,AM);var AF=this.getCoords_(Z,AL);this.currentPath_.push({type:AN,x:m.x,y:m.y,radius:AI,xStart:AG.x,yStart:AG.y,xEnd:AF.x,yEnd:AF.y})};M.rect=function(j,i,Z,m){this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath()};M.strokeRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.stroke();this.currentPath_=p};M.fillRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.fill();this.currentPath_=p};M.createLinearGradient=function(i,m,Z,j){var p=new v("gradient");p.x0_=i;p.y0_=m;p.x1_=Z;p.y1_=j;return p};M.createRadialGradient=function(m,AE,j,i,p,Z){var AF=new v("gradientradial");AF.x0_=m;AF.y0_=AE;AF.r0_=j;AF.x1_=i;AF.y1_=p;AF.r1_=Z;return AF};M.drawImage=function(AO,j){var AH,AF,AJ,AV,AM,AK,AQ,AX;var AI=AO.runtimeStyle.width;var AN=AO.runtimeStyle.height;AO.runtimeStyle.width="auto";AO.runtimeStyle.height="auto";var AG=AO.width;var AT=AO.height;AO.runtimeStyle.width=AI;AO.runtimeStyle.height=AN;if(arguments.length==3){AH=arguments[1];AF=arguments[2];AM=AK=0;AQ=AJ=AG;AX=AV=AT}else{if(arguments.length==5){AH=arguments[1];AF=arguments[2];AJ=arguments[3];AV=arguments[4];AM=AK=0;AQ=AG;AX=AT}else{if(arguments.length==9){AM=arguments[1];AK=arguments[2];AQ=arguments[3];AX=arguments[4];AH=arguments[5];AF=arguments[6];AJ=arguments[7];AV=arguments[8]}else{throw Error("Invalid number of arguments")}}}var AW=this.getCoords_(AH,AF);var m=AQ/2;var i=AX/2;var AU=[];var Z=10;var AE=10;AU.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",AU.join(""))};M.stroke=function(AM){var m=10;var AN=10;var AE=5000;var AG={x:null,y:null};var AL={x:null,y:null};for(var AH=0;AHAL.x){AL.x=Z.x}if(AG.y==null||Z.yAL.y){AL.y=Z.y}}}AK.push(' ">');if(!AM){R(this,AK)}else{a(this,AK,AG,AL)}AK.push("");this.element_.insertAdjacentHTML("beforeEnd",AK.join(""))}};function R(j,AE){var i=Y(j.strokeStyle);var m=i.color;var p=i.alpha*j.globalAlpha;var Z=j.lineScale_*j.lineWidth;if(Z<1){p*=Z}AE.push("')}function a(AO,AG,Ah,AP){var AH=AO.fillStyle;var AY=AO.arcScaleX_;var AX=AO.arcScaleY_;var Z=AP.x-Ah.x;var m=AP.y-Ah.y;if(AH instanceof v){var AL=0;var Ac={x:0,y:0};var AU=0;var AK=1;if(AH.type_=="gradient"){var AJ=AH.x0_/AY;var j=AH.y0_/AX;var AI=AH.x1_/AY;var Aj=AH.y1_/AX;var Ag=AO.getCoords_(AJ,j);var Af=AO.getCoords_(AI,Aj);var AE=Af.x-Ag.x;var p=Af.y-Ag.y;AL=Math.atan2(AE,p)*180/Math.PI;if(AL<0){AL+=360}if(AL<0.000001){AL=0}}else{var Ag=AO.getCoords_(AH.x0_,AH.y0_);Ac={x:(Ag.x-Ah.x)/Z,y:(Ag.y-Ah.y)/m};Z/=AY*D;m/=AX*D;var Aa=z.max(Z,m);AU=2*AH.r0_/Aa;AK=2*AH.r1_/Aa-AU}var AS=AH.colors_;AS.sort(function(Ak,i){return Ak.offset-i.offset});var AN=AS.length;var AR=AS[0].color;var AQ=AS[AN-1].color;var AW=AS[0].alpha*AO.globalAlpha;var AV=AS[AN-1].alpha*AO.globalAlpha;var Ab=[];for(var Ae=0;Ae')}else{if(AH instanceof u){if(Z&&m){var AF=-Ah.x;var AZ=-Ah.y;AG.push("')}}else{var Ai=Y(AO.fillStyle);var AT=Ai.color;var Ad=Ai.alpha*AO.globalAlpha;AG.push('')}}}M.fill=function(){this.stroke(true)};M.closePath=function(){this.currentPath_.push({type:"close"})};M.getCoords_=function(j,i){var Z=this.m_;return{x:D*(j*Z[0][0]+i*Z[1][0]+Z[2][0])-F,y:D*(j*Z[0][1]+i*Z[1][1]+Z[2][1])-F}};M.save=function(){var Z={};Q(this,Z);this.aStack_.push(Z);this.mStack_.push(this.m_);this.m_=d(V(),this.m_)};M.restore=function(){if(this.aStack_.length){Q(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function H(Z){return isFinite(Z[0][0])&&isFinite(Z[0][1])&&isFinite(Z[1][0])&&isFinite(Z[1][1])&&isFinite(Z[2][0])&&isFinite(Z[2][1])}function y(i,Z,j){if(!H(Z)){return }i.m_=Z;if(j){var p=Z[0][0]*Z[1][1]-Z[0][1]*Z[1][0];i.lineScale_=k(b(p))}}M.translate=function(j,i){var Z=[[1,0,0],[0,1,0],[j,i,1]];y(this,d(Z,this.m_),false)};M.rotate=function(i){var m=U(i);var j=J(i);var Z=[[m,j,0],[-j,m,0],[0,0,1]];y(this,d(Z,this.m_),false)};M.scale=function(j,i){this.arcScaleX_*=j;this.arcScaleY_*=i;var Z=[[j,0,0],[0,i,0],[0,0,1]];y(this,d(Z,this.m_),true)};M.transform=function(p,m,AF,AE,i,Z){var j=[[p,m,0],[AF,AE,0],[i,Z,1]];y(this,d(j,this.m_),true)};M.setTransform=function(AE,p,AG,AF,j,i){var Z=[[AE,p,0],[AG,AF,0],[j,i,1]];y(this,Z,true)};M.drawText_=function(AK,AI,AH,AN,AG){var AM=this.m_,AQ=1000,i=0,AP=AQ,AF={x:0,y:0},AE=[];var Z=P(X(this.font),this.element_);var j=AA(Z);var AR=this.element_.currentStyle;var p=this.textAlign.toLowerCase();switch(p){case"left":case"center":case"right":break;case"end":p=AR.direction=="ltr"?"right":"left";break;case"start":p=AR.direction=="rtl"?"right":"left";break;default:p="left"}switch(this.textBaseline){case"hanging":case"top":AF.y=Z.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":AF.y=-Z.size/2.25;break}switch(p){case"right":i=AQ;AP=0.05;break;case"center":i=AP=AQ/2;break}var AO=this.getCoords_(AI+AF.x,AH+AF.y);AE.push('');if(AG){R(this,AE)}else{a(this,AE,{x:-i,y:0},{x:AP,y:Z.size})}var AL=AM[0][0].toFixed(3)+","+AM[1][0].toFixed(3)+","+AM[0][1].toFixed(3)+","+AM[1][1].toFixed(3)+",0,0";var AJ=K(AO.x/D)+","+K(AO.y/D);AE.push('','','');this.element_.insertAdjacentHTML("beforeEnd",AE.join(""))};M.fillText=function(j,Z,m,i){this.drawText_(j,Z,m,i,false)};M.strokeText=function(j,Z,m,i){this.drawText_(j,Z,m,i,true)};M.measureText=function(j){if(!this.textMeasureEl_){var Z='';this.element_.insertAdjacentHTML("beforeEnd",Z);this.textMeasureEl_=this.element_.lastChild}var i=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(i.createTextNode(j));return{width:this.textMeasureEl_.offsetWidth}};M.clip=function(){};M.arcTo=function(){};M.createPattern=function(i,Z){return new u(i,Z)};function v(Z){this.type_=Z;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}v.prototype.addColorStop=function(i,Z){Z=Y(Z);this.colors_.push({offset:i,color:Z.color,alpha:Z.alpha})};function u(i,Z){q(i);switch(Z){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=Z;break;default:n("SYNTAX_ERR")}this.src_=i.src;this.width_=i.width;this.height_=i.height}function n(Z){throw new o(Z)}function q(Z){if(!Z||Z.nodeType!=1||Z.tagName!="IMG"){n("TYPE_MISMATCH_ERR")}if(Z.readyState!="complete"){n("INVALID_STATE_ERR")}}function o(Z){this.code=this[Z];this.message=Z+": DOM Exception "+this.code}var x=o.prototype=new Error;x.INDEX_SIZE_ERR=1;x.DOMSTRING_SIZE_ERR=2;x.HIERARCHY_REQUEST_ERR=3;x.WRONG_DOCUMENT_ERR=4;x.INVALID_CHARACTER_ERR=5;x.NO_DATA_ALLOWED_ERR=6;x.NO_MODIFICATION_ALLOWED_ERR=7;x.NOT_FOUND_ERR=8;x.NOT_SUPPORTED_ERR=9;x.INUSE_ATTRIBUTE_ERR=10;x.INVALID_STATE_ERR=11;x.SYNTAX_ERR=12;x.INVALID_MODIFICATION_ERR=13;x.NAMESPACE_ERR=14;x.INVALID_ACCESS_ERR=15;x.VALIDATION_ERR=16;x.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=E;CanvasRenderingContext2D=W;CanvasGradient=v;CanvasPattern=u;DOMException=o})()}; -------------------------------------------------------------------------------- /lib/static/js/plugins/flot/jquery.flot.pie.js: -------------------------------------------------------------------------------- 1 | /* 2 | Flot plugin for rendering pie charts. The plugin assumes the data is 3 | coming is as a single data value for each series, and each of those 4 | values is a positive value or zero (negative numbers don't make 5 | any sense and will cause strange effects). The data values do 6 | NOT need to be passed in as percentage values because it 7 | internally calculates the total and percentages. 8 | 9 | * Created by Brian Medendorp, June 2009 10 | * Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars 11 | 12 | * Changes: 13 | 2009-10-22: lineJoin set to round 14 | 2009-10-23: IE full circle fix, donut 15 | 2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera 16 | 2009-11-17: Added IE hover capability submitted by Anthony Aragues 17 | 2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well) 18 | 19 | 20 | Available options are: 21 | series: { 22 | pie: { 23 | show: true/false 24 | radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' 25 | innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect 26 | startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result 27 | tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) 28 | offset: { 29 | top: integer value to move the pie up or down 30 | left: integer value to move the pie left or right, or 'auto' 31 | }, 32 | stroke: { 33 | color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF') 34 | width: integer pixel width of the stroke 35 | }, 36 | label: { 37 | show: true/false, or 'auto' 38 | formatter: a user-defined function that modifies the text/style of the label text 39 | radius: 0-1 for percentage of fullsize, or a specified pixel length 40 | background: { 41 | color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000') 42 | opacity: 0-1 43 | }, 44 | threshold: 0-1 for the percentage value at which to hide labels (if they're too small) 45 | }, 46 | combine: { 47 | threshold: 0-1 for the percentage value at which to combine slices (if they're too small) 48 | color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined 49 | label: any text value of what the combined slice should be labeled 50 | } 51 | highlight: { 52 | opacity: 0-1 53 | } 54 | } 55 | } 56 | 57 | More detail and specific examples can be found in the included HTML file. 58 | 59 | */ 60 | 61 | (function ($) 62 | { 63 | function init(plot) // this is the "body" of the plugin 64 | { 65 | var canvas = null; 66 | var target = null; 67 | var maxRadius = null; 68 | var centerLeft = null; 69 | var centerTop = null; 70 | var total = 0; 71 | var redraw = true; 72 | var redrawAttempts = 10; 73 | var shrink = 0.95; 74 | var legendWidth = 0; 75 | var processed = false; 76 | var raw = false; 77 | 78 | // interactive variables 79 | var highlights = []; 80 | 81 | // add hook to determine if pie plugin in enabled, and then perform necessary operations 82 | plot.hooks.processOptions.push(checkPieEnabled); 83 | plot.hooks.bindEvents.push(bindEvents); 84 | 85 | // check to see if the pie plugin is enabled 86 | function checkPieEnabled(plot, options) 87 | { 88 | if (options.series.pie.show) 89 | { 90 | //disable grid 91 | options.grid.show = false; 92 | 93 | // set labels.show 94 | if (options.series.pie.label.show=='auto') 95 | if (options.legend.show) 96 | options.series.pie.label.show = false; 97 | else 98 | options.series.pie.label.show = true; 99 | 100 | // set radius 101 | if (options.series.pie.radius=='auto') 102 | if (options.series.pie.label.show) 103 | options.series.pie.radius = 3/4; 104 | else 105 | options.series.pie.radius = 1; 106 | 107 | // ensure sane tilt 108 | if (options.series.pie.tilt>1) 109 | options.series.pie.tilt=1; 110 | if (options.series.pie.tilt<0) 111 | options.series.pie.tilt=0; 112 | 113 | // add processData hook to do transformations on the data 114 | plot.hooks.processDatapoints.push(processDatapoints); 115 | plot.hooks.drawOverlay.push(drawOverlay); 116 | 117 | // add draw hook 118 | plot.hooks.draw.push(draw); 119 | } 120 | } 121 | 122 | // bind hoverable events 123 | function bindEvents(plot, eventHolder) 124 | { 125 | var options = plot.getOptions(); 126 | 127 | if (options.series.pie.show && options.grid.hoverable) 128 | eventHolder.unbind('mousemove').mousemove(onMouseMove); 129 | 130 | if (options.series.pie.show && options.grid.clickable) 131 | eventHolder.unbind('click').click(onClick); 132 | } 133 | 134 | 135 | // debugging function that prints out an object 136 | function alertObject(obj) 137 | { 138 | var msg = ''; 139 | function traverse(obj, depth) 140 | { 141 | if (!depth) 142 | depth = 0; 143 | for (var i = 0; i < obj.length; ++i) 144 | { 145 | for (var j=0; jcanvas.width-maxRadius) 207 | centerLeft = canvas.width-maxRadius; 208 | } 209 | 210 | function fixData(data) 211 | { 212 | for (var i = 0; i < data.length; ++i) 213 | { 214 | if (typeof(data[i].data)=='number') 215 | data[i].data = [[1,data[i].data]]; 216 | else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined') 217 | { 218 | if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined') 219 | data[i].label = data[i].data.label; // fix weirdness coming from flot 220 | data[i].data = [[1,0]]; 221 | 222 | } 223 | } 224 | return data; 225 | } 226 | 227 | function combine(data) 228 | { 229 | data = fixData(data); 230 | calcTotal(data); 231 | var combined = 0; 232 | var numCombined = 0; 233 | var color = options.series.pie.combine.color; 234 | 235 | var newdata = []; 236 | for (var i = 0; i < data.length; ++i) 237 | { 238 | // make sure its a number 239 | data[i].data[0][1] = parseFloat(data[i].data[0][1]); 240 | if (!data[i].data[0][1]) 241 | data[i].data[0][1] = 0; 242 | 243 | if (data[i].data[0][1]/total<=options.series.pie.combine.threshold) 244 | { 245 | combined += data[i].data[0][1]; 246 | numCombined++; 247 | if (!color) 248 | color = data[i].color; 249 | } 250 | else 251 | { 252 | newdata.push({ 253 | data: [[1,data[i].data[0][1]]], 254 | color: data[i].color, 255 | label: data[i].label, 256 | angle: (data[i].data[0][1]*(Math.PI*2))/total, 257 | percent: (data[i].data[0][1]/total*100) 258 | }); 259 | } 260 | } 261 | if (numCombined>0) 262 | newdata.push({ 263 | data: [[1,combined]], 264 | color: color, 265 | label: options.series.pie.combine.label, 266 | angle: (combined*(Math.PI*2))/total, 267 | percent: (combined/total*100) 268 | }); 269 | return newdata; 270 | } 271 | 272 | function draw(plot, newCtx) 273 | { 274 | if (!target) return; // if no series were passed 275 | ctx = newCtx; 276 | 277 | setupPie(); 278 | var slices = plot.getData(); 279 | 280 | var attempts = 0; 281 | while (redraw && attempts0) 285 | maxRadius *= shrink; 286 | attempts += 1; 287 | clear(); 288 | if (options.series.pie.tilt<=0.8) 289 | drawShadow(); 290 | drawPie(); 291 | } 292 | if (attempts >= redrawAttempts) { 293 | clear(); 294 | target.prepend('
Could not draw pie with labels contained inside canvas
'); 295 | } 296 | 297 | if ( plot.setSeries && plot.insertLegend ) 298 | { 299 | plot.setSeries(slices); 300 | plot.insertLegend(); 301 | } 302 | 303 | // we're actually done at this point, just defining internal functions at this point 304 | 305 | function clear() 306 | { 307 | ctx.clearRect(0,0,canvas.width,canvas.height); 308 | target.children().filter('.pieLabel, .pieLabelBackground').remove(); 309 | } 310 | 311 | function drawShadow() 312 | { 313 | var shadowLeft = 5; 314 | var shadowTop = 15; 315 | var edge = 10; 316 | var alpha = 0.02; 317 | 318 | // set radius 319 | if (options.series.pie.radius>1) 320 | var radius = options.series.pie.radius; 321 | else 322 | var radius = maxRadius * options.series.pie.radius; 323 | 324 | if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge) 325 | return; // shadow would be outside canvas, so don't draw it 326 | 327 | ctx.save(); 328 | ctx.translate(shadowLeft,shadowTop); 329 | ctx.globalAlpha = alpha; 330 | ctx.fillStyle = '#000'; 331 | 332 | // center and rotate to starting position 333 | ctx.translate(centerLeft,centerTop); 334 | ctx.scale(1, options.series.pie.tilt); 335 | 336 | //radius -= edge; 337 | for (var i=1; i<=edge; i++) 338 | { 339 | ctx.beginPath(); 340 | ctx.arc(0,0,radius,0,Math.PI*2,false); 341 | ctx.fill(); 342 | radius -= i; 343 | } 344 | 345 | ctx.restore(); 346 | } 347 | 348 | function drawPie() 349 | { 350 | startAngle = Math.PI*options.series.pie.startAngle; 351 | 352 | // set radius 353 | if (options.series.pie.radius>1) 354 | var radius = options.series.pie.radius; 355 | else 356 | var radius = maxRadius * options.series.pie.radius; 357 | 358 | // center and rotate to starting position 359 | ctx.save(); 360 | ctx.translate(centerLeft,centerTop); 361 | ctx.scale(1, options.series.pie.tilt); 362 | //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera 363 | 364 | // draw slices 365 | ctx.save(); 366 | var currentAngle = startAngle; 367 | for (var i = 0; i < slices.length; ++i) 368 | { 369 | slices[i].startAngle = currentAngle; 370 | drawSlice(slices[i].angle, slices[i].color, true); 371 | } 372 | ctx.restore(); 373 | 374 | // draw slice outlines 375 | ctx.save(); 376 | ctx.lineWidth = options.series.pie.stroke.width; 377 | currentAngle = startAngle; 378 | for (var i = 0; i < slices.length; ++i) 379 | drawSlice(slices[i].angle, options.series.pie.stroke.color, false); 380 | ctx.restore(); 381 | 382 | // draw donut hole 383 | drawDonutHole(ctx); 384 | 385 | // draw labels 386 | if (options.series.pie.label.show) 387 | drawLabels(); 388 | 389 | // restore to original state 390 | ctx.restore(); 391 | 392 | function drawSlice(angle, color, fill) 393 | { 394 | if (angle<=0) 395 | return; 396 | 397 | if (fill) 398 | ctx.fillStyle = color; 399 | else 400 | { 401 | ctx.strokeStyle = color; 402 | ctx.lineJoin = 'round'; 403 | } 404 | 405 | ctx.beginPath(); 406 | if (Math.abs(angle - Math.PI*2) > 0.000000001) 407 | ctx.moveTo(0,0); // Center of the pie 408 | else if ($.browser.msie) 409 | angle -= 0.0001; 410 | //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera 411 | ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false); 412 | ctx.closePath(); 413 | //ctx.rotate(angle); // This doesn't work properly in Opera 414 | currentAngle += angle; 415 | 416 | if (fill) 417 | ctx.fill(); 418 | else 419 | ctx.stroke(); 420 | } 421 | 422 | function drawLabels() 423 | { 424 | var currentAngle = startAngle; 425 | 426 | // set radius 427 | if (options.series.pie.label.radius>1) 428 | var radius = options.series.pie.label.radius; 429 | else 430 | var radius = maxRadius * options.series.pie.label.radius; 431 | 432 | for (var i = 0; i < slices.length; ++i) 433 | { 434 | if (slices[i].percent >= options.series.pie.label.threshold*100) 435 | drawLabel(slices[i], currentAngle, i); 436 | currentAngle += slices[i].angle; 437 | } 438 | 439 | function drawLabel(slice, startAngle, index) 440 | { 441 | if (slice.data[0][1]==0) 442 | return; 443 | 444 | // format label text 445 | var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; 446 | if (lf) 447 | text = lf(slice.label, slice); 448 | else 449 | text = slice.label; 450 | if (plf) 451 | text = plf(text, slice); 452 | 453 | var halfAngle = ((startAngle+slice.angle) + startAngle)/2; 454 | var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); 455 | var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; 456 | 457 | var html = '' + text + ""; 458 | target.append(html); 459 | var label = target.children('#pieLabel'+index); 460 | var labelTop = (y - label.height()/2); 461 | var labelLeft = (x - label.width()/2); 462 | label.css('top', labelTop); 463 | label.css('left', labelLeft); 464 | 465 | // check to make sure that the label is not outside the canvas 466 | if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0) 467 | redraw = true; 468 | 469 | if (options.series.pie.label.background.opacity != 0) { 470 | // put in the transparent background separately to avoid blended labels and label boxes 471 | var c = options.series.pie.label.background.color; 472 | if (c == null) { 473 | c = slice.color; 474 | } 475 | var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;'; 476 | $('
').insertBefore(label).css('opacity', options.series.pie.label.background.opacity); 477 | } 478 | } // end individual label function 479 | } // end drawLabels function 480 | } // end drawPie function 481 | } // end draw function 482 | 483 | // Placed here because it needs to be accessed from multiple locations 484 | function drawDonutHole(layer) 485 | { 486 | // draw donut hole 487 | if(options.series.pie.innerRadius > 0) 488 | { 489 | // subtract the center 490 | layer.save(); 491 | innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; 492 | layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color 493 | layer.beginPath(); 494 | layer.fillStyle = options.series.pie.stroke.color; 495 | layer.arc(0,0,innerRadius,0,Math.PI*2,false); 496 | layer.fill(); 497 | layer.closePath(); 498 | layer.restore(); 499 | 500 | // add inner stroke 501 | layer.save(); 502 | layer.beginPath(); 503 | layer.strokeStyle = options.series.pie.stroke.color; 504 | layer.arc(0,0,innerRadius,0,Math.PI*2,false); 505 | layer.stroke(); 506 | layer.closePath(); 507 | layer.restore(); 508 | // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. 509 | } 510 | } 511 | 512 | //-- Additional Interactive related functions -- 513 | 514 | function isPointInPoly(poly, pt) 515 | { 516 | for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) 517 | ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) 518 | && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) 519 | && (c = !c); 520 | return c; 521 | } 522 | 523 | function findNearbySlice(mouseX, mouseY) 524 | { 525 | var slices = plot.getData(), 526 | options = plot.getOptions(), 527 | radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; 528 | 529 | for (var i = 0; i < slices.length; ++i) 530 | { 531 | var s = slices[i]; 532 | 533 | if(s.pie.show) 534 | { 535 | ctx.save(); 536 | ctx.beginPath(); 537 | ctx.moveTo(0,0); // Center of the pie 538 | //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. 539 | ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false); 540 | ctx.closePath(); 541 | x = mouseX-centerLeft; 542 | y = mouseY-centerTop; 543 | if(ctx.isPointInPath) 544 | { 545 | if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop)) 546 | { 547 | //alert('found slice!'); 548 | ctx.restore(); 549 | return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; 550 | } 551 | } 552 | else 553 | { 554 | // excanvas for IE doesn;t support isPointInPath, this is a workaround. 555 | p1X = (radius * Math.cos(s.startAngle)); 556 | p1Y = (radius * Math.sin(s.startAngle)); 557 | p2X = (radius * Math.cos(s.startAngle+(s.angle/4))); 558 | p2Y = (radius * Math.sin(s.startAngle+(s.angle/4))); 559 | p3X = (radius * Math.cos(s.startAngle+(s.angle/2))); 560 | p3Y = (radius * Math.sin(s.startAngle+(s.angle/2))); 561 | p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5))); 562 | p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5))); 563 | p5X = (radius * Math.cos(s.startAngle+s.angle)); 564 | p5Y = (radius * Math.sin(s.startAngle+s.angle)); 565 | arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]]; 566 | arrPoint = [x,y]; 567 | // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? 568 | if(isPointInPoly(arrPoly, arrPoint)) 569 | { 570 | ctx.restore(); 571 | return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; 572 | } 573 | } 574 | ctx.restore(); 575 | } 576 | } 577 | 578 | return null; 579 | } 580 | 581 | function onMouseMove(e) 582 | { 583 | triggerClickHoverEvent('plothover', e); 584 | } 585 | 586 | function onClick(e) 587 | { 588 | triggerClickHoverEvent('plotclick', e); 589 | } 590 | 591 | // trigger click or hover event (they send the same parameters so we share their code) 592 | function triggerClickHoverEvent(eventname, e) 593 | { 594 | var offset = plot.offset(), 595 | canvasX = parseInt(e.pageX - offset.left), 596 | canvasY = parseInt(e.pageY - offset.top), 597 | item = findNearbySlice(canvasX, canvasY); 598 | 599 | if (options.grid.autoHighlight) 600 | { 601 | // clear auto-highlights 602 | for (var i = 0; i < highlights.length; ++i) 603 | { 604 | var h = highlights[i]; 605 | if (h.auto == eventname && !(item && h.series == item.series)) 606 | unhighlight(h.series); 607 | } 608 | } 609 | 610 | // highlight the slice 611 | if (item) 612 | highlight(item.series, eventname); 613 | 614 | // trigger any hover bind events 615 | var pos = { pageX: e.pageX, pageY: e.pageY }; 616 | target.trigger(eventname, [ pos, item ]); 617 | } 618 | 619 | function highlight(s, auto) 620 | { 621 | if (typeof s == "number") 622 | s = series[s]; 623 | 624 | var i = indexOfHighlight(s); 625 | if (i == -1) 626 | { 627 | highlights.push({ series: s, auto: auto }); 628 | plot.triggerRedrawOverlay(); 629 | } 630 | else if (!auto) 631 | highlights[i].auto = false; 632 | } 633 | 634 | function unhighlight(s) 635 | { 636 | if (s == null) 637 | { 638 | highlights = []; 639 | plot.triggerRedrawOverlay(); 640 | } 641 | 642 | if (typeof s == "number") 643 | s = series[s]; 644 | 645 | var i = indexOfHighlight(s); 646 | if (i != -1) 647 | { 648 | highlights.splice(i, 1); 649 | plot.triggerRedrawOverlay(); 650 | } 651 | } 652 | 653 | function indexOfHighlight(s) 654 | { 655 | for (var i = 0; i < highlights.length; ++i) 656 | { 657 | var h = highlights[i]; 658 | if (h.series == s) 659 | return i; 660 | } 661 | return -1; 662 | } 663 | 664 | function drawOverlay(plot, octx) 665 | { 666 | //alert(options.series.pie.radius); 667 | var options = plot.getOptions(); 668 | //alert(options.series.pie.radius); 669 | 670 | var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; 671 | 672 | octx.save(); 673 | octx.translate(centerLeft, centerTop); 674 | octx.scale(1, options.series.pie.tilt); 675 | 676 | for (i = 0; i < highlights.length; ++i) 677 | drawHighlight(highlights[i].series); 678 | 679 | drawDonutHole(octx); 680 | 681 | octx.restore(); 682 | 683 | function drawHighlight(series) 684 | { 685 | if (series.angle < 0) return; 686 | 687 | //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); 688 | octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor 689 | 690 | octx.beginPath(); 691 | if (Math.abs(series.angle - Math.PI*2) > 0.000000001) 692 | octx.moveTo(0,0); // Center of the pie 693 | octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false); 694 | octx.closePath(); 695 | octx.fill(); 696 | } 697 | 698 | } 699 | 700 | } // end init (plugin body) 701 | 702 | // define pie specific options and their default values 703 | var options = { 704 | series: { 705 | pie: { 706 | show: false, 707 | radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) 708 | innerRadius:0, /* for donut */ 709 | startAngle: 3/2, 710 | tilt: 1, 711 | offset: { 712 | top: 0, 713 | left: 'auto' 714 | }, 715 | stroke: { 716 | color: '#FFF', 717 | width: 1 718 | }, 719 | label: { 720 | show: 'auto', 721 | formatter: function(label, slice){ 722 | return '
'+label+'
'+Math.round(slice.percent)+'%
'; 723 | }, // formatter function 724 | radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) 725 | background: { 726 | color: null, 727 | opacity: 0 728 | }, 729 | threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) 730 | }, 731 | combine: { 732 | threshold: -1, // percentage at which to combine little slices into one larger slice 733 | color: null, // color to give the new slice (auto-generated if null) 734 | label: 'Other' // label to give the new slice 735 | }, 736 | highlight: { 737 | //color: '#FFF', // will add this functionality once parseColor is available 738 | opacity: 0.5 739 | } 740 | } 741 | } 742 | }; 743 | 744 | $.plot.plugins.push({ 745 | init: init, 746 | options: options, 747 | name: "pie", 748 | version: "1.0" 749 | }); 750 | })(jQuery); 751 | -------------------------------------------------------------------------------- /lib/static/js/plugins/flot/jquery.flot.resize.js: -------------------------------------------------------------------------------- 1 | /* Flot plugin for automatically redrawing plots as the placeholder resizes. 2 | 3 | Copyright (c) 2007-2013 IOLA and Ole Laursen. 4 | Licensed under the MIT license. 5 | 6 | It works by listening for changes on the placeholder div (through the jQuery 7 | resize event plugin) - if the size changes, it will redraw the plot. 8 | 9 | There are no options. If you need to disable the plugin for some plots, you 10 | can just fix the size of their placeholders. 11 | 12 | */ 13 | 14 | /* Inline dependency: 15 | * jQuery resize event - v1.1 - 3/14/2010 16 | * http://benalman.com/projects/jquery-resize-plugin/ 17 | * 18 | * Copyright (c) 2010 "Cowboy" Ben Alman 19 | * Dual licensed under the MIT and GPL licenses. 20 | * http://benalman.com/about/license/ 21 | */ 22 | 23 | (function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this); 24 | 25 | (function ($) { 26 | var options = { }; // no options 27 | 28 | function init(plot) { 29 | function onResize() { 30 | var placeholder = plot.getPlaceholder(); 31 | 32 | // somebody might have hidden us and we can't plot 33 | // when we don't have the dimensions 34 | if (placeholder.width() == 0 || placeholder.height() == 0) 35 | return; 36 | 37 | plot.resize(); 38 | plot.setupGrid(); 39 | plot.draw(); 40 | } 41 | 42 | function bindEvents(plot, eventHolder) { 43 | plot.getPlaceholder().resize(onResize); 44 | } 45 | 46 | function shutdown(plot, eventHolder) { 47 | plot.getPlaceholder().unbind("resize", onResize); 48 | } 49 | 50 | plot.hooks.bindEvents.push(bindEvents); 51 | plot.hooks.shutdown.push(shutdown); 52 | } 53 | 54 | $.plot.plugins.push({ 55 | init: init, 56 | options: options, 57 | name: 'resize', 58 | version: '1.0' 59 | }); 60 | })(jQuery); -------------------------------------------------------------------------------- /lib/static/js/plugins/flot/jquery.flot.tooltip.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jquery.flot.tooltip 3 | * 4 | * description: easy-to-use tooltips for Flot charts 5 | * version: 0.6.2 6 | * author: Krzysztof Urbas @krzysu [myviews.pl] 7 | * website: https://github.com/krzysu/flot.tooltip 8 | * 9 | * build on 2013-09-30 10 | * released under MIT License, 2012 11 | */ 12 | (function(t){var o={tooltip:!1,tooltipOpts:{content:"%s | X: %x | Y: %y",xDateFormat:null,yDateFormat:null,shifts:{x:10,y:20},defaultTheme:!0,onHover:function(){}}},i=function(t){this.tipPosition={x:0,y:0},this.init(t)};i.prototype.init=function(o){function i(t){var o={};o.x=t.pageX,o.y=t.pageY,s.updateTooltipPosition(o)}function e(t,o,i){var e=s.getDomElement();if(i){var n;n=s.stringFormat(s.tooltipOptions.content,i),e.html(n),s.updateTooltipPosition({x:o.pageX,y:o.pageY}),e.css({left:s.tipPosition.x+s.tooltipOptions.shifts.x,top:s.tipPosition.y+s.tooltipOptions.shifts.y}).show(),"function"==typeof s.tooltipOptions.onHover&&s.tooltipOptions.onHover(i,e)}else e.hide().html("")}var s=this;o.hooks.bindEvents.push(function(o,n){s.plotOptions=o.getOptions(),s.plotOptions.tooltip!==!1&&void 0!==s.plotOptions.tooltip&&(s.tooltipOptions=s.plotOptions.tooltipOpts,s.getDomElement(),t(o.getPlaceholder()).bind("plothover",e),t(n).bind("mousemove",i))}),o.hooks.shutdown.push(function(o,s){t(o.getPlaceholder()).unbind("plothover",e),t(s).unbind("mousemove",i)})},i.prototype.getDomElement=function(){var o;return t("#flotTip").length>0?o=t("#flotTip"):(o=t("
").attr("id","flotTip"),o.appendTo("body").hide().css({position:"absolute"}),this.tooltipOptions.defaultTheme&&o.css({background:"#fff","z-index":"100",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111",display:"none","white-space":"nowrap"})),o},i.prototype.updateTooltipPosition=function(o){var i=t("#flotTip").outerWidth()+this.tooltipOptions.shifts.x,e=t("#flotTip").outerHeight()+this.tooltipOptions.shifts.y;o.x-t(window).scrollLeft()>t(window).innerWidth()-i&&(o.x-=i),o.y-t(window).scrollTop()>t(window).innerHeight()-e&&(o.y-=e),this.tipPosition.x=o.x,this.tipPosition.y=o.y},i.prototype.stringFormat=function(t,o){var i=/%p\.{0,1}(\d{0,})/,e=/%s/,s=/%x\.{0,1}(?:\d{0,})/,n=/%y\.{0,1}(?:\d{0,})/;return"function"==typeof t&&(t=t(o.series.label,o.series.data[o.dataIndex][0],o.series.data[o.dataIndex][1],o)),o.series.percent!==void 0&&(t=this.adjustValPrecision(i,t,o.series.percent)),o.series.label!==void 0&&(t=t.replace(e,o.series.label)),this.isTimeMode("xaxis",o)&&this.isXDateFormat(o)&&(t=t.replace(s,this.timestampToDate(o.series.data[o.dataIndex][0],this.tooltipOptions.xDateFormat))),this.isTimeMode("yaxis",o)&&this.isYDateFormat(o)&&(t=t.replace(n,this.timestampToDate(o.series.data[o.dataIndex][1],this.tooltipOptions.yDateFormat))),"number"==typeof o.series.data[o.dataIndex][0]&&(t=this.adjustValPrecision(s,t,o.series.data[o.dataIndex][0])),"number"==typeof o.series.data[o.dataIndex][1]&&(t=this.adjustValPrecision(n,t,o.series.data[o.dataIndex][1])),o.series.xaxis.tickFormatter!==void 0&&(t=t.replace(s,o.series.xaxis.tickFormatter(o.series.data[o.dataIndex][0],o.series.xaxis))),o.series.yaxis.tickFormatter!==void 0&&(t=t.replace(n,o.series.yaxis.tickFormatter(o.series.data[o.dataIndex][1],o.series.yaxis))),t},i.prototype.isTimeMode=function(t,o){return o.series[t].options.mode!==void 0&&"time"===o.series[t].options.mode},i.prototype.isXDateFormat=function(){return this.tooltipOptions.xDateFormat!==void 0&&null!==this.tooltipOptions.xDateFormat},i.prototype.isYDateFormat=function(){return this.tooltipOptions.yDateFormat!==void 0&&null!==this.tooltipOptions.yDateFormat},i.prototype.timestampToDate=function(o,i){var e=new Date(o);return t.plot.formatDate(e,i)},i.prototype.adjustValPrecision=function(t,o,i){var e,s=o.match(t);return null!==s&&""!==RegExp.$1&&(e=RegExp.$1,i=i.toFixed(e),o=o.replace(t,i)),o};var e=function(t){new i(t)};t.plot.plugins.push({init:e,options:o,name:"tooltip",version:"0.6.1"})})(jQuery); -------------------------------------------------------------------------------- /lib/static/js/plugins/morris/morris-data.js: -------------------------------------------------------------------------------- 1 | // Morris.js Charts sample data for SB Admin template 2 | 3 | $(function() { 4 | 5 | // Area Chart 6 | Morris.Area({ 7 | element: 'morris-area-chart', 8 | data: [{ 9 | period: '2010 Q1', 10 | iphone: 2666, 11 | ipad: null, 12 | itouch: 2647 13 | }, { 14 | period: '2010 Q2', 15 | iphone: 2778, 16 | ipad: 2294, 17 | itouch: 2441 18 | }, { 19 | period: '2010 Q3', 20 | iphone: 4912, 21 | ipad: 1969, 22 | itouch: 2501 23 | }, { 24 | period: '2010 Q4', 25 | iphone: 3767, 26 | ipad: 3597, 27 | itouch: 5689 28 | }, { 29 | period: '2011 Q1', 30 | iphone: 6810, 31 | ipad: 1914, 32 | itouch: 2293 33 | }, { 34 | period: '2011 Q2', 35 | iphone: 5670, 36 | ipad: 4293, 37 | itouch: 1881 38 | }, { 39 | period: '2011 Q3', 40 | iphone: 4820, 41 | ipad: 3795, 42 | itouch: 1588 43 | }, { 44 | period: '2011 Q4', 45 | iphone: 15073, 46 | ipad: 5967, 47 | itouch: 5175 48 | }, { 49 | period: '2012 Q1', 50 | iphone: 10687, 51 | ipad: 4460, 52 | itouch: 2028 53 | }, { 54 | period: '2012 Q2', 55 | iphone: 8432, 56 | ipad: 5713, 57 | itouch: 1791 58 | }], 59 | xkey: 'period', 60 | ykeys: ['iphone', 'ipad', 'itouch'], 61 | labels: ['iPhone', 'iPad', 'iPod Touch'], 62 | pointSize: 2, 63 | hideHover: 'auto', 64 | resize: true 65 | }); 66 | 67 | // Donut Chart 68 | Morris.Donut({ 69 | element: 'morris-donut-chart', 70 | data: [{ 71 | label: "Download Sales", 72 | value: 12 73 | }, { 74 | label: "In-Store Sales", 75 | value: 30 76 | }, { 77 | label: "Mail-Order Sales", 78 | value: 20 79 | }], 80 | resize: true 81 | }); 82 | 83 | // Line Chart 84 | Morris.Line({ 85 | // ID of the element in which to draw the chart. 86 | element: 'morris-line-chart', 87 | // Chart data records -- each entry in this array corresponds to a point on 88 | // the chart. 89 | data: [{ 90 | d: '2012-10-01', 91 | visits: 802 92 | }, { 93 | d: '2012-10-02', 94 | visits: 783 95 | }, { 96 | d: '2012-10-03', 97 | visits: 820 98 | }, { 99 | d: '2012-10-04', 100 | visits: 839 101 | }, { 102 | d: '2012-10-05', 103 | visits: 792 104 | }, { 105 | d: '2012-10-06', 106 | visits: 859 107 | }, { 108 | d: '2012-10-07', 109 | visits: 790 110 | }, { 111 | d: '2012-10-08', 112 | visits: 1680 113 | }, { 114 | d: '2012-10-09', 115 | visits: 1592 116 | }, { 117 | d: '2012-10-10', 118 | visits: 1420 119 | }, { 120 | d: '2012-10-11', 121 | visits: 882 122 | }, { 123 | d: '2012-10-12', 124 | visits: 889 125 | }, { 126 | d: '2012-10-13', 127 | visits: 819 128 | }, { 129 | d: '2012-10-14', 130 | visits: 849 131 | }, { 132 | d: '2012-10-15', 133 | visits: 870 134 | }, { 135 | d: '2012-10-16', 136 | visits: 1063 137 | }, { 138 | d: '2012-10-17', 139 | visits: 1192 140 | }, { 141 | d: '2012-10-18', 142 | visits: 1224 143 | }, { 144 | d: '2012-10-19', 145 | visits: 1329 146 | }, { 147 | d: '2012-10-20', 148 | visits: 1329 149 | }, { 150 | d: '2012-10-21', 151 | visits: 1239 152 | }, { 153 | d: '2012-10-22', 154 | visits: 1190 155 | }, { 156 | d: '2012-10-23', 157 | visits: 1312 158 | }, { 159 | d: '2012-10-24', 160 | visits: 1293 161 | }, { 162 | d: '2012-10-25', 163 | visits: 1283 164 | }, { 165 | d: '2012-10-26', 166 | visits: 1248 167 | }, { 168 | d: '2012-10-27', 169 | visits: 1323 170 | }, { 171 | d: '2012-10-28', 172 | visits: 1390 173 | }, { 174 | d: '2012-10-29', 175 | visits: 1420 176 | }, { 177 | d: '2012-10-30', 178 | visits: 1529 179 | }, { 180 | d: '2012-10-31', 181 | visits: 1892 182 | }, ], 183 | // The name of the data record attribute that contains x-visitss. 184 | xkey: 'd', 185 | // A list of names of data record attributes that contain y-visitss. 186 | ykeys: ['visits'], 187 | // Labels for the ykeys -- will be displayed when you hover over the 188 | // chart. 189 | labels: ['Visits'], 190 | // Disables line smoothing 191 | smooth: false, 192 | resize: true 193 | }); 194 | 195 | // Bar Chart 196 | Morris.Bar({ 197 | element: 'morris-bar-chart', 198 | data: [{ 199 | device: 'iPhone', 200 | geekbench: 136 201 | }, { 202 | device: 'iPhone 3G', 203 | geekbench: 137 204 | }, { 205 | device: 'iPhone 3GS', 206 | geekbench: 275 207 | }, { 208 | device: 'iPhone 4', 209 | geekbench: 380 210 | }, { 211 | device: 'iPhone 4S', 212 | geekbench: 655 213 | }, { 214 | device: 'iPhone 5', 215 | geekbench: 1571 216 | }], 217 | xkey: 'device', 218 | ykeys: ['geekbench'], 219 | labels: ['Geekbench'], 220 | barRatio: 0.4, 221 | xLabelAngle: 35, 222 | hideHover: 'auto', 223 | resize: true 224 | }); 225 | 226 | 227 | }); 228 | -------------------------------------------------------------------------------- /lib/static/js/plugins/typeahead/bloodhound.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * typeahead.js 0.11.1 3 | * https://github.com/twitter/typeahead.js 4 | * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT 5 | */ 6 | 7 | !function(a,b){"function"==typeof define&&define.amd?define("bloodhound",["jquery"],function(c){return a.Bloodhound=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):a.Bloodhound=b(jQuery)}(this,function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},isElement:function(a){return!(!a||1!==a.nodeType)},isJQuery:function(b){return b instanceof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,identity:function(a){return a},clone:function(b){return a.extend(!0,{},b)},getIdGenerator:function(){var a=0;return function(){return a++}},templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c="0.11.1",d=function(){"use strict";function a(a){return a=b.toStr(a),a?a.split(/\s+/):[]}function c(a){return a=b.toStr(a),a?a.split(/\W+/):[]}function d(a){return function(c){return c=b.isArray(c)?c:[].slice.call(arguments,0),function(d){var e=[];return b.each(c,function(c){e=e.concat(a(b.toStr(d[c])))}),e}}}return{nonword:c,whitespace:a,obj:{nonword:d(c),whitespace:d(a)}}}(),e=function(){"use strict";function c(c){this.maxSize=b.isNumber(c)?c:100,this.reset(),this.maxSize<=0&&(this.set=this.get=a.noop)}function d(){this.head=this.tail=null}function e(a,b){this.key=a,this.val=b,this.prev=this.next=null}return b.mixin(c.prototype,{set:function(a,b){var c,d=this.list.tail;this.size>=this.maxSize&&(this.list.remove(d),delete this.hash[d.key],this.size--),(c=this.hash[a])?(c.val=b,this.list.moveToFront(c)):(c=new e(a,b),this.list.add(c),this.hash[a]=c,this.size++)},get:function(a){var b=this.hash[a];return b?(this.list.moveToFront(b),b.val):void 0},reset:function(){this.size=0,this.hash={},this.list=new d}}),b.mixin(d.prototype,{add:function(a){this.head&&(a.next=this.head,this.head.prev=a),this.head=a,this.tail=this.tail||a},remove:function(a){a.prev?a.prev.next=a.next:this.head=a.next,a.next?a.next.prev=a.prev:this.tail=a.prev},moveToFront:function(a){this.remove(a),this.add(a)}}),c}(),f=function(){"use strict";function c(a,c){this.prefix=["__",a,"__"].join(""),this.ttlKey="__ttl__",this.keyMatcher=new RegExp("^"+b.escapeRegExChars(this.prefix)),this.ls=c||h,!this.ls&&this._noop()}function d(){return(new Date).getTime()}function e(a){return JSON.stringify(b.isUndefined(a)?null:a)}function f(b){return a.parseJSON(b)}function g(a){var b,c,d=[],e=h.length;for(b=0;e>b;b++)(c=h.key(b)).match(a)&&d.push(c.replace(a,""));return d}var h;try{h=window.localStorage,h.setItem("~~~","!"),h.removeItem("~~~")}catch(i){h=null}return b.mixin(c.prototype,{_prefix:function(a){return this.prefix+a},_ttlKey:function(a){return this._prefix(a)+this.ttlKey},_noop:function(){this.get=this.set=this.remove=this.clear=this.isExpired=b.noop},_safeSet:function(a,b){try{this.ls.setItem(a,b)}catch(c){"QuotaExceededError"===c.name&&(this.clear(),this._noop())}},get:function(a){return this.isExpired(a)&&this.remove(a),f(this.ls.getItem(this._prefix(a)))},set:function(a,c,f){return b.isNumber(f)?this._safeSet(this._ttlKey(a),e(d()+f)):this.ls.removeItem(this._ttlKey(a)),this._safeSet(this._prefix(a),e(c))},remove:function(a){return this.ls.removeItem(this._ttlKey(a)),this.ls.removeItem(this._prefix(a)),this},clear:function(){var a,b=g(this.keyMatcher);for(a=b.length;a--;)this.remove(b[a]);return this},isExpired:function(a){var c=f(this.ls.getItem(this._ttlKey(a)));return b.isNumber(c)&&d()>c?!0:!1}}),c}(),g=function(){"use strict";function c(a){a=a||{},this.cancelled=!1,this.lastReq=null,this._send=a.transport,this._get=a.limiter?a.limiter(this._get):this._get,this._cache=a.cache===!1?new e(0):h}var d=0,f={},g=6,h=new e(10);return c.setMaxPendingRequests=function(a){g=a},c.resetCache=function(){h.reset()},b.mixin(c.prototype,{_fingerprint:function(b){return b=b||{},b.url+b.type+a.param(b.data||{})},_get:function(a,b){function c(a){b(null,a),k._cache.set(i,a)}function e(){b(!0)}function h(){d--,delete f[i],k.onDeckRequestArgs&&(k._get.apply(k,k.onDeckRequestArgs),k.onDeckRequestArgs=null)}var i,j,k=this;i=this._fingerprint(a),this.cancelled||i!==this.lastReq||((j=f[i])?j.done(c).fail(e):g>d?(d++,f[i]=this._send(a).done(c).fail(e).always(h)):this.onDeckRequestArgs=[].slice.call(arguments,0))},get:function(c,d){var e,f;d=d||a.noop,c=b.isString(c)?{url:c}:c||{},f=this._fingerprint(c),this.cancelled=!1,this.lastReq=f,(e=this._cache.get(f))?d(null,e):this._get(c,d)},cancel:function(){this.cancelled=!0}}),c}(),h=window.SearchIndex=function(){"use strict";function c(c){c=c||{},c.datumTokenizer&&c.queryTokenizer||a.error("datumTokenizer and queryTokenizer are both required"),this.identify=c.identify||b.stringify,this.datumTokenizer=c.datumTokenizer,this.queryTokenizer=c.queryTokenizer,this.reset()}function d(a){return a=b.filter(a,function(a){return!!a}),a=b.map(a,function(a){return a.toLowerCase()})}function e(){var a={};return a[i]=[],a[h]={},a}function f(a){for(var b={},c=[],d=0,e=a.length;e>d;d++)b[a[d]]||(b[a[d]]=!0,c.push(a[d]));return c}function g(a,b){var c=0,d=0,e=[];a=a.sort(),b=b.sort();for(var f=a.length,g=b.length;f>c&&g>d;)a[c]b[d]?d++:(e.push(a[c]),c++,d++);return e}var h="c",i="i";return b.mixin(c.prototype,{bootstrap:function(a){this.datums=a.datums,this.trie=a.trie},add:function(a){var c=this;a=b.isArray(a)?a:[a],b.each(a,function(a){var f,g;c.datums[f=c.identify(a)]=a,g=d(c.datumTokenizer(a)),b.each(g,function(a){var b,d,g;for(b=c.trie,d=a.split("");g=d.shift();)b=b[h][g]||(b[h][g]=e()),b[i].push(f)})})},get:function(a){var c=this;return b.map(a,function(a){return c.datums[a]})},search:function(a){var c,e,j=this;return c=d(this.queryTokenizer(a)),b.each(c,function(a){var b,c,d,f;if(e&&0===e.length)return!1;for(b=j.trie,c=a.split("");b&&(d=c.shift());)b=b[h][d];return b&&0===c.length?(f=b[i].slice(0),void(e=e?g(e,f):f)):(e=[],!1)}),e?b.map(f(e),function(a){return j.datums[a]}):[]},all:function(){var a=[];for(var b in this.datums)a.push(this.datums[b]);return a},reset:function(){this.datums={},this.trie=e()},serialize:function(){return{datums:this.datums,trie:this.trie}}}),c}(),i=function(){"use strict";function a(a){this.url=a.url,this.ttl=a.ttl,this.cache=a.cache,this.prepare=a.prepare,this.transform=a.transform,this.transport=a.transport,this.thumbprint=a.thumbprint,this.storage=new f(a.cacheKey)}var c;return c={data:"data",protocol:"protocol",thumbprint:"thumbprint"},b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},store:function(a){this.cache&&(this.storage.set(c.data,a,this.ttl),this.storage.set(c.protocol,location.protocol,this.ttl),this.storage.set(c.thumbprint,this.thumbprint,this.ttl))},fromCache:function(){var a,b={};return this.cache?(b.data=this.storage.get(c.data),b.protocol=this.storage.get(c.protocol),b.thumbprint=this.storage.get(c.thumbprint),a=b.thumbprint!==this.thumbprint||b.protocol!==location.protocol,b.data&&!a?b.data:null):null},fromNetwork:function(a){function b(){a(!0)}function c(b){a(null,e.transform(b))}var d,e=this;a&&(d=this.prepare(this._settings()),this.transport(d).fail(b).done(c))},clear:function(){return this.storage.clear(),this}}),a}(),j=function(){"use strict";function a(a){this.url=a.url,this.prepare=a.prepare,this.transform=a.transform,this.transport=new g({cache:a.cache,limiter:a.limiter,transport:a.transport})}return b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},get:function(a,b){function c(a,c){b(a?[]:e.transform(c))}var d,e=this;if(b)return a=a||"",d=this.prepare(a,this._settings()),this.transport.get(d,c)},cancelLastRequest:function(){this.transport.cancel()}}),a}(),k=function(){"use strict";function d(d){var e;return d?(e={url:null,ttl:864e5,cache:!0,cacheKey:null,thumbprint:"",prepare:b.identity,transform:b.identity,transport:null},d=b.isString(d)?{url:d}:d,d=b.mixin(e,d),!d.url&&a.error("prefetch requires url to be set"),d.transform=d.filter||d.transform,d.cacheKey=d.cacheKey||d.url,d.thumbprint=c+d.thumbprint,d.transport=d.transport?h(d.transport):a.ajax,d):null}function e(c){var d;if(c)return d={url:null,cache:!0,prepare:null,replace:null,wildcard:null,limiter:null,rateLimitBy:"debounce",rateLimitWait:300,transform:b.identity,transport:null},c=b.isString(c)?{url:c}:c,c=b.mixin(d,c),!c.url&&a.error("remote requires url to be set"),c.transform=c.filter||c.transform,c.prepare=f(c),c.limiter=g(c),c.transport=c.transport?h(c.transport):a.ajax,delete c.replace,delete c.wildcard,delete c.rateLimitBy,delete c.rateLimitWait,c}function f(a){function b(a,b){return b.url=f(b.url,a),b}function c(a,b){return b.url=b.url.replace(g,encodeURIComponent(a)),b}function d(a,b){return b}var e,f,g;return e=a.prepare,f=a.replace,g=a.wildcard,e?e:e=f?b:a.wildcard?c:d}function g(a){function c(a){return function(c){return b.debounce(c,a)}}function d(a){return function(c){return b.throttle(c,a)}}var e,f,g;return e=a.limiter,f=a.rateLimitBy,g=a.rateLimitWait,e||(e=/^throttle$/i.test(f)?d(g):c(g)),e}function h(c){return function(d){function e(a){b.defer(function(){g.resolve(a)})}function f(a){b.defer(function(){g.reject(a)})}var g=a.Deferred();return c(d,e,f),g}}return function(c){var f,g;return f={initialize:!0,identify:b.stringify,datumTokenizer:null,queryTokenizer:null,sufficient:5,sorter:null,local:[],prefetch:null,remote:null},c=b.mixin(f,c||{}),!c.datumTokenizer&&a.error("datumTokenizer is required"),!c.queryTokenizer&&a.error("queryTokenizer is required"),g=c.sorter,c.sorter=g?function(a){return a.sort(g)}:b.identity,c.local=b.isFunction(c.local)?c.local():c.local,c.prefetch=d(c.prefetch),c.remote=e(c.remote),c}}(),l=function(){"use strict";function c(a){a=k(a),this.sorter=a.sorter,this.identify=a.identify,this.sufficient=a.sufficient,this.local=a.local,this.remote=a.remote?new j(a.remote):null,this.prefetch=a.prefetch?new i(a.prefetch):null,this.index=new h({identify:this.identify,datumTokenizer:a.datumTokenizer,queryTokenizer:a.queryTokenizer}),a.initialize!==!1&&this.initialize()}var e;return e=window&&window.Bloodhound,c.noConflict=function(){return window&&(window.Bloodhound=e),c},c.tokenizers=d,b.mixin(c.prototype,{__ttAdapter:function(){function a(a,b,d){return c.search(a,b,d)}function b(a,b){return c.search(a,b)}var c=this;return this.remote?a:b},_loadPrefetch:function(){function b(a,b){return a?c.reject():(e.add(b),e.prefetch.store(e.index.serialize()),void c.resolve())}var c,d,e=this;return c=a.Deferred(),this.prefetch?(d=this.prefetch.fromCache())?(this.index.bootstrap(d),c.resolve()):this.prefetch.fromNetwork(b):c.resolve(),c.promise()},_initialize:function(){function a(){b.add(b.local)}var b=this;return this.clear(),(this.initPromise=this._loadPrefetch()).done(a),this.initPromise},initialize:function(a){return!this.initPromise||a?this._initialize():this.initPromise},add:function(a){return this.index.add(a),this},get:function(a){return a=b.isArray(a)?a:[].slice.call(arguments),this.index.get(a)},search:function(a,c,d){function e(a){var c=[];b.each(a,function(a){!b.some(f,function(b){return g.identify(a)===g.identify(b)})&&c.push(a)}),d&&d(c)}var f,g=this;return f=this.sorter(this.index.search(a)),c(this.remote?f.slice():f),this.remote&&f.length0?this.$element.data("active",b[0]):this.$element.data("active",null),this.options.addItem&&b.push(this.options.addItem),"all"==this.options.items?this.render(b).show():this.render(b.slice(0,this.options.items)).show()):this.shown?this.hide():this},matcher:function(a){var b=this.displayText(a);return~b.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){for(var b,c=[],d=[],e=[];b=a.shift();){var f=this.displayText(b);f.toLowerCase().indexOf(this.query.toLowerCase())?~f.indexOf(this.query)?d.push(b):e.push(b):c.push(b)}return c.concat(d,e)},highlighter:function(b){var c,d,e,f,g,h=a("
"),i=this.query,j=b.toLowerCase().indexOf(i.toLowerCase());if(c=i.length,0===c)return h.text(b).html();for(;j>-1;)d=b.substr(0,j),e=b.substr(j,c),f=b.substr(j+c),g=a("").text(e),h.append(document.createTextNode(d)).append(g),b=f,j=b.toLowerCase().indexOf(i.toLowerCase());return h.append(document.createTextNode(b)).html()},render:function(b){var c=this,d=this,e=!1;return b=a(b).map(function(b,f){var g=d.displayText(f);return b=a(c.options.item).data("value",f),b.find("a").html(c.highlighter(g)),g==d.$element.val()&&(b.addClass("active"),d.$element.data("active",f),e=!0),b[0]}),this.autoSelect&&!e&&(b.first().addClass("active"),this.$element.data("active",b.first().data("value"))),this.$menu.html(b),this},displayText:function(a){return a.name||a},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this)).on("mouseleave","li",a.proxy(this.mouseleave,this))},destroy:function(){this.$element.data("typeahead",null),this.$element.data("active",null),this.$element.off("focus").off("blur").off("keypress").off("keyup"),this.eventSupported("keydown")&&this.$element.off("keydown"),this.$menu.remove()},eventSupported:function(a){var b=a in this.$element;return b||(this.$element.setAttribute(a,"return;"),b="function"==typeof this.$element[a]),b},move:function(a){if(this.shown){switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:if(a.shiftKey)return;a.preventDefault(),this.prev();break;case 40:if(a.shiftKey)return;a.preventDefault(),this.next()}a.stopPropagation()}},keydown:function(b){this.suppressKeyPressRepeat=~a.inArray(b.keyCode,[40,38,9,13,27]),this.shown||40!=b.keyCode?this.move(b):this.lookup()},keypress:function(a){this.suppressKeyPressRepeat||this.move(a)},keyup:function(a){switch(a.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},focus:function(a){this.focused||(this.focused=!0,this.options.showHintOnFocus&&this.lookup(""))},blur:function(a){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(a){a.stopPropagation(),a.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(b){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")},mouseleave:function(a){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var c=a.fn.typeahead;a.fn.typeahead=function(c){var d=arguments;return"string"==typeof c&&"getActive"==c?this.data("active"):this.each(function(){var e=a(this),f=e.data("typeahead"),g="object"==typeof c&&c;f||e.data("typeahead",f=new b(this,g)),"string"==typeof c&&(d.length>1?f[c].apply(f,Array.prototype.slice.call(d,1)):f[c]())})},a.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1,scrollHeight:0,autoSelect:!0,afterSelect:a.noop,addItem:!1,delay:0},a.fn.typeahead.Constructor=b,a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this},a(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);c.data("typeahead")||c.typeahead(c.data())})}); -------------------------------------------------------------------------------- /lib/strategy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Strategy { 4 | constructor(options) { 5 | this.passport = options.passport; 6 | this.username = options.username; 7 | this.password = options.password; 8 | } 9 | 10 | middleware(req, res, next) { 11 | if (!this.passport) { 12 | if(req.session.isLoggedIn) { 13 | req.app.locals.user = req.session.user; 14 | return next(); 15 | }; 16 | if(req.method === 'POST' && req.path === ('/login' || '/logout')) { 17 | return next() 18 | }; 19 | res.locals.redirect = req.app.locals.appPath + req.path; 20 | return res.render('login', {pageId: 'login'}); 21 | } else { 22 | // will have to populate "user" variable for template 23 | return this.passport; 24 | } 25 | } 26 | 27 | login(req, res, next) { 28 | // safeguard: don't use this 29 | // if passport is defined! 30 | var redirect = req.query.redirect; 31 | if(!this.passport) { 32 | if (req.body.username !== this.username || req.body.password !== this.password) { 33 | var message = "Looks like you've got the wrong username or password!" 34 | req.session.message.error.push(message); 35 | // loops back through middleware 36 | return res.redirect(redirect) 37 | } else { 38 | req.session.user = { 39 | name: 'admin' 40 | }; 41 | req.app.locals.user = req.session.user; 42 | req.session.isLoggedIn = true; 43 | return res.redirect(redirect); 44 | } 45 | } 46 | res.send(500); 47 | } 48 | 49 | logout(req, res) { 50 | delete req.session.user; 51 | delete req.session.isLoggedIn; 52 | var message = "You've been successfully logged out!" 53 | req.session.message.success.push(message); 54 | res.redirect(req.app.locals.appPath); 55 | } 56 | }; 57 | 58 | module.exports = Strategy; -------------------------------------------------------------------------------- /lib/utils/paginate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var url = require('url'), 3 | qs = require('querystring'); 4 | 5 | // Thanks Madhusudhan Srinivasa 6 | // http://madhums.me/2012/08/20/pagination-using-mongoose-express-and-jade/ 7 | 8 | module.exports = function(req, res) { 9 | return function(pages, page) { 10 | var params = qs.parse(url.parse(req.url).query), 11 | str = '' 12 | 13 | params.page = 0 14 | var clas = page == 0 ? "active" : "no" 15 | str += '
  • First
  • ' 16 | for (var p = 1; p < pages; p++) { 17 | params.page = p 18 | clas = page == p ? "active" : "no" 19 | str += '
  • ' + p + '
  • ' 20 | } 21 | params.page = --p 22 | clas = page == params.page ? "active" : "no" 23 | str += '
  • Last
  • ' 24 | 25 | return str 26 | }; 27 | }; -------------------------------------------------------------------------------- /lib/views/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdngr/sriracha/f77d9fc8a7ff1ffdefa7462342b542159f6cad24/lib/views/.DS_Store -------------------------------------------------------------------------------- /lib/views/collection.jade: -------------------------------------------------------------------------------- 1 | extends ./layout 2 | 3 | block content 4 | #wrapper 5 | include ./nav 6 | #page-wrapper 7 | include ./messages 8 | .container-fluid 9 | // Page Heading 10 | .row 11 | .col-md-10 12 | h1.page-header 13 | | #{Collection.pluralName} 14 | ol.breadcrumb 15 | li 16 | i.fa.fa-dashboard 17 | a(href=appPath) Admin 18 | li.active 19 | i.fa.fa- 20 | | #{Collection.pluralName} 21 | // /.row 22 | if Collection.searchField 23 | .row 24 | .col-md-10 25 | .form-group.input-group-lg.input-group 26 | input.typeahead.form-control(type='text', placeholder="Search by " + Collection.searchField) 27 | span.input-group-btn 28 | button.btn.btn-default(type='button') 29 | i.fa.fa-search 30 | 31 | .row 32 | .col-md-10 33 | a.btn.btn-success.btn-lg(href=Collection.collection.name + "/new") New #{Collection.modelName} 34 | 35 | 36 | .row 37 | .col-md-10 38 | h2 #{Collection.pluralName} 39 | .table-responsive 40 | table.table.table-bordered.table-hover.table-striped 41 | thead 42 | tr 43 | th # 44 | each field in Collection.adminPaths 45 | th= field 46 | a(href="?sortField=" + field + "&criteria=" + (criteria || '')) 47 | i.fa.fa-sort.pull-right 48 | tbody 49 | each doc, i in docs 50 | tr 51 | td= 10 * page + (i + 1) 52 | each field in Collection.adminPaths 53 | td 54 | a(href="#{appPath}/#{Collection.collection.name}/#{doc.id}") #{doc[field]} 55 | if (pages > 1) 56 | nav 57 | ul.pagination 58 | != paginate(pages, page) 59 | 60 | block append scripts 61 | script(type='text/javascript'). 62 | 63 | $('.typeahead').typeahead({ 64 | displayText: function(item) { 65 | return item.value; 66 | }, 67 | afterSelect: function(item) { 68 | window.location.href = window.location.href+'/'+item.id 69 | }, 70 | source: function(term, process) { 71 | return $.ajax({ 72 | dataType: "json", 73 | url: window.location.href.split('?')[0] + '/suggest', 74 | data: {term: term}, 75 | type: 'POST', 76 | success: function (json) { 77 | process(json); 78 | } 79 | }) 80 | } 81 | }); -------------------------------------------------------------------------------- /lib/views/doc.jade: -------------------------------------------------------------------------------- 1 | extends ./layout 2 | 3 | block content 4 | #wrapper 5 | include ./nav 6 | #page-wrapper 7 | include ./messages 8 | .container-fluid 9 | if !doc 10 | .row 11 | .col-lg-12 12 | h1.page-header 13 | #{Collection.modelName} 14 | ol.breadcrumb 15 | li 16 | i.fa.fa-dashboard 17 | a(href=appPath) Admin 18 | li.active 19 | i.fa.fa- 20 | a(href=appPath + '/' + Collection.collection.name) 21 | | #{Collection.pluralName} 22 | li 23 | i.fa.fa- 24 | .row 25 | .col-lg-12 26 | h2 27 | Can't find document with id #{id}! 28 | 29 | 30 | if doc 31 | .row 32 | .col-lg-12 33 | h1.page-header 34 | #{Collection.modelName} #{doc.id} 35 | ol.breadcrumb 36 | li 37 | i.fa.fa-dashboard 38 | a(href=appPath) Admin 39 | li.active 40 | i.fa.fa- 41 | a(href=appPath + '/' + Collection.collection.name) 42 | | #{Collection.pluralName} 43 | li 44 | i.fa.fa- 45 | | #{doc.id} 46 | .row 47 | .col-xs-8.col-xs-offset-2 48 | form(action="", method="post") 49 | each path in Collection.adminPaths 50 | include ./fields/fields 51 | ul.list-inline 52 | li 53 | button.btn.btn-success(type='submit') Save 54 | li 55 | button.btn.btn-danger(type='submit' name="_method" value='DELETE') Delete 56 | // textarea 57 | block append scripts 58 | script(type='text/javascript'). 59 | // bootstrap textareas 60 | tinymce.init({ 61 | selector: "textarea" 62 | }); 63 | 64 | //date 65 | block append scripts 66 | script(type='text/javascript'). 67 | $(function () { 68 | $('.datetimepicker').datetimepicker(); 69 | }); 70 | -------------------------------------------------------------------------------- /lib/views/fields/array.jade: -------------------------------------------------------------------------------- 1 | .form-group(class=typeof errors[field] === "undefined" ? "" : "has-error") 2 | .input-group 3 | label.input-group-addon(for=path) #{path} 4 | input.form-control(name=path, type='text', placeholder='', autofocus='', value=doc.get(path)) 5 | p.help-block 6 | | Array field. Supply a list of comma separated values. 7 | if errors[path] 8 | span.help-block.text-danger= errors[path].message -------------------------------------------------------------------------------- /lib/views/fields/checkbox.jade: -------------------------------------------------------------------------------- 1 | .checkbox(class=typeof errors[path] === "undefined" ? "" : "has-error") 2 | label 3 | input(name=path type="hidden" value="false") 4 | input(name=path, type='checkbox', autofocus='', value=doc.get(path) checked=doc.get(path)) 5 | | #{path} 6 | if errors[path] 7 | span.help-block.text-danger= errors[path].message -------------------------------------------------------------------------------- /lib/views/fields/date.jade: -------------------------------------------------------------------------------- 1 | .form-group(class=typeof errors[field] === "undefined" ? "" : "has-error") 2 | .input-group.datetimepicker 3 | label.input-group-addon(for=path) #{path} 4 | input.form-control(name=path, type='text', placeholder='', autofocus='', data-date-default-date="#{doc.get(path)}") 5 | span.input-group-addon 6 | span.glyphicon.glyphicon-calendar 7 | if errors[path] 8 | span.help-block.text-danger= errors[path].message -------------------------------------------------------------------------------- /lib/views/fields/fields.jade: -------------------------------------------------------------------------------- 1 | case Collection.getPathType(path) 2 | when 'text' 3 | include ./text 4 | when 'checkbox' 5 | include ./checkbox 6 | when 'ref' 7 | include ./ref 8 | when 'textarea' 9 | include ./textarea 10 | when 'array' 11 | include ./array 12 | when 'date' 13 | include ./date 14 | -------------------------------------------------------------------------------- /lib/views/fields/object.jade: -------------------------------------------------------------------------------- 1 | each val, field in value 2 | include ./fields -------------------------------------------------------------------------------- /lib/views/fields/ref.jade: -------------------------------------------------------------------------------- 1 | 2 | .form-group(class=typeof errors[path] === "undefined" ? "" : "has-error") 3 | .input-group 4 | label.input-group-addon(for=path) #{path} 5 | div.ref-host 6 | ul 7 | - var value = doc.get(path); 8 | if value && value.length 9 | each ref in value 10 | a.btn.btn-success(href=appPath + '/' + ref._collectionName + '/' + ref) 11 | | #{ref} 12 | else 13 | - var ref = value 14 | li 15 | a.btn.btn-success(href=appPath + '/' + ref._collectionName + '/' + ref) 16 | | #{ref} 17 | if errors[path] 18 | span.help-block.text-danger= errors[path].message -------------------------------------------------------------------------------- /lib/views/fields/text.jade: -------------------------------------------------------------------------------- 1 | .form-group(class=typeof errors[field] === "undefined" ? "" : "has-error") 2 | .input-group 3 | label.input-group-addon(for=path) #{path} 4 | input.form-control(name=path, type='text', placeholder='', autofocus='', value=doc.get(path)) 5 | if errors[path] 6 | span.help-block.text-danger= errors[path].message -------------------------------------------------------------------------------- /lib/views/fields/textarea.jade: -------------------------------------------------------------------------------- 1 | .form-group(class=typeof errors[field] === "undefined" ? "" : "has-error") 2 | .input-group 3 | h4 4 | .label.label-default(for=path) #{path} 5 | textarea.form-control(id=path name=path, type='text', placeholder='', autofocus='',) 6 | | #{doc.get(path)} 7 | if errors[path] 8 | span.help-block.text-danger= errors[path].message 9 | 10 | -------------------------------------------------------------------------------- /lib/views/index.jade: -------------------------------------------------------------------------------- 1 | extends ./layout 2 | 3 | block content 4 | #wrapper 5 | include ./nav 6 | #page-wrapper 7 | include ./messages 8 | 9 | .container-fluid 10 | // Page Heading 11 | .row 12 | .col-lg-12 13 | h1.page-header 14 | | Dashboard 15 | small Sriracha Dashboard 16 | ol.breadcrumb 17 | li.active 18 | i.fa.fa-dashboard 19 | | Dashboard 20 | .row 21 | .col-lg-12 22 | p.lead 23 | | Thanks for using Sriracha. 24 | | Would love to have you contribute. 25 | | File a bug, request a feature, or fork me on 26 | | github. 27 | -------------------------------------------------------------------------------- /lib/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | meta(charset='utf-8') 5 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 6 | meta(name='viewport', content='width=device-width, initial-scale=1') 7 | meta(name='description', content='') 8 | meta(name='author', content='') 9 | link(rel='shortcut icon' href='#{appPath}/static/favicon.ico' type='image/x-icon') 10 | link(rel='icon' href='#{appPath}/static/favicon.ico' type='image/x-icon') 11 | title SB Admin - Bootstrap Admin Template 12 | block styles 13 | // Bootstrap Core CSS 14 | link(href='#{appPath}/static/css/bootstrap.min.css', rel='stylesheet') 15 | // Custom CSS 16 | link(href='#{appPath}/static/css/sb-admin.css', rel='stylesheet') 17 | // Morris Charts CSS 18 | link(href='#{appPath}/static/css/plugins/morris.css', rel='stylesheet') 19 | // Custom Fonts 20 | link(href='#{appPath}/static/font-awesome/css/font-awesome.min.css', rel='stylesheet', type='text/css') 21 | // HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries 22 | // WARNING: Respond.js doesn't work if you view the page via file:// 23 | //if lt IE 9 24 | script(src='https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js') 25 | script(src='https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js') 26 | body(id=pageId) 27 | block content 28 | 29 | block scripts 30 | script(type="text/javascript" src='#{appPath}/static/js/jquery.js') 31 | script(type="text/javascript" src="#{appPath}/components/tinymce/tinymce.js") 32 | // Bootstrap Core JavaScript 33 | script(src='#{appPath}/static/js/bootstrap.min.js') 34 | // Search suggestions 35 | script(src='#{appPath}/static/js/plugins/typeahead/bootstrap3-typeahead.min.js') 36 | script(src='#{appPath}/static/js/plugins/typeahead/bloodhound.min.js') 37 | 38 | script(type="text/javascript" src="#{appPath}/components/moment/min/moment.min.js") 39 | 40 | script(type="text/javascript" src="#{appPath}/components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js") 41 | 42 | script(type='text/javascript'). 43 | //- close alerts after 5 seconds 44 | window.setTimeout(function() { $(".alert").alert('close'); }, 5000); 45 | -------------------------------------------------------------------------------- /lib/views/login.jade: -------------------------------------------------------------------------------- 1 | extends ./layout 2 | 3 | block append styles 4 | 5 | style(type='text/css'). 6 | 7 | body { 8 | padding-top: 40px; 9 | padding-bottom: 40px; 10 | background-color: #eee; 11 | } 12 | 13 | .form-signin { 14 | max-width: 330px; 15 | padding: 15px; 16 | margin: 0 auto; 17 | } 18 | .form-signin .form-signin-heading, 19 | .form-signin .checkbox { 20 | margin-bottom: 10px; 21 | } 22 | .form-signin .checkbox { 23 | font-weight: normal; 24 | } 25 | .form-signin .form-control { 26 | position: relative; 27 | height: auto; 28 | -webkit-box-sizing: border-box; 29 | -moz-box-sizing: border-box; 30 | box-sizing: border-box; 31 | padding: 10px; 32 | font-size: 16px; 33 | } 34 | .form-signin .form-control:focus { 35 | z-index: 2; 36 | } 37 | .form-signin input[type="email"] { 38 | margin-bottom: -1px; 39 | border-bottom-right-radius: 0; 40 | border-bottom-left-radius: 0; 41 | } 42 | .form-signin input[type="password"] { 43 | margin-bottom: 10px; 44 | border-top-left-radius: 0; 45 | border-top-right-radius: 0; 46 | } 47 | 48 | block content 49 | #page-wrapper 50 | include ./messages 51 | .container 52 | form.form-signin(action=appPath + '/login?redirect=' + redirect, method="POST") 53 | h2.form-signin-heading Please sign in 54 | label.sr-only(for='inputEmail') Email address 55 | input.form-control(name="username" type='text', placeholder='username', required='', autofocus='') 56 | label.sr-only(for='inputPassword') Password 57 | input.form-control(name="password" type='password', placeholder='Password', required='') 58 | .checkbox 59 | label 60 | input(type='checkbox', value='remember-me') 61 | | Remember me 62 | //- input(type="hidden" name="redirect" val=window.location.pathname) 63 | button.btn.btn-lg.btn-primary.btn-block(type='submit') Sign in -------------------------------------------------------------------------------- /lib/views/messages.jade: -------------------------------------------------------------------------------- 1 | - var i 2 | - if (message.success && message.success.length) 3 | .alert.alert-success.alert-dismissable 4 | button.close(type="button", data-dismiss="alert", aria-hidden="true") × 5 | - for (i = 0; i < message.success.length; i++) 6 | .center!= message.success[i] 7 | - if (message.error && message.error.length) 8 | .alert.alert-danger.alert-dismissable 9 | button.close(type="button", data-dismiss="alert", aria-hidden="true") × 10 | - for (i = 0; i < message.error.length; i++) 11 | .center!= message.error[i] 12 | 13 | - message.info = message.success = message.error = [] 14 | -------------------------------------------------------------------------------- /lib/views/nav.jade: -------------------------------------------------------------------------------- 1 | // Navigation 2 | nav.navbar.navbar-inverse.navbar-fixed-top(role='navigation') 3 | // Brand and toggle get grouped for better mobile display 4 | .navbar-header 5 | button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-ex1-collapse') 6 | span.sr-only Toggle navigation 7 | span.icon-bar 8 | span.icon-bar 9 | span.icon-bar 10 | a.navbar-brand(href='#{appPath}') Sriracha Admin 11 | 12 | // Top Menu Items 13 | ul.nav.navbar-right.top-nav 14 | li.dropdown 15 | a.dropdown-toggle(href='#', data-toggle='dropdown') 16 | i.fa.fa-user 17 | //- | #{user.firstName} #{user.lastName} 18 | b.caret 19 | ul.dropdown-menu 20 | li 21 | a(href) 22 | i.fa.fa-fw.fa-user 23 | | #{user.name} 24 | li.divider 25 | li 26 | form(id="logout" action=appPath + '/logout', method='POST') 27 | i.fa.fa-fw.fa-power-off 28 | button(type="submit") Log Out 29 | 30 | // Sidebar Menu Items - These collapse to the responsive navigation menu on small screens 31 | .collapse.navbar-collapse.navbar-ex1-collapse 32 | ul.nav.navbar-nav.side-nav 33 | each Collection in collections 34 | li 35 | a(href=appPath + '/' + Collection.collection.name) 36 | i.fa.fa-fw.fa-file 37 | | #{Collection.pluralName} 38 | // /.navbar-collapse -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sriracha", 3 | "version": "1.0.0", 4 | "description": "A spicy admin middleware for mongoose and express.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/hdngr/sriracha.git" 8 | }, 9 | "main": "index.js", 10 | "files": [ 11 | "LICENSE", 12 | "Readme.md", 13 | "index.js", 14 | "lib/" 15 | ], 16 | "scripts": { 17 | "start": "gulp simple", 18 | "test": "gulp test", 19 | "test-ci": "istanbul cover -x 'examples' _mocha -- tests/ -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 20 | }, 21 | "author": "", 22 | "license": "ISC", 23 | "dependencies": { 24 | "body-parser": "^1.14.1", 25 | "express": ">=4.0", 26 | "express-session": "^1.12.1", 27 | "jade": "^1.11.0", 28 | "lodash": "^3.10.1", 29 | "method-override": "^2.3.5", 30 | "mongoose": ">=4" 31 | }, 32 | "devDependencies": { 33 | "async": "^1.4.2", 34 | "cheerio": "^0.19.0", 35 | "coveralls": "^2.11.4", 36 | "express-session": "^1.12.0", 37 | "gulp": "^3.9.0", 38 | "gulp-livereload": "^3.8.0", 39 | "gulp-mocha": "^2.1.3", 40 | "gulp-nodemon": "^2.0.4", 41 | "istanbul": "^0.4.0", 42 | "minimist": "^1.1.1", 43 | "mocha": "^2.3.3", 44 | "mocha-lcov-reporter": "^1.0.0", 45 | "should": "^7.1.0", 46 | "supertest": "^1.1.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/advanced-e2e.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // import the moongoose helper utilities 4 | var should = require('should'), 5 | cheerio = require('cheerio'), 6 | advanced = require('./utils').advanced; 7 | 8 | describe('using advanced example', function() { 9 | var agent, server; 10 | 11 | before(function(done) { 12 | var result = advanced.start(); 13 | agent = result[0]; 14 | server = result[1]; 15 | done(); 16 | }); 17 | 18 | after(function(done) { 19 | advanced.stop(server, done); 20 | }); 21 | 22 | describe('/crazy-mount-path/login', function() { 23 | it('should authenticate the user', function(done) { 24 | // create session 25 | agent.post('/crazy-mount-path/login') 26 | .type('form') 27 | .send({ 28 | username: 'admin', 29 | password: 'admin' 30 | }) 31 | .expect(302) 32 | .end(done); 33 | }); 34 | }); 35 | 36 | describe('crazy-mount-path is the mountpath', function() { 37 | it('/crazy-mount-path should respond with the index page a status code of 200', function(done) { 38 | agent.get('/crazy-mount-path') 39 | .expect(200) 40 | .expect(function(res) { 41 | var $ = cheerio.load(res.text); 42 | $('body').attr('id').should.match('index'); 43 | }) 44 | .end(done); 45 | }); 46 | }); 47 | 48 | describe('route /crazy-mount-path/users', function() { 49 | it('should respond with status code 200 and Users rendered', function(done) { 50 | agent.get('/crazy-mount-path/users') 51 | .expect(200) 52 | .expect(function(res) { 53 | var $ = cheerio.load(res.text); 54 | $('h1.page-header').text().should.equal('Users'); 55 | }) 56 | .end(done); 57 | }); 58 | }); 59 | 60 | describe('route /crazy-mount-path/posts', function() { 61 | it('should respond with status code 200 and Posts rendered', function(done) { 62 | agent.get('/crazy-mount-path/posts') 63 | .expect(200) 64 | .expect(function(res) { 65 | var $ = cheerio.load(res.text); 66 | $('h1.page-header').text().should.equal('Posts'); 67 | }) 68 | .end(done); 69 | }); 70 | 71 | }); 72 | 73 | }); -------------------------------------------------------------------------------- /tests/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var main = require('../../lib/controllers/main'), 4 | advanced = require('../utils').advanced; 5 | 6 | 7 | describe('main', function() { 8 | 9 | // before(function(done) { 10 | // var result = advanced.start(); 11 | // agent = result[0]; 12 | // server = result[1]; 13 | // agent.post('/crazy-mount-path/login') 14 | // .type('form') 15 | // .send({ 16 | // username: 'admin', 17 | // password: 'admin' 18 | // }) 19 | // .end(done); 20 | // }); 21 | 22 | // after(function(done) { 23 | // advanced.stop(server, done); 24 | // }); 25 | 26 | describe('.main', function() { 27 | it('should render the "index" view', function() { 28 | var req = {}; 29 | var res = { 30 | viewName: undefined, 31 | render: function(view) { 32 | this.viewName = view; 33 | } 34 | }; 35 | main.main(req, res); 36 | res.viewName.should.equal('index'); 37 | }) 38 | }) 39 | 40 | 41 | }); -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | process.env.NODE_ENV = 'test'; 4 | 5 | describe('end to end tests', function() { 6 | require('./simple-e2e'); 7 | require('./advanced-e2e'); 8 | }); 9 | 10 | describe('models', function() { 11 | require('./models/Collection'); 12 | require('./models/Document'); 13 | }); 14 | 15 | describe('controllers', function() { 16 | require('./controllers/main'); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/models/Collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Collection = require('../../lib/models/Collection'); 4 | var options = require('../../lib/options')(); 5 | var should = require('should'); 6 | 7 | describe('Collection', function() { 8 | 9 | var User; 10 | var Post; 11 | var UserCol; 12 | var PostCol; 13 | 14 | before(function(done) { 15 | User = require('../../examples/advanced/models/User'); 16 | Post = require('../../examples/advanced/models/Post'); 17 | UserCol = Collection(User, options); 18 | PostCol = Collection(Post, options); 19 | done(); 20 | }); 21 | 22 | describe('.find .findOne .remove', function() { 23 | it('should contain mongoose Model methods', function() { 24 | UserCol.find.should.be.a.Function 25 | UserCol.findOne.should.be.a.Function 26 | UserCol.remove.should.be.a.Function 27 | }) 28 | }); 29 | 30 | describe('.pluralName', function() { 31 | it('should be capitalized with an s at the end', function() { 32 | UserCol.pluralName.should.equal('Users'); 33 | PostCol.pluralName.should.equal('Posts'); 34 | }) 35 | }); 36 | 37 | describe('.getPathType', function() { 38 | it('should return "text" for a String schema type', function() { 39 | UserCol.getPathType('firstName').should.equal('text'); 40 | }); 41 | 42 | it('should return "checkbox" for a Boolean schema type', function() { 43 | UserCol.getPathType('onboarding.hasLoggedIn').should.equal('checkbox'); 44 | }); 45 | 46 | it('should return "ref" for an ObjectId schema type that references another collection', function() { 47 | PostCol.getPathType('author').should.equal('ref'); 48 | }); 49 | 50 | it('should return "ref" for an ObjectId schema type that does not reference another collection', function() { 51 | PostCol.getPathType('_id').should.equal('text'); 52 | }); 53 | 54 | it('should return "date" field type from mongoose schema if it\'s defined', function() { 55 | PostCol.getPathType('createdOn').should.equal('date'); 56 | }); 57 | 58 | it('should return "adminFieldType" from mongoose schema if it\'s defined', function() { 59 | PostCol.getPathType('body').should.equal('textarea'); 60 | }); 61 | 62 | it('should return "array" field type by default from an Array schema type', function() { 63 | UserCol.getPathType('roles').should.equal('array'); 64 | }); 65 | 66 | it('should return "date" field type if the adminFieldType is set to "date"', function() { 67 | UserCol.getPathType('onboarding.signupDate').should.equal('date'); 68 | }); 69 | 70 | }); 71 | 72 | }) -------------------------------------------------------------------------------- /tests/models/Document.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Document = require('../../lib/models/Document'); 4 | var should = require('should'); 5 | 6 | describe('Document', function() { 7 | 8 | // var Post; 9 | // var User; 10 | var doc; 11 | // var doc; 12 | // var collections = {}; 13 | 14 | before(function(done) { 15 | var Post = require('../../examples/advanced/models/Post'); 16 | var User = require('../../examples/advanced/models/User'); 17 | var collections = {'User': User }; 18 | var user = new User(); 19 | var post = new Post({ title: 'Some Post', author: user.id}); 20 | doc = Document(post, null, collections); 21 | done(); 22 | }); 23 | 24 | describe('.._collectionName', function() { 25 | it('should be created for any that is a ref', function() { 26 | doc.author._collectionName.should.equal('users'); 27 | }) 28 | }) 29 | 30 | }); -------------------------------------------------------------------------------- /tests/simple-e2e.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // import the moongoose helper utilities 4 | var request = require('supertest'); 5 | var should = require('should'); 6 | 7 | describe('using simple example', function() { 8 | var agent; 9 | var server; 10 | 11 | before(function(done) { 12 | var mongoose = require('mongoose'); 13 | // clear all mongoose connections 14 | mongoose.connection.models = {}; 15 | mongoose.models = {}; 16 | // if mongoose connection is still alive disconnect it 17 | if (mongoose.connection.readyState === 1) { 18 | try { 19 | mongoose.disconnect(); 20 | } catch(e) { 21 | console.log(e); 22 | } 23 | startServer(); 24 | } else { 25 | startServer(); 26 | }; 27 | 28 | function startServer() { 29 | // start the server 30 | console.log('starting simple server'); 31 | server = require('../examples/simple/server'); 32 | agent = request.agent(server); 33 | done(); 34 | } 35 | }); 36 | 37 | after(function(done) { 38 | var mock = require('../examples/simple/mock'); 39 | console.log('closing simple server'); 40 | try { 41 | server.close(); 42 | } catch(e) { 43 | console.log(e); 44 | } 45 | mock.destroy(done); 46 | }); 47 | 48 | describe('/admin/login', function() { 49 | it('should authenticate the user', function(done) { 50 | // create session 51 | agent.post('/admin/login') 52 | .type('form') 53 | .send({ 54 | username: 'admin', 55 | password: 'admin' 56 | }) 57 | .expect(302) 58 | .end(done); 59 | }); 60 | }); 61 | 62 | describe('/admin is the mount path and', function() { 63 | it('should respond with a status code of 200', function(done) { 64 | agent.get('/admin') 65 | .expect(200) 66 | .end(done); 67 | }); 68 | }); 69 | 70 | describe('/admin/users', function() { 71 | it('should respond with status code 200', function(done) { 72 | agent.get('/admin/users') 73 | .expect(200) 74 | .end(done); 75 | }); 76 | 77 | it('should respond with user models rendered', function(done) { 78 | agent.get('/admin/users') 79 | .expect(function(res) { 80 | res.text.should.match(/Users/) 81 | }) 82 | .end(done); 83 | }); 84 | }); 85 | 86 | describe('/admin/posts', function() { 87 | it('should respond with status code 200', function(done) { 88 | agent.get('/admin/posts') 89 | .expect(200) 90 | .end(done); 91 | }); 92 | 93 | it('should respond with Post model rendered', function(done) { 94 | agent.get('/admin/posts') 95 | .expect(function(res) { 96 | res.text.should.match(/Posts/) 97 | }) 98 | .end(done); 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /tests/tests.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | Document testing strategy for contributers. 3 | -------------------------------------------------------------------------------- /tests/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var request = require('supertest') 4 | 5 | module.exports = { 6 | advanced: { 7 | start: function() { 8 | var mongoose = require('mongoose'); 9 | mongoose.connection.models = {}; 10 | mongoose.models = {}; 11 | if (mongoose.connection.readyState === 1) { 12 | mongoose.disconnect(); 13 | }; 14 | console.log('starting advanced server'); 15 | var server = require('../examples/advanced/server'); 16 | var agent = request.agent(server); 17 | return [agent, server] 18 | }, 19 | stop: function(server, done) { 20 | var mock = require('../examples/advanced/mock'); 21 | console.log('closing advanced server'); 22 | server.close(); 23 | mock.destroy(done); 24 | } 25 | } 26 | }; --------------------------------------------------------------------------------