├── .gitignore ├── README.md ├── app.js ├── app ├── assets │ ├── javascript │ │ ├── .keep │ │ ├── app.js │ │ ├── controllers │ │ │ ├── noteCreateController.js │ │ │ ├── noteEditController.js │ │ │ ├── noteIndexController.js │ │ │ ├── noteShowController.js │ │ │ ├── usersIndexController.js │ │ │ └── usersShowController.js │ │ ├── directives │ │ │ ├── nwCategorySelector.js │ │ │ ├── nwPageNav.js │ │ │ └── title.js │ │ ├── filters │ │ │ └── categoryFilter.js │ │ ├── routes.js │ │ ├── services │ │ │ ├── category.js │ │ │ ├── note.js │ │ │ └── user.js │ │ └── vendor │ │ │ ├── angular-resource.js │ │ │ ├── angular-route.js │ │ │ ├── angular.js │ │ │ ├── bootstrap.js │ │ │ ├── jquery-2.1.3.min.js │ │ │ └── ngGravatar.js │ ├── styles │ │ ├── .keep │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap.css │ │ └── bootstrap.css.map │ └── templates │ │ ├── .keep │ │ ├── directives │ │ ├── nwCategorySelector.html │ │ └── nwPageNav.html │ │ ├── notes │ │ ├── edit.html │ │ ├── index.html │ │ ├── new.html │ │ └── show.html │ │ └── users │ │ ├── index.html │ │ └── show.html └── views │ ├── .keep │ └── index.html ├── npm-shrinkwrap.json ├── package.json └── server ├── expressConfig.js ├── models ├── category.js ├── note.js └── user.js ├── routes.js └── routes ├── category.js ├── note.js └── user.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Staying Sharp with Angular.js Watch Us Build 2 | 3 | *We changed the name of this series to "Watch Us Build," so you may see an occasional reference to its former name ("Soup to Bits") in this repository.* 4 | 5 | This repo contains the app built in the [Watch Us Build episode](https://www.codeschool.com/screencasts/build-a-note-taking-app-with-angularjs) for the [Staying Sharp with Angular.js course](https://www.codeschool.com/courses/staying-sharp-with-angular-js). 6 | 7 | There is a branch called `starting` that can be used as a starting point for the video. Feel free to clone down this repo and follow along. 8 | 9 | # Running the Node App 10 | 11 | I used node `0.10.28` at the time of writing the app. We're only using express and lodash, so any version of node (or io.js) should work. You simply need to type: `npm start` in your terminal to start it up. 12 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var app = require("./server/routes"); 2 | 3 | // Start the server 4 | var server = app.listen(8000, function() { 5 | console.log('Listening on port %d', server.address().port); 6 | }); 7 | -------------------------------------------------------------------------------- /app/assets/javascript/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-NoteTakingAppWithAngularJS/cd926d9a7f670fe748ff338487baf40cd1a39a98/app/assets/javascript/.keep -------------------------------------------------------------------------------- /app/assets/javascript/app.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler', ['ngRoute', 'ngResource', "ngGravatar"]).config(function(GravatarProvider){ 2 | GravatarProvider.setSize(100); 3 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/noteCreateController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('NotesCreateController', function($scope, Note, $location){ 2 | $scope.note = new Note(); 3 | $scope.isSubmitting = false; 4 | 5 | $scope.saveNote = function(note){ 6 | $scope.isSubmitting = true; 7 | note.$save().then(function(){ 8 | $location.path("/notes"); 9 | }).finally(function(){ 10 | $scope.isSubmitting = false; 11 | }); 12 | } 13 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/noteEditController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('NotesEditController', function($scope,User, Note, Category, $routeParams, $location){ 2 | $scope.note = Note.get({id: $routeParams.id}) 3 | $scope.isSubmitting = false; 4 | $scope.categories = Category.query(); 5 | $scope.users = User.query(); 6 | 7 | $scope.saveNote = function(note){ 8 | $scope.isSubmitting = true; 9 | 10 | note.$update().finally(function(){ 11 | $scope.isSubmitting = false; 12 | $location.path("/notes/" + note.id); 13 | }); 14 | } 15 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/noteIndexController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('NotesIndexController', function(Note, $scope){ 2 | $scope.notes = Note.query(); 3 | $scope.search = {}; 4 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/noteShowController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('NotesShowController', function(Note, $scope, $routeParams, $location){ 2 | $scope.note = Note.get({id: $routeParams.id}); 3 | 4 | $scope.deleteNote = function(note){ 5 | note.$remove().then(function(){ 6 | $location.path('/notes'); 7 | }); 8 | } 9 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/usersIndexController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('UsersIndexController', function(User, $scope){ 2 | $scope.users = User.query(); 3 | }); -------------------------------------------------------------------------------- /app/assets/javascript/controllers/usersShowController.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').controller('UsersShowController', function(User, $scope, $routeParams){ 2 | $scope.user = User.get({id: $routeParams.id}); 3 | }); -------------------------------------------------------------------------------- /app/assets/javascript/directives/nwCategorySelector.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').directive("nwCategorySelector", function(Category){ 2 | return { 3 | replace: true, 4 | restrict: "E", 5 | require: "?ngModel", 6 | templateUrl: '/assets/templates/directives/nwCategorySelector.html', 7 | link: function(scope, element, attrs, ngModelCtrl){ 8 | var activeCategory = {}; 9 | scope.categories = Category.query(); 10 | 11 | scope.isActive = function(category){ 12 | return activeCategory && activeCategory.id === category.id; 13 | } 14 | 15 | scope.toggleCategory = function(category){ 16 | if(category === activeCategory) { 17 | activeCategory = {}; 18 | } else { 19 | activeCategory = category; 20 | } 21 | 22 | ngModelCtrl.$setViewValue(activeCategory); 23 | } 24 | 25 | ngModelCtrl.$render = function() { 26 | activeCategory = ngModelCtrl.$viewValue; 27 | } 28 | } 29 | }; 30 | }); -------------------------------------------------------------------------------- /app/assets/javascript/directives/nwPageNav.js: -------------------------------------------------------------------------------- 1 | angular.module("NoteWrangler").directive('nwPageNav', function(){ 2 | return { 3 | replace: true, 4 | restrict: "E", 5 | templateUrl: "assets/templates/directives/nwPageNav.html", 6 | controller: function($scope, $location){ 7 | $scope.isPage = function(name){ 8 | return new RegExp("/" + name + "($|/)").test($location.path()); 9 | }; 10 | } 11 | }; 12 | }); -------------------------------------------------------------------------------- /app/assets/javascript/directives/title.js: -------------------------------------------------------------------------------- 1 | angular.module("NoteWrangler").directive('title', function($timeout){ 2 | return function(scope, element, attrs) { 3 | $timeout(function(){ 4 | $(element).tooltip() 5 | }); 6 | 7 | scope.$on('$destroy', function(){ 8 | $(element).tooltip('destroy'); 9 | }) 10 | } 11 | }); -------------------------------------------------------------------------------- /app/assets/javascript/filters/categoryFilter.js: -------------------------------------------------------------------------------- 1 | angular.module("NoteWrangler").filter('categoryFilter', function(){ 2 | return function(collection, category){ 3 | var newCollection = []; 4 | 5 | if(category && category.id) { 6 | for(var i=0, l=collection.length; i < l; i++){ 7 | if(collection[i].categoryId === category.id) { 8 | newCollection.push(collection[i]); 9 | } 10 | } 11 | 12 | return newCollection; 13 | } else { 14 | return collection; 15 | } 16 | } 17 | }); -------------------------------------------------------------------------------- /app/assets/javascript/routes.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').config(function($routeProvider){ 2 | $routeProvider 3 | .when('/', { 4 | redirectTo: '/notes' 5 | }) 6 | 7 | .when('/notes', { 8 | templateUrl: "assets/templates/notes/index.html", 9 | controller: "NotesIndexController" 10 | }) 11 | 12 | .when('/notes/new', { 13 | templateUrl: "assets/templates/notes/new.html", 14 | controller: "NotesCreateController" 15 | }) 16 | 17 | .when('/notes/:id', { 18 | templateUrl: "assets/templates/notes/show.html", 19 | controller: "NotesShowController" 20 | }) 21 | 22 | .when('/notes/:id/edit', { 23 | templateUrl: "assets/templates/notes/edit.html", 24 | controller: "NotesEditController" 25 | }) 26 | 27 | .when('/users', { 28 | templateUrl: "assets/templates/users/index.html", 29 | controller: "UsersIndexController" 30 | }) 31 | 32 | .when('/users/:id', { 33 | templateUrl: "assets/templates/users/show.html", 34 | controller: "UsersShowController" 35 | }); 36 | 37 | }); -------------------------------------------------------------------------------- /app/assets/javascript/services/category.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').factory('Category', function($resource){ 2 | return $resource('/categories/:id'); 3 | }); -------------------------------------------------------------------------------- /app/assets/javascript/services/note.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').factory('Note', function($resource){ 2 | return $resource('/notes/:id', {id: "@id"}, { 3 | update: { 4 | method: "PUT" 5 | } 6 | }); 7 | }); -------------------------------------------------------------------------------- /app/assets/javascript/services/user.js: -------------------------------------------------------------------------------- 1 | angular.module('NoteWrangler').factory('User', function($resource){ 2 | return $resource('/users/:id'); 3 | }); -------------------------------------------------------------------------------- /app/assets/javascript/vendor/angular-resource.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.3.9 3 | * (c) 2010-2014 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) {'use strict'; 7 | 8 | var $resourceMinErr = angular.$$minErr('$resource'); 9 | 10 | // Helper functions and regex to lookup a dotted path on an object 11 | // stopping at undefined/null. The path must be composed of ASCII 12 | // identifiers (just like $parse) 13 | var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/; 14 | 15 | function isValidDottedPath(path) { 16 | return (path != null && path !== '' && path !== 'hasOwnProperty' && 17 | MEMBER_NAME_REGEX.test('.' + path)); 18 | } 19 | 20 | function lookupDottedPath(obj, path) { 21 | if (!isValidDottedPath(path)) { 22 | throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path); 23 | } 24 | var keys = path.split('.'); 25 | for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) { 26 | var key = keys[i]; 27 | obj = (obj !== null) ? obj[key] : undefined; 28 | } 29 | return obj; 30 | } 31 | 32 | /** 33 | * Create a shallow copy of an object and clear other fields from the destination 34 | */ 35 | function shallowClearAndCopy(src, dst) { 36 | dst = dst || {}; 37 | 38 | angular.forEach(dst, function(value, key) { 39 | delete dst[key]; 40 | }); 41 | 42 | for (var key in src) { 43 | if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { 44 | dst[key] = src[key]; 45 | } 46 | } 47 | 48 | return dst; 49 | } 50 | 51 | /** 52 | * @ngdoc module 53 | * @name ngResource 54 | * @description 55 | * 56 | * # ngResource 57 | * 58 | * The `ngResource` module provides interaction support with RESTful services 59 | * via the $resource service. 60 | * 61 | * 62 | *
63 | * 64 | * See {@link ngResource.$resource `$resource`} for usage. 65 | */ 66 | 67 | /** 68 | * @ngdoc service 69 | * @name $resource 70 | * @requires $http 71 | * 72 | * @description 73 | * A factory which creates a resource object that lets you interact with 74 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. 75 | * 76 | * The returned resource object has action methods which provide high-level behaviors without 77 | * the need to interact with the low level {@link ng.$http $http} service. 78 | * 79 | * Requires the {@link ngResource `ngResource`} module to be installed. 80 | * 81 | * By default, trailing slashes will be stripped from the calculated URLs, 82 | * which can pose problems with server backends that do not expect that 83 | * behavior. This can be disabled by configuring the `$resourceProvider` like 84 | * this: 85 | * 86 | * ```js 87 | app.config(['$resourceProvider', function($resourceProvider) { 88 | // Don't strip trailing slashes from calculated URLs 89 | $resourceProvider.defaults.stripTrailingSlashes = false; 90 | }]); 91 | * ``` 92 | * 93 | * @param {string} url A parametrized URL template with parameters prefixed by `:` as in 94 | * `/user/:username`. If you are using a URL with a port number (e.g. 95 | * `http://example.com:8080/api`), it will be respected. 96 | * 97 | * If you are using a url with a suffix, just add the suffix, like this: 98 | * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')` 99 | * or even `$resource('http://example.com/resource/:resource_id.:format')` 100 | * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be 101 | * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you 102 | * can escape it with `/\.`. 103 | * 104 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in 105 | * `actions` methods. If any of the parameter value is a function, it will be executed every time 106 | * when a param value needs to be obtained for a request (unless the param was overridden). 107 | * 108 | * Each key value in the parameter object is first bound to url template if present and then any 109 | * excess keys are appended to the url search query after the `?`. 110 | * 111 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in 112 | * URL `/path/greet?salutation=Hello`. 113 | * 114 | * If the parameter value is prefixed with `@` then the value for that parameter will be extracted 115 | * from the corresponding property on the `data` object (provided when calling an action method). For 116 | * example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam` 117 | * will be `data.someProp`. 118 | * 119 | * @param {Object.=} actions Hash with declaration of custom actions that should extend 120 | * the default set of resource actions. The declaration should be created in the format of {@link 121 | * ng.$http#usage $http.config}: 122 | * 123 | * {action1: {method:?, params:?, isArray:?, headers:?, ...}, 124 | * action2: {method:?, params:?, isArray:?, headers:?, ...}, 125 | * ...} 126 | * 127 | * Where: 128 | * 129 | * - **`action`** – {string} – The name of action. This name becomes the name of the method on 130 | * your resource object. 131 | * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`, 132 | * `DELETE`, `JSONP`, etc). 133 | * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of 134 | * the parameter value is a function, it will be executed every time when a param value needs to 135 | * be obtained for a request (unless the param was overridden). 136 | * - **`url`** – {string} – action specific `url` override. The url templating is supported just 137 | * like for the resource-level urls. 138 | * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, 139 | * see `returns` section. 140 | * - **`transformRequest`** – 141 | * `{function(data, headersGetter)|Array.}` – 142 | * transform function or an array of such functions. The transform function takes the http 143 | * request body and headers and returns its transformed (typically serialized) version. 144 | * By default, transformRequest will contain one function that checks if the request data is 145 | * an object and serializes to using `angular.toJson`. To prevent this behavior, set 146 | * `transformRequest` to an empty array: `transformRequest: []` 147 | * - **`transformResponse`** – 148 | * `{function(data, headersGetter)|Array.}` – 149 | * transform function or an array of such functions. The transform function takes the http 150 | * response body and headers and returns its transformed (typically deserialized) version. 151 | * By default, transformResponse will contain one function that checks if the response looks like 152 | * a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set 153 | * `transformResponse` to an empty array: `transformResponse: []` 154 | * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the 155 | * GET request, otherwise if a cache instance built with 156 | * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for 157 | * caching. 158 | * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that 159 | * should abort the request when resolved. 160 | * - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the 161 | * XHR object. See 162 | * [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5) 163 | * for more information. 164 | * - **`responseType`** - `{string}` - see 165 | * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). 166 | * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods - 167 | * `response` and `responseError`. Both `response` and `responseError` interceptors get called 168 | * with `http response` object. See {@link ng.$http $http interceptors}. 169 | * 170 | * @param {Object} options Hash with custom settings that should extend the 171 | * default `$resourceProvider` behavior. The only supported option is 172 | * 173 | * Where: 174 | * 175 | * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing 176 | * slashes from any calculated URL will be stripped. (Defaults to true.) 177 | * 178 | * @returns {Object} A resource "class" object with methods for the default set of resource actions 179 | * optionally extended with custom `actions`. The default set contains these actions: 180 | * ```js 181 | * { 'get': {method:'GET'}, 182 | * 'save': {method:'POST'}, 183 | * 'query': {method:'GET', isArray:true}, 184 | * 'remove': {method:'DELETE'}, 185 | * 'delete': {method:'DELETE'} }; 186 | * ``` 187 | * 188 | * Calling these methods invoke an {@link ng.$http} with the specified http method, 189 | * destination and parameters. When the data is returned from the server then the object is an 190 | * instance of the resource class. The actions `save`, `remove` and `delete` are available on it 191 | * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, 192 | * read, update, delete) on server-side data like this: 193 | * ```js 194 | * var User = $resource('/user/:userId', {userId:'@id'}); 195 | * var user = User.get({userId:123}, function() { 196 | * user.abc = true; 197 | * user.$save(); 198 | * }); 199 | * ``` 200 | * 201 | * It is important to realize that invoking a $resource object method immediately returns an 202 | * empty reference (object or array depending on `isArray`). Once the data is returned from the 203 | * server the existing reference is populated with the actual data. This is a useful trick since 204 | * usually the resource is assigned to a model which is then rendered by the view. Having an empty 205 | * object results in no rendering, once the data arrives from the server then the object is 206 | * populated with the data and the view automatically re-renders itself showing the new data. This 207 | * means that in most cases one never has to write a callback function for the action methods. 208 | * 209 | * The action methods on the class object or instance object can be invoked with the following 210 | * parameters: 211 | * 212 | * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` 213 | * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` 214 | * - non-GET instance actions: `instance.$action([parameters], [success], [error])` 215 | * 216 | * Success callback is called with (value, responseHeaders) arguments. Error callback is called 217 | * with (httpResponse) argument. 218 | * 219 | * Class actions return empty instance (with additional properties below). 220 | * Instance actions return promise of the action. 221 | * 222 | * The Resource instances and collection have these additional properties: 223 | * 224 | * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this 225 | * instance or collection. 226 | * 227 | * On success, the promise is resolved with the same resource instance or collection object, 228 | * updated with data from server. This makes it easy to use in 229 | * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view 230 | * rendering until the resource(s) are loaded. 231 | * 232 | * On failure, the promise is resolved with the {@link ng.$http http response} object, without 233 | * the `resource` property. 234 | * 235 | * If an interceptor object was provided, the promise will instead be resolved with the value 236 | * returned by the interceptor. 237 | * 238 | * - `$resolved`: `true` after first server interaction is completed (either with success or 239 | * rejection), `false` before that. Knowing if the Resource has been resolved is useful in 240 | * data-binding. 241 | * 242 | * @example 243 | * 244 | * # Credit card resource 245 | * 246 | * ```js 247 | // Define CreditCard class 248 | var CreditCard = $resource('/user/:userId/card/:cardId', 249 | {userId:123, cardId:'@id'}, { 250 | charge: {method:'POST', params:{charge:true}} 251 | }); 252 | 253 | // We can retrieve a collection from the server 254 | var cards = CreditCard.query(function() { 255 | // GET: /user/123/card 256 | // server returns: [ {id:456, number:'1234', name:'Smith'} ]; 257 | 258 | var card = cards[0]; 259 | // each item is an instance of CreditCard 260 | expect(card instanceof CreditCard).toEqual(true); 261 | card.name = "J. Smith"; 262 | // non GET methods are mapped onto the instances 263 | card.$save(); 264 | // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'} 265 | // server returns: {id:456, number:'1234', name: 'J. Smith'}; 266 | 267 | // our custom method is mapped as well. 268 | card.$charge({amount:9.99}); 269 | // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'} 270 | }); 271 | 272 | // we can create an instance as well 273 | var newCard = new CreditCard({number:'0123'}); 274 | newCard.name = "Mike Smith"; 275 | newCard.$save(); 276 | // POST: /user/123/card {number:'0123', name:'Mike Smith'} 277 | // server returns: {id:789, number:'0123', name: 'Mike Smith'}; 278 | expect(newCard.id).toEqual(789); 279 | * ``` 280 | * 281 | * The object returned from this function execution is a resource "class" which has "static" method 282 | * for each action in the definition. 283 | * 284 | * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and 285 | * `headers`. 286 | * When the data is returned from the server then the object is an instance of the resource type and 287 | * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD 288 | * operations (create, read, update, delete) on server-side data. 289 | 290 | ```js 291 | var User = $resource('/user/:userId', {userId:'@id'}); 292 | User.get({userId:123}, function(user) { 293 | user.abc = true; 294 | user.$save(); 295 | }); 296 | ``` 297 | * 298 | * It's worth noting that the success callback for `get`, `query` and other methods gets passed 299 | * in the response that came from the server as well as $http header getter function, so one 300 | * could rewrite the above example and get access to http headers as: 301 | * 302 | ```js 303 | var User = $resource('/user/:userId', {userId:'@id'}); 304 | User.get({userId:123}, function(u, getResponseHeaders){ 305 | u.abc = true; 306 | u.$save(function(u, putResponseHeaders) { 307 | //u => saved user object 308 | //putResponseHeaders => $http header getter 309 | }); 310 | }); 311 | ``` 312 | * 313 | * You can also access the raw `$http` promise via the `$promise` property on the object returned 314 | * 315 | ``` 316 | var User = $resource('/user/:userId', {userId:'@id'}); 317 | User.get({userId:123}) 318 | .$promise.then(function(user) { 319 | $scope.user = user; 320 | }); 321 | ``` 322 | 323 | * # Creating a custom 'PUT' request 324 | * In this example we create a custom method on our resource to make a PUT request 325 | * ```js 326 | * var app = angular.module('app', ['ngResource', 'ngRoute']); 327 | * 328 | * // Some APIs expect a PUT request in the format URL/object/ID 329 | * // Here we are creating an 'update' method 330 | * app.factory('Notes', ['$resource', function($resource) { 331 | * return $resource('/notes/:id', null, 332 | * { 333 | * 'update': { method:'PUT' } 334 | * }); 335 | * }]); 336 | * 337 | * // In our controller we get the ID from the URL using ngRoute and $routeParams 338 | * // We pass in $routeParams and our Notes factory along with $scope 339 | * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes', 340 | function($scope, $routeParams, Notes) { 341 | * // First get a note object from the factory 342 | * var note = Notes.get({ id:$routeParams.id }); 343 | * $id = note.id; 344 | * 345 | * // Now call update passing in the ID first then the object you are updating 346 | * Notes.update({ id:$id }, note); 347 | * 348 | * // This will PUT /notes/ID with the note object in the request payload 349 | * }]); 350 | * ``` 351 | */ 352 | angular.module('ngResource', ['ng']). 353 | provider('$resource', function() { 354 | var provider = this; 355 | 356 | this.defaults = { 357 | // Strip slashes by default 358 | stripTrailingSlashes: true, 359 | 360 | // Default actions configuration 361 | actions: { 362 | 'get': {method: 'GET'}, 363 | 'save': {method: 'POST'}, 364 | 'query': {method: 'GET', isArray: true}, 365 | 'remove': {method: 'DELETE'}, 366 | 'delete': {method: 'DELETE'} 367 | } 368 | }; 369 | 370 | this.$get = ['$http', '$q', function($http, $q) { 371 | 372 | var noop = angular.noop, 373 | forEach = angular.forEach, 374 | extend = angular.extend, 375 | copy = angular.copy, 376 | isFunction = angular.isFunction; 377 | 378 | /** 379 | * We need our custom method because encodeURIComponent is too aggressive and doesn't follow 380 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set 381 | * (pchar) allowed in path segments: 382 | * segment = *pchar 383 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 384 | * pct-encoded = "%" HEXDIG HEXDIG 385 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 386 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 387 | * / "*" / "+" / "," / ";" / "=" 388 | */ 389 | function encodeUriSegment(val) { 390 | return encodeUriQuery(val, true). 391 | replace(/%26/gi, '&'). 392 | replace(/%3D/gi, '='). 393 | replace(/%2B/gi, '+'); 394 | } 395 | 396 | 397 | /** 398 | * This method is intended for encoding *key* or *value* parts of query component. We need a 399 | * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't 400 | * have to be encoded per http://tools.ietf.org/html/rfc3986: 401 | * query = *( pchar / "/" / "?" ) 402 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 403 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 404 | * pct-encoded = "%" HEXDIG HEXDIG 405 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 406 | * / "*" / "+" / "," / ";" / "=" 407 | */ 408 | function encodeUriQuery(val, pctEncodeSpaces) { 409 | return encodeURIComponent(val). 410 | replace(/%40/gi, '@'). 411 | replace(/%3A/gi, ':'). 412 | replace(/%24/g, '$'). 413 | replace(/%2C/gi, ','). 414 | replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); 415 | } 416 | 417 | function Route(template, defaults) { 418 | this.template = template; 419 | this.defaults = extend({}, provider.defaults, defaults); 420 | this.urlParams = {}; 421 | } 422 | 423 | Route.prototype = { 424 | setUrlParams: function(config, params, actionUrl) { 425 | var self = this, 426 | url = actionUrl || self.template, 427 | val, 428 | encodedVal; 429 | 430 | var urlParams = self.urlParams = {}; 431 | forEach(url.split(/\W/), function(param) { 432 | if (param === 'hasOwnProperty') { 433 | throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); 434 | } 435 | if (!(new RegExp("^\\d+$").test(param)) && param && 436 | (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { 437 | urlParams[param] = true; 438 | } 439 | }); 440 | url = url.replace(/\\:/g, ':'); 441 | 442 | params = params || {}; 443 | forEach(self.urlParams, function(_, urlParam) { 444 | val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; 445 | if (angular.isDefined(val) && val !== null) { 446 | encodedVal = encodeUriSegment(val); 447 | url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) { 448 | return encodedVal + p1; 449 | }); 450 | } else { 451 | url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match, 452 | leadingSlashes, tail) { 453 | if (tail.charAt(0) == '/') { 454 | return tail; 455 | } else { 456 | return leadingSlashes + tail; 457 | } 458 | }); 459 | } 460 | }); 461 | 462 | // strip trailing slashes and set the url (unless this behavior is specifically disabled) 463 | if (self.defaults.stripTrailingSlashes) { 464 | url = url.replace(/\/+$/, '') || '/'; 465 | } 466 | 467 | // then replace collapse `/.` if found in the last URL path segment before the query 468 | // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x` 469 | url = url.replace(/\/\.(?=\w+($|\?))/, '.'); 470 | // replace escaped `/\.` with `/.` 471 | config.url = url.replace(/\/\\\./, '/.'); 472 | 473 | 474 | // set params - delegate param encoding to $http 475 | forEach(params, function(value, key) { 476 | if (!self.urlParams[key]) { 477 | config.params = config.params || {}; 478 | config.params[key] = value; 479 | } 480 | }); 481 | } 482 | }; 483 | 484 | 485 | function resourceFactory(url, paramDefaults, actions, options) { 486 | var route = new Route(url, options); 487 | 488 | actions = extend({}, provider.defaults.actions, actions); 489 | 490 | function extractParams(data, actionParams) { 491 | var ids = {}; 492 | actionParams = extend({}, paramDefaults, actionParams); 493 | forEach(actionParams, function(value, key) { 494 | if (isFunction(value)) { value = value(); } 495 | ids[key] = value && value.charAt && value.charAt(0) == '@' ? 496 | lookupDottedPath(data, value.substr(1)) : value; 497 | }); 498 | return ids; 499 | } 500 | 501 | function defaultResponseInterceptor(response) { 502 | return response.resource; 503 | } 504 | 505 | function Resource(value) { 506 | shallowClearAndCopy(value || {}, this); 507 | } 508 | 509 | Resource.prototype.toJSON = function() { 510 | var data = extend({}, this); 511 | delete data.$promise; 512 | delete data.$resolved; 513 | return data; 514 | }; 515 | 516 | forEach(actions, function(action, name) { 517 | var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); 518 | 519 | Resource[name] = function(a1, a2, a3, a4) { 520 | var params = {}, data, success, error; 521 | 522 | /* jshint -W086 */ /* (purposefully fall through case statements) */ 523 | switch (arguments.length) { 524 | case 4: 525 | error = a4; 526 | success = a3; 527 | //fallthrough 528 | case 3: 529 | case 2: 530 | if (isFunction(a2)) { 531 | if (isFunction(a1)) { 532 | success = a1; 533 | error = a2; 534 | break; 535 | } 536 | 537 | success = a2; 538 | error = a3; 539 | //fallthrough 540 | } else { 541 | params = a1; 542 | data = a2; 543 | success = a3; 544 | break; 545 | } 546 | case 1: 547 | if (isFunction(a1)) success = a1; 548 | else if (hasBody) data = a1; 549 | else params = a1; 550 | break; 551 | case 0: break; 552 | default: 553 | throw $resourceMinErr('badargs', 554 | "Expected up to 4 arguments [params, data, success, error], got {0} arguments", 555 | arguments.length); 556 | } 557 | /* jshint +W086 */ /* (purposefully fall through case statements) */ 558 | 559 | var isInstanceCall = this instanceof Resource; 560 | var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data)); 561 | var httpConfig = {}; 562 | var responseInterceptor = action.interceptor && action.interceptor.response || 563 | defaultResponseInterceptor; 564 | var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || 565 | undefined; 566 | 567 | forEach(action, function(value, key) { 568 | if (key != 'params' && key != 'isArray' && key != 'interceptor') { 569 | httpConfig[key] = copy(value); 570 | } 571 | }); 572 | 573 | if (hasBody) httpConfig.data = data; 574 | route.setUrlParams(httpConfig, 575 | extend({}, extractParams(data, action.params || {}), params), 576 | action.url); 577 | 578 | var promise = $http(httpConfig).then(function(response) { 579 | var data = response.data, 580 | promise = value.$promise; 581 | 582 | if (data) { 583 | // Need to convert action.isArray to boolean in case it is undefined 584 | // jshint -W018 585 | if (angular.isArray(data) !== (!!action.isArray)) { 586 | throw $resourceMinErr('badcfg', 587 | 'Error in resource configuration for action `{0}`. Expected response to ' + 588 | 'contain an {1} but got an {2}', name, action.isArray ? 'array' : 'object', 589 | angular.isArray(data) ? 'array' : 'object'); 590 | } 591 | // jshint +W018 592 | if (action.isArray) { 593 | value.length = 0; 594 | forEach(data, function(item) { 595 | if (typeof item === "object") { 596 | value.push(new Resource(item)); 597 | } else { 598 | // Valid JSON values may be string literals, and these should not be converted 599 | // into objects. These items will not have access to the Resource prototype 600 | // methods, but unfortunately there 601 | value.push(item); 602 | } 603 | }); 604 | } else { 605 | shallowClearAndCopy(data, value); 606 | value.$promise = promise; 607 | } 608 | } 609 | 610 | value.$resolved = true; 611 | 612 | response.resource = value; 613 | 614 | return response; 615 | }, function(response) { 616 | value.$resolved = true; 617 | 618 | (error || noop)(response); 619 | 620 | return $q.reject(response); 621 | }); 622 | 623 | promise = promise.then( 624 | function(response) { 625 | var value = responseInterceptor(response); 626 | (success || noop)(value, response.headers); 627 | return value; 628 | }, 629 | responseErrorInterceptor); 630 | 631 | if (!isInstanceCall) { 632 | // we are creating instance / collection 633 | // - set the initial promise 634 | // - return the instance / collection 635 | value.$promise = promise; 636 | value.$resolved = false; 637 | 638 | return value; 639 | } 640 | 641 | // instance call 642 | return promise; 643 | }; 644 | 645 | 646 | Resource.prototype['$' + name] = function(params, success, error) { 647 | if (isFunction(params)) { 648 | error = success; success = params; params = {}; 649 | } 650 | var result = Resource[name].call(this, params, this, success, error); 651 | return result.$promise || result; 652 | }; 653 | }); 654 | 655 | Resource.bind = function(additionalParamDefaults) { 656 | return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); 657 | }; 658 | 659 | return Resource; 660 | } 661 | 662 | return resourceFactory; 663 | }]; 664 | }); 665 | 666 | 667 | })(window, window.angular); 668 | -------------------------------------------------------------------------------- /app/assets/javascript/vendor/angular-route.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.3.9 3 | * (c) 2010-2014 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) {'use strict'; 7 | 8 | /** 9 | * @ngdoc module 10 | * @name ngRoute 11 | * @description 12 | * 13 | * # ngRoute 14 | * 15 | * The `ngRoute` module provides routing and deeplinking services and directives for angular apps. 16 | * 17 | * ## Example 18 | * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. 19 | * 20 | * 21 | *
22 | */ 23 | /* global -ngRouteModule */ 24 | var ngRouteModule = angular.module('ngRoute', ['ng']). 25 | provider('$route', $RouteProvider), 26 | $routeMinErr = angular.$$minErr('ngRoute'); 27 | 28 | /** 29 | * @ngdoc provider 30 | * @name $routeProvider 31 | * 32 | * @description 33 | * 34 | * Used for configuring routes. 35 | * 36 | * ## Example 37 | * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. 38 | * 39 | * ## Dependencies 40 | * Requires the {@link ngRoute `ngRoute`} module to be installed. 41 | */ 42 | function $RouteProvider() { 43 | function inherit(parent, extra) { 44 | return angular.extend(Object.create(parent), extra); 45 | } 46 | 47 | var routes = {}; 48 | 49 | /** 50 | * @ngdoc method 51 | * @name $routeProvider#when 52 | * 53 | * @param {string} path Route path (matched against `$location.path`). If `$location.path` 54 | * contains redundant trailing slash or is missing one, the route will still match and the 55 | * `$location.path` will be updated to add or drop the trailing slash to exactly match the 56 | * route definition. 57 | * 58 | * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up 59 | * to the next slash are matched and stored in `$routeParams` under the given `name` 60 | * when the route matches. 61 | * * `path` can contain named groups starting with a colon and ending with a star: 62 | * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` 63 | * when the route matches. 64 | * * `path` can contain optional named groups with a question mark: e.g.`:name?`. 65 | * 66 | * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match 67 | * `/color/brown/largecode/code/with/slashes/edit` and extract: 68 | * 69 | * * `color: brown` 70 | * * `largecode: code/with/slashes`. 71 | * 72 | * 73 | * @param {Object} route Mapping information to be assigned to `$route.current` on route 74 | * match. 75 | * 76 | * Object properties: 77 | * 78 | * - `controller` – `{(string|function()=}` – Controller fn that should be associated with 79 | * newly created scope or the name of a {@link angular.Module#controller registered 80 | * controller} if passed as a string. 81 | * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be 82 | * published to scope under the `controllerAs` name. 83 | * - `template` – `{string=|function()=}` – html template as a string or a function that 84 | * returns an html template as a string which should be used by {@link 85 | * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. 86 | * This property takes precedence over `templateUrl`. 87 | * 88 | * If `template` is a function, it will be called with the following parameters: 89 | * 90 | * - `{Array.}` - route parameters extracted from the current 91 | * `$location.path()` by applying the current route 92 | * 93 | * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html 94 | * template that should be used by {@link ngRoute.directive:ngView ngView}. 95 | * 96 | * If `templateUrl` is a function, it will be called with the following parameters: 97 | * 98 | * - `{Array.}` - route parameters extracted from the current 99 | * `$location.path()` by applying the current route 100 | * 101 | * - `resolve` - `{Object.=}` - An optional map of dependencies which should 102 | * be injected into the controller. If any of these dependencies are promises, the router 103 | * will wait for them all to be resolved or one to be rejected before the controller is 104 | * instantiated. 105 | * If all the promises are resolved successfully, the values of the resolved promises are 106 | * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is 107 | * fired. If any of the promises are rejected the 108 | * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object 109 | * is: 110 | * 111 | * - `key` – `{string}`: a name of a dependency to be injected into the controller. 112 | * - `factory` - `{string|function}`: If `string` then it is an alias for a service. 113 | * Otherwise if function, then it is {@link auto.$injector#invoke injected} 114 | * and the return value is treated as the dependency. If the result is a promise, it is 115 | * resolved before its value is injected into the controller. Be aware that 116 | * `ngRoute.$routeParams` will still refer to the previous route within these resolve 117 | * functions. Use `$route.current.params` to access the new route parameters, instead. 118 | * 119 | * - `redirectTo` – {(string|function())=} – value to update 120 | * {@link ng.$location $location} path with and trigger route redirection. 121 | * 122 | * If `redirectTo` is a function, it will be called with the following parameters: 123 | * 124 | * - `{Object.}` - route parameters extracted from the current 125 | * `$location.path()` by applying the current route templateUrl. 126 | * - `{string}` - current `$location.path()` 127 | * - `{Object}` - current `$location.search()` 128 | * 129 | * The custom `redirectTo` function is expected to return a string which will be used 130 | * to update `$location.path()` and `$location.search()`. 131 | * 132 | * - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()` 133 | * or `$location.hash()` changes. 134 | * 135 | * If the option is set to `false` and url in the browser changes, then 136 | * `$routeUpdate` event is broadcasted on the root scope. 137 | * 138 | * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive 139 | * 140 | * If the option is set to `true`, then the particular route can be matched without being 141 | * case sensitive 142 | * 143 | * @returns {Object} self 144 | * 145 | * @description 146 | * Adds a new route definition to the `$route` service. 147 | */ 148 | this.when = function(path, route) { 149 | //copy original route object to preserve params inherited from proto chain 150 | var routeCopy = angular.copy(route); 151 | if (angular.isUndefined(routeCopy.reloadOnSearch)) { 152 | routeCopy.reloadOnSearch = true; 153 | } 154 | if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { 155 | routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; 156 | } 157 | routes[path] = angular.extend( 158 | routeCopy, 159 | path && pathRegExp(path, routeCopy) 160 | ); 161 | 162 | // create redirection for trailing slashes 163 | if (path) { 164 | var redirectPath = (path[path.length - 1] == '/') 165 | ? path.substr(0, path.length - 1) 166 | : path + '/'; 167 | 168 | routes[redirectPath] = angular.extend( 169 | {redirectTo: path}, 170 | pathRegExp(redirectPath, routeCopy) 171 | ); 172 | } 173 | 174 | return this; 175 | }; 176 | 177 | /** 178 | * @ngdoc property 179 | * @name $routeProvider#caseInsensitiveMatch 180 | * @description 181 | * 182 | * A boolean property indicating if routes defined 183 | * using this provider should be matched using a case insensitive 184 | * algorithm. Defaults to `false`. 185 | */ 186 | this.caseInsensitiveMatch = false; 187 | 188 | /** 189 | * @param path {string} path 190 | * @param opts {Object} options 191 | * @return {?Object} 192 | * 193 | * @description 194 | * Normalizes the given path, returning a regular expression 195 | * and the original path. 196 | * 197 | * Inspired by pathRexp in visionmedia/express/lib/utils.js. 198 | */ 199 | function pathRegExp(path, opts) { 200 | var insensitive = opts.caseInsensitiveMatch, 201 | ret = { 202 | originalPath: path, 203 | regexp: path 204 | }, 205 | keys = ret.keys = []; 206 | 207 | path = path 208 | .replace(/([().])/g, '\\$1') 209 | .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) { 210 | var optional = option === '?' ? option : null; 211 | var star = option === '*' ? option : null; 212 | keys.push({ name: key, optional: !!optional }); 213 | slash = slash || ''; 214 | return '' 215 | + (optional ? '' : slash) 216 | + '(?:' 217 | + (optional ? slash : '') 218 | + (star && '(.+?)' || '([^/]+)') 219 | + (optional || '') 220 | + ')' 221 | + (optional || ''); 222 | }) 223 | .replace(/([\/$\*])/g, '\\$1'); 224 | 225 | ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); 226 | return ret; 227 | } 228 | 229 | /** 230 | * @ngdoc method 231 | * @name $routeProvider#otherwise 232 | * 233 | * @description 234 | * Sets route definition that will be used on route change when no other route definition 235 | * is matched. 236 | * 237 | * @param {Object|string} params Mapping information to be assigned to `$route.current`. 238 | * If called with a string, the value maps to `redirectTo`. 239 | * @returns {Object} self 240 | */ 241 | this.otherwise = function(params) { 242 | if (typeof params === 'string') { 243 | params = {redirectTo: params}; 244 | } 245 | this.when(null, params); 246 | return this; 247 | }; 248 | 249 | 250 | this.$get = ['$rootScope', 251 | '$location', 252 | '$routeParams', 253 | '$q', 254 | '$injector', 255 | '$templateRequest', 256 | '$sce', 257 | function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) { 258 | 259 | /** 260 | * @ngdoc service 261 | * @name $route 262 | * @requires $location 263 | * @requires $routeParams 264 | * 265 | * @property {Object} current Reference to the current route definition. 266 | * The route definition contains: 267 | * 268 | * - `controller`: The controller constructor as define in route definition. 269 | * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for 270 | * controller instantiation. The `locals` contain 271 | * the resolved values of the `resolve` map. Additionally the `locals` also contain: 272 | * 273 | * - `$scope` - The current route scope. 274 | * - `$template` - The current route template HTML. 275 | * 276 | * @property {Object} routes Object with all route configuration Objects as its properties. 277 | * 278 | * @description 279 | * `$route` is used for deep-linking URLs to controllers and views (HTML partials). 280 | * It watches `$location.url()` and tries to map the path to an existing route definition. 281 | * 282 | * Requires the {@link ngRoute `ngRoute`} module to be installed. 283 | * 284 | * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. 285 | * 286 | * The `$route` service is typically used in conjunction with the 287 | * {@link ngRoute.directive:ngView `ngView`} directive and the 288 | * {@link ngRoute.$routeParams `$routeParams`} service. 289 | * 290 | * @example 291 | * This example shows how changing the URL hash causes the `$route` to match a route against the 292 | * URL, and the `ngView` pulls in the partial. 293 | * 294 | * 296 | * 297 | *
298 | * Choose: 299 | * Moby | 300 | * Moby: Ch1 | 301 | * Gatsby | 302 | * Gatsby: Ch4 | 303 | * Scarlet Letter
304 | * 305 | *
306 | * 307 | *
308 | * 309 | *
$location.path() = {{$location.path()}}
310 | *
$route.current.templateUrl = {{$route.current.templateUrl}}
311 | *
$route.current.params = {{$route.current.params}}
312 | *
$route.current.scope.name = {{$route.current.scope.name}}
313 | *
$routeParams = {{$routeParams}}
314 | *
315 | *
316 | * 317 | * 318 | * controller: {{name}}
319 | * Book Id: {{params.bookId}}
320 | *
321 | * 322 | * 323 | * controller: {{name}}
324 | * Book Id: {{params.bookId}}
325 | * Chapter Id: {{params.chapterId}} 326 | *
327 | * 328 | * 329 | * angular.module('ngRouteExample', ['ngRoute']) 330 | * 331 | * .controller('MainController', function($scope, $route, $routeParams, $location) { 332 | * $scope.$route = $route; 333 | * $scope.$location = $location; 334 | * $scope.$routeParams = $routeParams; 335 | * }) 336 | * 337 | * .controller('BookController', function($scope, $routeParams) { 338 | * $scope.name = "BookController"; 339 | * $scope.params = $routeParams; 340 | * }) 341 | * 342 | * .controller('ChapterController', function($scope, $routeParams) { 343 | * $scope.name = "ChapterController"; 344 | * $scope.params = $routeParams; 345 | * }) 346 | * 347 | * .config(function($routeProvider, $locationProvider) { 348 | * $routeProvider 349 | * .when('/Book/:bookId', { 350 | * templateUrl: 'book.html', 351 | * controller: 'BookController', 352 | * resolve: { 353 | * // I will cause a 1 second delay 354 | * delay: function($q, $timeout) { 355 | * var delay = $q.defer(); 356 | * $timeout(delay.resolve, 1000); 357 | * return delay.promise; 358 | * } 359 | * } 360 | * }) 361 | * .when('/Book/:bookId/ch/:chapterId', { 362 | * templateUrl: 'chapter.html', 363 | * controller: 'ChapterController' 364 | * }); 365 | * 366 | * // configure html5 to get links working on jsfiddle 367 | * $locationProvider.html5Mode(true); 368 | * }); 369 | * 370 | * 371 | * 372 | * 373 | * it('should load and compile correct template', function() { 374 | * element(by.linkText('Moby: Ch1')).click(); 375 | * var content = element(by.css('[ng-view]')).getText(); 376 | * expect(content).toMatch(/controller\: ChapterController/); 377 | * expect(content).toMatch(/Book Id\: Moby/); 378 | * expect(content).toMatch(/Chapter Id\: 1/); 379 | * 380 | * element(by.partialLinkText('Scarlet')).click(); 381 | * 382 | * content = element(by.css('[ng-view]')).getText(); 383 | * expect(content).toMatch(/controller\: BookController/); 384 | * expect(content).toMatch(/Book Id\: Scarlet/); 385 | * }); 386 | * 387 | *
388 | */ 389 | 390 | /** 391 | * @ngdoc event 392 | * @name $route#$routeChangeStart 393 | * @eventType broadcast on root scope 394 | * @description 395 | * Broadcasted before a route change. At this point the route services starts 396 | * resolving all of the dependencies needed for the route change to occur. 397 | * Typically this involves fetching the view template as well as any dependencies 398 | * defined in `resolve` route property. Once all of the dependencies are resolved 399 | * `$routeChangeSuccess` is fired. 400 | * 401 | * The route change (and the `$location` change that triggered it) can be prevented 402 | * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} 403 | * for more details about event object. 404 | * 405 | * @param {Object} angularEvent Synthetic event object. 406 | * @param {Route} next Future route information. 407 | * @param {Route} current Current route information. 408 | */ 409 | 410 | /** 411 | * @ngdoc event 412 | * @name $route#$routeChangeSuccess 413 | * @eventType broadcast on root scope 414 | * @description 415 | * Broadcasted after a route dependencies are resolved. 416 | * {@link ngRoute.directive:ngView ngView} listens for the directive 417 | * to instantiate the controller and render the view. 418 | * 419 | * @param {Object} angularEvent Synthetic event object. 420 | * @param {Route} current Current route information. 421 | * @param {Route|Undefined} previous Previous route information, or undefined if current is 422 | * first route entered. 423 | */ 424 | 425 | /** 426 | * @ngdoc event 427 | * @name $route#$routeChangeError 428 | * @eventType broadcast on root scope 429 | * @description 430 | * Broadcasted if any of the resolve promises are rejected. 431 | * 432 | * @param {Object} angularEvent Synthetic event object 433 | * @param {Route} current Current route information. 434 | * @param {Route} previous Previous route information. 435 | * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. 436 | */ 437 | 438 | /** 439 | * @ngdoc event 440 | * @name $route#$routeUpdate 441 | * @eventType broadcast on root scope 442 | * @description 443 | * 444 | * The `reloadOnSearch` property has been set to false, and we are reusing the same 445 | * instance of the Controller. 446 | */ 447 | 448 | var forceReload = false, 449 | preparedRoute, 450 | preparedRouteIsUpdateOnly, 451 | $route = { 452 | routes: routes, 453 | 454 | /** 455 | * @ngdoc method 456 | * @name $route#reload 457 | * 458 | * @description 459 | * Causes `$route` service to reload the current route even if 460 | * {@link ng.$location $location} hasn't changed. 461 | * 462 | * As a result of that, {@link ngRoute.directive:ngView ngView} 463 | * creates new scope and reinstantiates the controller. 464 | */ 465 | reload: function() { 466 | forceReload = true; 467 | $rootScope.$evalAsync(function() { 468 | // Don't support cancellation of a reload for now... 469 | prepareRoute(); 470 | commitRoute(); 471 | }); 472 | }, 473 | 474 | /** 475 | * @ngdoc method 476 | * @name $route#updateParams 477 | * 478 | * @description 479 | * Causes `$route` service to update the current URL, replacing 480 | * current route parameters with those specified in `newParams`. 481 | * Provided property names that match the route's path segment 482 | * definitions will be interpolated into the location's path, while 483 | * remaining properties will be treated as query params. 484 | * 485 | * @param {Object} newParams mapping of URL parameter names to values 486 | */ 487 | updateParams: function(newParams) { 488 | if (this.current && this.current.$$route) { 489 | var searchParams = {}, self=this; 490 | 491 | angular.forEach(Object.keys(newParams), function(key) { 492 | if (!self.current.pathParams[key]) searchParams[key] = newParams[key]; 493 | }); 494 | 495 | newParams = angular.extend({}, this.current.params, newParams); 496 | $location.path(interpolate(this.current.$$route.originalPath, newParams)); 497 | $location.search(angular.extend({}, $location.search(), searchParams)); 498 | } 499 | else { 500 | throw $routeMinErr('norout', 'Tried updating route when with no current route'); 501 | } 502 | } 503 | }; 504 | 505 | $rootScope.$on('$locationChangeStart', prepareRoute); 506 | $rootScope.$on('$locationChangeSuccess', commitRoute); 507 | 508 | return $route; 509 | 510 | ///////////////////////////////////////////////////// 511 | 512 | /** 513 | * @param on {string} current url 514 | * @param route {Object} route regexp to match the url against 515 | * @return {?Object} 516 | * 517 | * @description 518 | * Check if the route matches the current url. 519 | * 520 | * Inspired by match in 521 | * visionmedia/express/lib/router/router.js. 522 | */ 523 | function switchRouteMatcher(on, route) { 524 | var keys = route.keys, 525 | params = {}; 526 | 527 | if (!route.regexp) return null; 528 | 529 | var m = route.regexp.exec(on); 530 | if (!m) return null; 531 | 532 | for (var i = 1, len = m.length; i < len; ++i) { 533 | var key = keys[i - 1]; 534 | 535 | var val = m[i]; 536 | 537 | if (key && val) { 538 | params[key.name] = val; 539 | } 540 | } 541 | return params; 542 | } 543 | 544 | function prepareRoute($locationEvent) { 545 | var lastRoute = $route.current; 546 | 547 | preparedRoute = parseRoute(); 548 | preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route 549 | && angular.equals(preparedRoute.pathParams, lastRoute.pathParams) 550 | && !preparedRoute.reloadOnSearch && !forceReload; 551 | 552 | if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) { 553 | if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) { 554 | if ($locationEvent) { 555 | $locationEvent.preventDefault(); 556 | } 557 | } 558 | } 559 | } 560 | 561 | function commitRoute() { 562 | var lastRoute = $route.current; 563 | var nextRoute = preparedRoute; 564 | 565 | if (preparedRouteIsUpdateOnly) { 566 | lastRoute.params = nextRoute.params; 567 | angular.copy(lastRoute.params, $routeParams); 568 | $rootScope.$broadcast('$routeUpdate', lastRoute); 569 | } else if (nextRoute || lastRoute) { 570 | forceReload = false; 571 | $route.current = nextRoute; 572 | if (nextRoute) { 573 | if (nextRoute.redirectTo) { 574 | if (angular.isString(nextRoute.redirectTo)) { 575 | $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params) 576 | .replace(); 577 | } else { 578 | $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search())) 579 | .replace(); 580 | } 581 | } 582 | } 583 | 584 | $q.when(nextRoute). 585 | then(function() { 586 | if (nextRoute) { 587 | var locals = angular.extend({}, nextRoute.resolve), 588 | template, templateUrl; 589 | 590 | angular.forEach(locals, function(value, key) { 591 | locals[key] = angular.isString(value) ? 592 | $injector.get(value) : $injector.invoke(value, null, null, key); 593 | }); 594 | 595 | if (angular.isDefined(template = nextRoute.template)) { 596 | if (angular.isFunction(template)) { 597 | template = template(nextRoute.params); 598 | } 599 | } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) { 600 | if (angular.isFunction(templateUrl)) { 601 | templateUrl = templateUrl(nextRoute.params); 602 | } 603 | templateUrl = $sce.getTrustedResourceUrl(templateUrl); 604 | if (angular.isDefined(templateUrl)) { 605 | nextRoute.loadedTemplateUrl = templateUrl; 606 | template = $templateRequest(templateUrl); 607 | } 608 | } 609 | if (angular.isDefined(template)) { 610 | locals['$template'] = template; 611 | } 612 | return $q.all(locals); 613 | } 614 | }). 615 | // after route change 616 | then(function(locals) { 617 | if (nextRoute == $route.current) { 618 | if (nextRoute) { 619 | nextRoute.locals = locals; 620 | angular.copy(nextRoute.params, $routeParams); 621 | } 622 | $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute); 623 | } 624 | }, function(error) { 625 | if (nextRoute == $route.current) { 626 | $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error); 627 | } 628 | }); 629 | } 630 | } 631 | 632 | 633 | /** 634 | * @returns {Object} the current active route, by matching it against the URL 635 | */ 636 | function parseRoute() { 637 | // Match a route 638 | var params, match; 639 | angular.forEach(routes, function(route, path) { 640 | if (!match && (params = switchRouteMatcher($location.path(), route))) { 641 | match = inherit(route, { 642 | params: angular.extend({}, $location.search(), params), 643 | pathParams: params}); 644 | match.$$route = route; 645 | } 646 | }); 647 | // No route matched; fallback to "otherwise" route 648 | return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); 649 | } 650 | 651 | /** 652 | * @returns {string} interpolation of the redirect path with the parameters 653 | */ 654 | function interpolate(string, params) { 655 | var result = []; 656 | angular.forEach((string || '').split(':'), function(segment, i) { 657 | if (i === 0) { 658 | result.push(segment); 659 | } else { 660 | var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); 661 | var key = segmentMatch[1]; 662 | result.push(params[key]); 663 | result.push(segmentMatch[2] || ''); 664 | delete params[key]; 665 | } 666 | }); 667 | return result.join(''); 668 | } 669 | }]; 670 | } 671 | 672 | ngRouteModule.provider('$routeParams', $RouteParamsProvider); 673 | 674 | 675 | /** 676 | * @ngdoc service 677 | * @name $routeParams 678 | * @requires $route 679 | * 680 | * @description 681 | * The `$routeParams` service allows you to retrieve the current set of route parameters. 682 | * 683 | * Requires the {@link ngRoute `ngRoute`} module to be installed. 684 | * 685 | * The route parameters are a combination of {@link ng.$location `$location`}'s 686 | * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}. 687 | * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched. 688 | * 689 | * In case of parameter name collision, `path` params take precedence over `search` params. 690 | * 691 | * The service guarantees that the identity of the `$routeParams` object will remain unchanged 692 | * (but its properties will likely change) even when a route change occurs. 693 | * 694 | * Note that the `$routeParams` are only updated *after* a route change completes successfully. 695 | * This means that you cannot rely on `$routeParams` being correct in route resolve functions. 696 | * Instead you can use `$route.current.params` to access the new route's parameters. 697 | * 698 | * @example 699 | * ```js 700 | * // Given: 701 | * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby 702 | * // Route: /Chapter/:chapterId/Section/:sectionId 703 | * // 704 | * // Then 705 | * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'} 706 | * ``` 707 | */ 708 | function $RouteParamsProvider() { 709 | this.$get = function() { return {}; }; 710 | } 711 | 712 | ngRouteModule.directive('ngView', ngViewFactory); 713 | ngRouteModule.directive('ngView', ngViewFillContentFactory); 714 | 715 | 716 | /** 717 | * @ngdoc directive 718 | * @name ngView 719 | * @restrict ECA 720 | * 721 | * @description 722 | * # Overview 723 | * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by 724 | * including the rendered template of the current route into the main layout (`index.html`) file. 725 | * Every time the current route changes, the included view changes with it according to the 726 | * configuration of the `$route` service. 727 | * 728 | * Requires the {@link ngRoute `ngRoute`} module to be installed. 729 | * 730 | * @animations 731 | * enter - animation is used to bring new content into the browser. 732 | * leave - animation is used to animate existing content away. 733 | * 734 | * The enter and leave animation occur concurrently. 735 | * 736 | * @scope 737 | * @priority 400 738 | * @param {string=} onload Expression to evaluate whenever the view updates. 739 | * 740 | * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll 741 | * $anchorScroll} to scroll the viewport after the view is updated. 742 | * 743 | * - If the attribute is not set, disable scrolling. 744 | * - If the attribute is set without value, enable scrolling. 745 | * - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated 746 | * as an expression yields a truthy value. 747 | * @example 748 | 751 | 752 |
753 | Choose: 754 | Moby | 755 | Moby: Ch1 | 756 | Gatsby | 757 | Gatsby: Ch4 | 758 | Scarlet Letter
759 | 760 |
761 |
762 |
763 |
764 | 765 |
$location.path() = {{main.$location.path()}}
766 |
$route.current.templateUrl = {{main.$route.current.templateUrl}}
767 |
$route.current.params = {{main.$route.current.params}}
768 |
$routeParams = {{main.$routeParams}}
769 |
770 |
771 | 772 | 773 |
774 | controller: {{book.name}}
775 | Book Id: {{book.params.bookId}}
776 |
777 |
778 | 779 | 780 |
781 | controller: {{chapter.name}}
782 | Book Id: {{chapter.params.bookId}}
783 | Chapter Id: {{chapter.params.chapterId}} 784 |
785 |
786 | 787 | 788 | .view-animate-container { 789 | position:relative; 790 | height:100px!important; 791 | background:white; 792 | border:1px solid black; 793 | height:40px; 794 | overflow:hidden; 795 | } 796 | 797 | .view-animate { 798 | padding:10px; 799 | } 800 | 801 | .view-animate.ng-enter, .view-animate.ng-leave { 802 | -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; 803 | transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; 804 | 805 | display:block; 806 | width:100%; 807 | border-left:1px solid black; 808 | 809 | position:absolute; 810 | top:0; 811 | left:0; 812 | right:0; 813 | bottom:0; 814 | padding:10px; 815 | } 816 | 817 | .view-animate.ng-enter { 818 | left:100%; 819 | } 820 | .view-animate.ng-enter.ng-enter-active { 821 | left:0; 822 | } 823 | .view-animate.ng-leave.ng-leave-active { 824 | left:-100%; 825 | } 826 | 827 | 828 | 829 | angular.module('ngViewExample', ['ngRoute', 'ngAnimate']) 830 | .config(['$routeProvider', '$locationProvider', 831 | function($routeProvider, $locationProvider) { 832 | $routeProvider 833 | .when('/Book/:bookId', { 834 | templateUrl: 'book.html', 835 | controller: 'BookCtrl', 836 | controllerAs: 'book' 837 | }) 838 | .when('/Book/:bookId/ch/:chapterId', { 839 | templateUrl: 'chapter.html', 840 | controller: 'ChapterCtrl', 841 | controllerAs: 'chapter' 842 | }); 843 | 844 | $locationProvider.html5Mode(true); 845 | }]) 846 | .controller('MainCtrl', ['$route', '$routeParams', '$location', 847 | function($route, $routeParams, $location) { 848 | this.$route = $route; 849 | this.$location = $location; 850 | this.$routeParams = $routeParams; 851 | }]) 852 | .controller('BookCtrl', ['$routeParams', function($routeParams) { 853 | this.name = "BookCtrl"; 854 | this.params = $routeParams; 855 | }]) 856 | .controller('ChapterCtrl', ['$routeParams', function($routeParams) { 857 | this.name = "ChapterCtrl"; 858 | this.params = $routeParams; 859 | }]); 860 | 861 | 862 | 863 | 864 | it('should load and compile correct template', function() { 865 | element(by.linkText('Moby: Ch1')).click(); 866 | var content = element(by.css('[ng-view]')).getText(); 867 | expect(content).toMatch(/controller\: ChapterCtrl/); 868 | expect(content).toMatch(/Book Id\: Moby/); 869 | expect(content).toMatch(/Chapter Id\: 1/); 870 | 871 | element(by.partialLinkText('Scarlet')).click(); 872 | 873 | content = element(by.css('[ng-view]')).getText(); 874 | expect(content).toMatch(/controller\: BookCtrl/); 875 | expect(content).toMatch(/Book Id\: Scarlet/); 876 | }); 877 | 878 |
879 | */ 880 | 881 | 882 | /** 883 | * @ngdoc event 884 | * @name ngView#$viewContentLoaded 885 | * @eventType emit on the current ngView scope 886 | * @description 887 | * Emitted every time the ngView content is reloaded. 888 | */ 889 | ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; 890 | function ngViewFactory($route, $anchorScroll, $animate) { 891 | return { 892 | restrict: 'ECA', 893 | terminal: true, 894 | priority: 400, 895 | transclude: 'element', 896 | link: function(scope, $element, attr, ctrl, $transclude) { 897 | var currentScope, 898 | currentElement, 899 | previousLeaveAnimation, 900 | autoScrollExp = attr.autoscroll, 901 | onloadExp = attr.onload || ''; 902 | 903 | scope.$on('$routeChangeSuccess', update); 904 | update(); 905 | 906 | function cleanupLastView() { 907 | if (previousLeaveAnimation) { 908 | $animate.cancel(previousLeaveAnimation); 909 | previousLeaveAnimation = null; 910 | } 911 | 912 | if (currentScope) { 913 | currentScope.$destroy(); 914 | currentScope = null; 915 | } 916 | if (currentElement) { 917 | previousLeaveAnimation = $animate.leave(currentElement); 918 | previousLeaveAnimation.then(function() { 919 | previousLeaveAnimation = null; 920 | }); 921 | currentElement = null; 922 | } 923 | } 924 | 925 | function update() { 926 | var locals = $route.current && $route.current.locals, 927 | template = locals && locals.$template; 928 | 929 | if (angular.isDefined(template)) { 930 | var newScope = scope.$new(); 931 | var current = $route.current; 932 | 933 | // Note: This will also link all children of ng-view that were contained in the original 934 | // html. If that content contains controllers, ... they could pollute/change the scope. 935 | // However, using ng-view on an element with additional content does not make sense... 936 | // Note: We can't remove them in the cloneAttchFn of $transclude as that 937 | // function is called before linking the content, which would apply child 938 | // directives to non existing elements. 939 | var clone = $transclude(newScope, function(clone) { 940 | $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() { 941 | if (angular.isDefined(autoScrollExp) 942 | && (!autoScrollExp || scope.$eval(autoScrollExp))) { 943 | $anchorScroll(); 944 | } 945 | }); 946 | cleanupLastView(); 947 | }); 948 | 949 | currentElement = clone; 950 | currentScope = current.scope = newScope; 951 | currentScope.$emit('$viewContentLoaded'); 952 | currentScope.$eval(onloadExp); 953 | } else { 954 | cleanupLastView(); 955 | } 956 | } 957 | } 958 | }; 959 | } 960 | 961 | // This directive is called during the $transclude call of the first `ngView` directive. 962 | // It will replace and compile the content of the element with the loaded template. 963 | // We need this directive so that the element content is already filled when 964 | // the link function of another directive on the same element as ngView 965 | // is called. 966 | ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; 967 | function ngViewFillContentFactory($compile, $controller, $route) { 968 | return { 969 | restrict: 'ECA', 970 | priority: -400, 971 | link: function(scope, $element) { 972 | var current = $route.current, 973 | locals = current.locals; 974 | 975 | $element.html(locals.$template); 976 | 977 | var link = $compile($element.contents()); 978 | 979 | if (current.controller) { 980 | locals.$scope = scope; 981 | var controller = $controller(current.controller, locals); 982 | if (current.controllerAs) { 983 | scope[current.controllerAs] = controller; 984 | } 985 | $element.data('$ngControllerController', controller); 986 | $element.children().data('$ngControllerController', controller); 987 | } 988 | 989 | link(scope); 990 | } 991 | }; 992 | } 993 | 994 | 995 | })(window, window.angular); 996 | -------------------------------------------------------------------------------- /app/assets/javascript/vendor/ngGravatar.js: -------------------------------------------------------------------------------- 1 | angular.module('ngGravatar', []); 2 | angular.module('ngGravatar').directive("gravatar", function(Gravatar){ 3 | return { 4 | replace: true, 5 | restrict: "E", 6 | template: "", 7 | scope: {email: "="}, 8 | link: function(scope){ 9 | scope.gravatarUrl = function(){ 10 | return Gravatar(scope.email); 11 | } 12 | } 13 | }; 14 | }); 15 | 16 | angular.module('ngGravatar').provider('Gravatar', function(){ 17 | var imageSize = 50; 18 | var url = "http://www.gravatar.com/avatar/" 19 | 20 | this.setSize = function(value) { 21 | imageSize = value; 22 | } 23 | 24 | this.$get = function(){ 25 | return function(email){ 26 | return url + CryptoJS.MD5(email) + "?size=" + imageSize.toString(); 27 | } 28 | } 29 | }); -------------------------------------------------------------------------------- /app/assets/styles/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-NoteTakingAppWithAngularJS/cd926d9a7f670fe748ff338487baf40cd1a39a98/app/assets/styles/.keep -------------------------------------------------------------------------------- /app/assets/styles/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn-default .badge, 33 | .btn-primary .badge, 34 | .btn-success .badge, 35 | .btn-info .badge, 36 | .btn-warning .badge, 37 | .btn-danger .badge { 38 | text-shadow: none; 39 | } 40 | .btn:active, 41 | .btn.active { 42 | background-image: none; 43 | } 44 | .btn-default { 45 | text-shadow: 0 1px 0 #fff; 46 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 47 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 48 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 49 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 50 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 51 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 52 | background-repeat: repeat-x; 53 | border-color: #dbdbdb; 54 | border-color: #ccc; 55 | } 56 | .btn-default:hover, 57 | .btn-default:focus { 58 | background-color: #e0e0e0; 59 | background-position: 0 -15px; 60 | } 61 | .btn-default:active, 62 | .btn-default.active { 63 | background-color: #e0e0e0; 64 | border-color: #dbdbdb; 65 | } 66 | .btn-default.disabled, 67 | .btn-default:disabled, 68 | .btn-default[disabled] { 69 | background-color: #e0e0e0; 70 | background-image: none; 71 | } 72 | .btn-primary { 73 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 74 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 75 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 76 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 79 | background-repeat: repeat-x; 80 | border-color: #245580; 81 | } 82 | .btn-primary:hover, 83 | .btn-primary:focus { 84 | background-color: #265a88; 85 | background-position: 0 -15px; 86 | } 87 | .btn-primary:active, 88 | .btn-primary.active { 89 | background-color: #265a88; 90 | border-color: #245580; 91 | } 92 | .btn-primary.disabled, 93 | .btn-primary:disabled, 94 | .btn-primary[disabled] { 95 | background-color: #265a88; 96 | background-image: none; 97 | } 98 | .btn-success { 99 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 100 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 101 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 102 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 103 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 104 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 105 | background-repeat: repeat-x; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:hover, 109 | .btn-success:focus { 110 | background-color: #419641; 111 | background-position: 0 -15px; 112 | } 113 | .btn-success:active, 114 | .btn-success.active { 115 | background-color: #419641; 116 | border-color: #3e8f3e; 117 | } 118 | .btn-success.disabled, 119 | .btn-success:disabled, 120 | .btn-success[disabled] { 121 | background-color: #419641; 122 | background-image: none; 123 | } 124 | .btn-info { 125 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 126 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 127 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 128 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 129 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 130 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 131 | background-repeat: repeat-x; 132 | border-color: #28a4c9; 133 | } 134 | .btn-info:hover, 135 | .btn-info:focus { 136 | background-color: #2aabd2; 137 | background-position: 0 -15px; 138 | } 139 | .btn-info:active, 140 | .btn-info.active { 141 | background-color: #2aabd2; 142 | border-color: #28a4c9; 143 | } 144 | .btn-info.disabled, 145 | .btn-info:disabled, 146 | .btn-info[disabled] { 147 | background-color: #2aabd2; 148 | background-image: none; 149 | } 150 | .btn-warning { 151 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 152 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 153 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 154 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 155 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 156 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 157 | background-repeat: repeat-x; 158 | border-color: #e38d13; 159 | } 160 | .btn-warning:hover, 161 | .btn-warning:focus { 162 | background-color: #eb9316; 163 | background-position: 0 -15px; 164 | } 165 | .btn-warning:active, 166 | .btn-warning.active { 167 | background-color: #eb9316; 168 | border-color: #e38d13; 169 | } 170 | .btn-warning.disabled, 171 | .btn-warning:disabled, 172 | .btn-warning[disabled] { 173 | background-color: #eb9316; 174 | background-image: none; 175 | } 176 | .btn-danger { 177 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 178 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 179 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 180 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 182 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 183 | background-repeat: repeat-x; 184 | border-color: #b92c28; 185 | } 186 | .btn-danger:hover, 187 | .btn-danger:focus { 188 | background-color: #c12e2a; 189 | background-position: 0 -15px; 190 | } 191 | .btn-danger:active, 192 | .btn-danger.active { 193 | background-color: #c12e2a; 194 | border-color: #b92c28; 195 | } 196 | .btn-danger.disabled, 197 | .btn-danger:disabled, 198 | .btn-danger[disabled] { 199 | background-color: #c12e2a; 200 | background-image: none; 201 | } 202 | .thumbnail, 203 | .img-thumbnail { 204 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 205 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 206 | } 207 | .dropdown-menu > li > a:hover, 208 | .dropdown-menu > li > a:focus { 209 | background-color: #e8e8e8; 210 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 211 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 212 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 213 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 214 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 215 | background-repeat: repeat-x; 216 | } 217 | .dropdown-menu > .active > a, 218 | .dropdown-menu > .active > a:hover, 219 | .dropdown-menu > .active > a:focus { 220 | background-color: #2e6da4; 221 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 222 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 223 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 224 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 225 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 226 | background-repeat: repeat-x; 227 | } 228 | .navbar-default { 229 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 230 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 231 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 232 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 233 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 234 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 235 | background-repeat: repeat-x; 236 | border-radius: 4px; 237 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 238 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 239 | } 240 | .navbar-default .navbar-nav > .open > a, 241 | .navbar-default .navbar-nav > .active > a { 242 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 243 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 244 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 245 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 246 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 247 | background-repeat: repeat-x; 248 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 249 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 250 | } 251 | .navbar-brand, 252 | .navbar-nav > li > a { 253 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 254 | } 255 | .navbar-inverse { 256 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 257 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 258 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 259 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 260 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 261 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 262 | background-repeat: repeat-x; 263 | } 264 | .navbar-inverse .navbar-nav > .open > a, 265 | .navbar-inverse .navbar-nav > .active > a { 266 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 267 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 268 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 269 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 270 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 271 | background-repeat: repeat-x; 272 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 273 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 274 | } 275 | .navbar-inverse .navbar-brand, 276 | .navbar-inverse .navbar-nav > li > a { 277 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 278 | } 279 | .navbar-static-top, 280 | .navbar-fixed-top, 281 | .navbar-fixed-bottom { 282 | border-radius: 0; 283 | } 284 | @media (max-width: 767px) { 285 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 286 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 287 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 288 | color: #fff; 289 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 290 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 291 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 292 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 294 | background-repeat: repeat-x; 295 | } 296 | } 297 | .alert { 298 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 299 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 300 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 301 | } 302 | .alert-success { 303 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 304 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 305 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 306 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 307 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 308 | background-repeat: repeat-x; 309 | border-color: #b2dba1; 310 | } 311 | .alert-info { 312 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 313 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 314 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 315 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 316 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 317 | background-repeat: repeat-x; 318 | border-color: #9acfea; 319 | } 320 | .alert-warning { 321 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 322 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 323 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 324 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 325 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 326 | background-repeat: repeat-x; 327 | border-color: #f5e79e; 328 | } 329 | .alert-danger { 330 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 331 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 332 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 333 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 334 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 335 | background-repeat: repeat-x; 336 | border-color: #dca7a7; 337 | } 338 | .progress { 339 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 340 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 342 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 344 | background-repeat: repeat-x; 345 | } 346 | .progress-bar { 347 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 348 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 349 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 350 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 352 | background-repeat: repeat-x; 353 | } 354 | .progress-bar-success { 355 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 356 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 357 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 358 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 360 | background-repeat: repeat-x; 361 | } 362 | .progress-bar-info { 363 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 364 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 365 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 366 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 367 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 368 | background-repeat: repeat-x; 369 | } 370 | .progress-bar-warning { 371 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 372 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 374 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 376 | background-repeat: repeat-x; 377 | } 378 | .progress-bar-danger { 379 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 380 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 381 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 382 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 383 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 384 | background-repeat: repeat-x; 385 | } 386 | .progress-bar-striped { 387 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 388 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 389 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 390 | } 391 | .list-group { 392 | border-radius: 4px; 393 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 394 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 395 | } 396 | .list-group-item.active, 397 | .list-group-item.active:hover, 398 | .list-group-item.active:focus { 399 | text-shadow: 0 -1px 0 #286090; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 405 | background-repeat: repeat-x; 406 | border-color: #2b669a; 407 | } 408 | .list-group-item.active .badge, 409 | .list-group-item.active:hover .badge, 410 | .list-group-item.active:focus .badge { 411 | text-shadow: none; 412 | } 413 | .panel { 414 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 415 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 416 | } 417 | .panel-default > .panel-heading { 418 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 419 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 420 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 421 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 422 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 423 | background-repeat: repeat-x; 424 | } 425 | .panel-primary > .panel-heading { 426 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 427 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 428 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 429 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 430 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 431 | background-repeat: repeat-x; 432 | } 433 | .panel-success > .panel-heading { 434 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 435 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 436 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 437 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 438 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 439 | background-repeat: repeat-x; 440 | } 441 | .panel-info > .panel-heading { 442 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 443 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 444 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 445 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 446 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 447 | background-repeat: repeat-x; 448 | } 449 | .panel-warning > .panel-heading { 450 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 451 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 453 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .panel-danger > .panel-heading { 458 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 459 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 461 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .well { 466 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 467 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 469 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 471 | background-repeat: repeat-x; 472 | border-color: #dcdcdc; 473 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 474 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 475 | } 476 | /*# sourceMappingURL=bootstrap-theme.css.map */ 477 | -------------------------------------------------------------------------------- /app/assets/styles/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAcA;;;;;;EAME,0CAAA;ECgDA,6FAAA;EACQ,qFAAA;EC5DT;AFgBC;;;;;;;;;;;;EC2CA,0DAAA;EACQ,kDAAA;EC7CT;AFVD;;;;;;EAiBI,mBAAA;EECH;AFiCC;;EAEE,wBAAA;EE/BH;AFoCD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EAgC2C,2BAAA;EAA2B,oBAAA;EEzBvE;AFLC;;EAEE,2BAAA;EACA,8BAAA;EEOH;AFJC;;EAEE,2BAAA;EACA,uBAAA;EEMH;AFHC;;;EAGE,2BAAA;EACA,wBAAA;EEKH;AFUD;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEgCD;AF9BC;;EAEE,2BAAA;EACA,8BAAA;EEgCH;AF7BC;;EAEE,2BAAA;EACA,uBAAA;EE+BH;AF5BC;;;EAGE,2BAAA;EACA,wBAAA;EE8BH;AFdD;EGrDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEyDD;AFvDC;;EAEE,2BAAA;EACA,8BAAA;EEyDH;AFtDC;;EAEE,2BAAA;EACA,uBAAA;EEwDH;AFrDC;;;EAGE,2BAAA;EACA,wBAAA;EEuDH;AFtCD;EGtDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEkFD;AFhFC;;EAEE,2BAAA;EACA,8BAAA;EEkFH;AF/EC;;EAEE,2BAAA;EACA,uBAAA;EEiFH;AF9EC;;;EAGE,2BAAA;EACA,wBAAA;EEgFH;AF9DD;EGvDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EE2GD;AFzGC;;EAEE,2BAAA;EACA,8BAAA;EE2GH;AFxGC;;EAEE,2BAAA;EACA,uBAAA;EE0GH;AFvGC;;;EAGE,2BAAA;EACA,wBAAA;EEyGH;AFtFD;EGxDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEoID;AFlIC;;EAEE,2BAAA;EACA,8BAAA;EEoIH;AFjIC;;EAEE,2BAAA;EACA,uBAAA;EEmIH;AFhIC;;;EAGE,2BAAA;EACA,wBAAA;EEkIH;AFxGD;;EChBE,oDAAA;EACQ,4CAAA;EC4HT;AFnGD;;EGzEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHwEF,2BAAA;EEyGD;AFvGD;;;EG9EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8EF,2BAAA;EE6GD;AFpGD;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ6GA,oBAAA;EC/CA,6FAAA;EACQ,qFAAA;EC0JT;AF/GD;;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECoKT;AF5GD;;EAEE,gDAAA;EE8GD;AF1GD;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EF+OD;AFlHD;;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0LT;AF5HD;;EAYI,2CAAA;EEoHH;AF/GD;;;EAGE,kBAAA;EEiHD;AF5FD;EAfI;;;IAGE,aAAA;IG3IF,0EAAA;IACA,qEAAA;IACA,+FAAA;IAAA,wEAAA;IACA,6BAAA;IACA,wHAAA;ID0PD;EACF;AFxGD;EACE,+CAAA;ECzGA,4FAAA;EACQ,oFAAA;ECoNT;AFhGD;EGpKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4GD;AFvGD;EGrKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoHD;AF9GD;EGtKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4HD;AFrHD;EGvKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoID;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AFlHD;EGzLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AFxHD;EG1LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqTH;AF9HD;EG3LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4TH;AFpID;EG5LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmUH;AF1ID;EG7LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0UH;AF7ID;EGhKI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDgTH;AFzID;EACE,oBAAA;EC5JA,oDAAA;EACQ,4CAAA;ECwST;AF1ID;;;EAGE,+BAAA;EGjNE,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH+MF,uBAAA;EEgJD;AFrJD;;;EAQI,mBAAA;EEkJH;AFxID;ECjLE,mDAAA;EACQ,2CAAA;EC4TT;AFlID;EG1OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED+WH;AFxID;EG3OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDsXH;AF9ID;EG5OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED6XH;AFpJD;EG7OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDoYH;AF1JD;EG9OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2YH;AFhKD;EG/OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkZH;AFhKD;EGtPI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHoPF,uBAAA;ECzMA,2FAAA;EACQ,mFAAA;ECgXT","file":"bootstrap-theme.css","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",".btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default:disabled,\n.btn-default[disabled] {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary:disabled,\n.btn-primary[disabled] {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success:disabled,\n.btn-success[disabled] {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info:disabled,\n.btn-info[disabled] {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning:disabled,\n.btn-warning[disabled] {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger:disabled,\n.btn-danger[disabled] {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /app/assets/templates/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-NoteTakingAppWithAngularJS/cd926d9a7f670fe748ff338487baf40cd1a39a98/app/assets/templates/.keep -------------------------------------------------------------------------------- /app/assets/templates/directives/nwCategorySelector.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/templates/directives/nwPageNav.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/templates/notes/edit.html: -------------------------------------------------------------------------------- 1 |

Edit: {{note.title}}

2 |
3 |
4 | 5 | 6 |
7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 | 29 |
-------------------------------------------------------------------------------- /app/assets/templates/notes/index.html: -------------------------------------------------------------------------------- 1 |

Notes New Note

2 |
3 | Search: 4 |
5 | 6 | 7 |
8 |
9 |

{{note.title}}

10 |

{{note.description}}

11 |

{{note.category.name}}

12 |
13 |
-------------------------------------------------------------------------------- /app/assets/templates/notes/new.html: -------------------------------------------------------------------------------- 1 |

New Note

2 |
3 |
4 | 5 | 6 |
7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 |
-------------------------------------------------------------------------------- /app/assets/templates/notes/show.html: -------------------------------------------------------------------------------- 1 | 5 |

{{note.content}}

6 |

{{note.category.name}}

7 | Edit Note 8 | Delete 9 | -------------------------------------------------------------------------------- /app/assets/templates/users/index.html: -------------------------------------------------------------------------------- 1 |

Users

2 |
3 |
4 |

{{user.name}}

5 |

{{user.site}}

6 |
7 |
-------------------------------------------------------------------------------- /app/assets/templates/users/show.html: -------------------------------------------------------------------------------- 1 |

{{user.name}}


2 | {{user.site}} 3 |

{{user.bio}}

4 | -------------------------------------------------------------------------------- /app/views/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-NoteTakingAppWithAngularJS/cd926d9a7f670fe748ff338487baf40cd1a39a98/app/views/.keep -------------------------------------------------------------------------------- /app/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Note Wrangler 4 | 5 | 6 | 7 | 8 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "note-wrangler", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "body-parser": { 6 | "version": "1.12.0", 7 | "from": "body-parser@^1.4.3", 8 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.0.tgz", 9 | "dependencies": { 10 | "bytes": { 11 | "version": "1.0.0", 12 | "from": "bytes@1.0.0", 13 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" 14 | }, 15 | "content-type": { 16 | "version": "1.0.1", 17 | "from": "content-type@~1.0.1", 18 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" 19 | }, 20 | "debug": { 21 | "version": "2.1.1", 22 | "from": "debug@~2.1.1", 23 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz", 24 | "dependencies": { 25 | "ms": { 26 | "version": "0.6.2", 27 | "from": "ms@0.6.2", 28 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" 29 | } 30 | } 31 | }, 32 | "depd": { 33 | "version": "1.0.0", 34 | "from": "depd@~1.0.0", 35 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" 36 | }, 37 | "iconv-lite": { 38 | "version": "0.4.7", 39 | "from": "iconv-lite@0.4.7", 40 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.7.tgz" 41 | }, 42 | "on-finished": { 43 | "version": "2.2.0", 44 | "from": "on-finished@~2.2.0", 45 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.0.tgz", 46 | "dependencies": { 47 | "ee-first": { 48 | "version": "1.1.0", 49 | "from": "ee-first@1.1.0", 50 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" 51 | } 52 | } 53 | }, 54 | "qs": { 55 | "version": "2.3.3", 56 | "from": "qs@2.3.3", 57 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" 58 | }, 59 | "raw-body": { 60 | "version": "1.3.3", 61 | "from": "raw-body@1.3.3", 62 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.3.tgz" 63 | }, 64 | "type-is": { 65 | "version": "1.6.0", 66 | "from": "type-is@~1.6.0", 67 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.0.tgz", 68 | "dependencies": { 69 | "media-typer": { 70 | "version": "0.3.0", 71 | "from": "media-typer@0.3.0", 72 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" 73 | }, 74 | "mime-types": { 75 | "version": "2.0.9", 76 | "from": "mime-types@~2.0.9", 77 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", 78 | "dependencies": { 79 | "mime-db": { 80 | "version": "1.7.0", 81 | "from": "mime-db@~1.7.0", 82 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | }, 90 | "cookie-parser": { 91 | "version": "1.3.4", 92 | "from": "cookie-parser@^1.3.2", 93 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.3.4.tgz", 94 | "dependencies": { 95 | "cookie": { 96 | "version": "0.1.2", 97 | "from": "cookie@0.1.2", 98 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz" 99 | }, 100 | "cookie-signature": { 101 | "version": "1.0.6", 102 | "from": "cookie-signature@1.0.6", 103 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" 104 | } 105 | } 106 | }, 107 | "express": { 108 | "version": "4.4.5", 109 | "from": "express@~4.4.1", 110 | "resolved": "https://registry.npmjs.org/express/-/express-4.4.5.tgz", 111 | "dependencies": { 112 | "accepts": { 113 | "version": "1.0.7", 114 | "from": "accepts@~1.0.5", 115 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.0.7.tgz", 116 | "dependencies": { 117 | "mime-types": { 118 | "version": "1.0.2", 119 | "from": "mime-types@~1.0.0", 120 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" 121 | }, 122 | "negotiator": { 123 | "version": "0.4.7", 124 | "from": "negotiator@0.4.7", 125 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.4.7.tgz" 126 | } 127 | } 128 | }, 129 | "buffer-crc32": { 130 | "version": "0.2.3", 131 | "from": "buffer-crc32@0.2.3", 132 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.3.tgz" 133 | }, 134 | "debug": { 135 | "version": "1.0.2", 136 | "from": "debug@1.0.2", 137 | "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.2.tgz", 138 | "dependencies": { 139 | "ms": { 140 | "version": "0.6.2", 141 | "from": "ms@0.6.2", 142 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" 143 | } 144 | } 145 | }, 146 | "escape-html": { 147 | "version": "1.0.1", 148 | "from": "escape-html@1.0.1", 149 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" 150 | }, 151 | "methods": { 152 | "version": "1.0.1", 153 | "from": "methods@1.0.1", 154 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz" 155 | }, 156 | "parseurl": { 157 | "version": "1.0.1", 158 | "from": "parseurl@1.0.1", 159 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.0.1.tgz" 160 | }, 161 | "proxy-addr": { 162 | "version": "1.0.1", 163 | "from": "proxy-addr@1.0.1", 164 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.1.tgz", 165 | "dependencies": { 166 | "ipaddr.js": { 167 | "version": "0.1.2", 168 | "from": "ipaddr.js@0.1.2", 169 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-0.1.2.tgz" 170 | } 171 | } 172 | }, 173 | "range-parser": { 174 | "version": "1.0.0", 175 | "from": "range-parser@1.0.0", 176 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.0.tgz" 177 | }, 178 | "send": { 179 | "version": "0.4.3", 180 | "from": "send@0.4.3", 181 | "resolved": "https://registry.npmjs.org/send/-/send-0.4.3.tgz", 182 | "dependencies": { 183 | "finished": { 184 | "version": "1.2.2", 185 | "from": "finished@1.2.2", 186 | "resolved": "https://registry.npmjs.org/finished/-/finished-1.2.2.tgz", 187 | "dependencies": { 188 | "ee-first": { 189 | "version": "1.0.3", 190 | "from": "ee-first@1.0.3", 191 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.0.3.tgz" 192 | } 193 | } 194 | }, 195 | "mime": { 196 | "version": "1.2.11", 197 | "from": "mime@1.2.11", 198 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" 199 | } 200 | } 201 | }, 202 | "serve-static": { 203 | "version": "1.2.3", 204 | "from": "serve-static@1.2.3", 205 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.2.3.tgz" 206 | }, 207 | "type-is": { 208 | "version": "1.2.1", 209 | "from": "type-is@1.2.1", 210 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.2.1.tgz", 211 | "dependencies": { 212 | "mime-types": { 213 | "version": "1.0.0", 214 | "from": "mime-types@1.0.0", 215 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.0.tgz" 216 | } 217 | } 218 | }, 219 | "vary": { 220 | "version": "0.1.0", 221 | "from": "vary@0.1.0", 222 | "resolved": "https://registry.npmjs.org/vary/-/vary-0.1.0.tgz" 223 | }, 224 | "cookie": { 225 | "version": "0.1.2", 226 | "from": "cookie@0.1.2", 227 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz" 228 | }, 229 | "fresh": { 230 | "version": "0.2.2", 231 | "from": "fresh@0.2.2", 232 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.2.tgz" 233 | }, 234 | "cookie-signature": { 235 | "version": "1.0.4", 236 | "from": "cookie-signature@1.0.4", 237 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.4.tgz" 238 | }, 239 | "merge-descriptors": { 240 | "version": "0.0.2", 241 | "from": "merge-descriptors@0.0.2", 242 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz" 243 | }, 244 | "utils-merge": { 245 | "version": "1.0.0", 246 | "from": "utils-merge@1.0.0", 247 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" 248 | }, 249 | "qs": { 250 | "version": "0.6.6", 251 | "from": "qs@0.6.6", 252 | "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" 253 | }, 254 | "path-to-regexp": { 255 | "version": "0.1.2", 256 | "from": "path-to-regexp@0.1.2", 257 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.2.tgz" 258 | } 259 | } 260 | }, 261 | "lodash": { 262 | "version": "3.3.0", 263 | "from": "lodash@", 264 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.3.0.tgz" 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "note-wrangler", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "A Code School Angular Production.", 6 | "repository": "https://**alyssa here**", 7 | "license": "MIT", 8 | "dependencies": { 9 | "body-parser": "^1.4.3", 10 | "cookie-parser": "^1.3.2", 11 | "express": "~4.4.1", 12 | "lodash": "^3.3.0" 13 | }, 14 | "devDependencies": { 15 | "bower": "^1.3.1", 16 | "grunt": "^0.4.5", 17 | "grunt-contrib-sass": "^0.7.3", 18 | "grunt-contrib-watch": "^0.6.1" 19 | }, 20 | "scripts": { 21 | "start": "node app.js" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/expressConfig.js: -------------------------------------------------------------------------------- 1 | var bodyParser = require('body-parser'); 2 | 3 | module.exports = function(app, express) { 4 | // Serve static assets from the app folder. This enables things like javascript 5 | // and stylesheets to be loaded as expected. You would normally use something like 6 | // nginx for this, but this makes for a simpler demo app to just let express do it. 7 | app.use("/", express.static("app/")); 8 | 9 | // Set the view directory, this enables us to use the .render method inside routes 10 | app.set('views', __dirname + '/../app/views'); 11 | 12 | // parse application/x-www-form-urlencoded 13 | app.use(bodyParser.urlencoded({ extended: false })); 14 | 15 | // parse application/json 16 | app.use(bodyParser.json()); 17 | } 18 | -------------------------------------------------------------------------------- /server/models/category.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var categories = [ 4 | {"id": 1, "name": "Testing"}, 5 | {"id": 2, "name": "Personal Note"}, 6 | {"id": 3, "name": "Bash"}, 7 | {"id": 4, "name": "Idea"}, 8 | {"id": 5, "name": "Use with Caution"}, 9 | {"id": 6, "name": "Question"}, 10 | {"id": 7, "name": "Best Practice"}, 11 | {"id": 8, "name": "Code Snippet"} 12 | ] 13 | 14 | module.exports = { 15 | get: function(id) { 16 | return _.find(categories, function(category){ 17 | return category.id === id; 18 | }); 19 | }, 20 | all: function() { 21 | return categories; 22 | } 23 | } -------------------------------------------------------------------------------- /server/models/note.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Category = require('./category') 3 | var User = require('./user') 4 | var notes = [ 5 | {"id":1 ,"userId": 13, "categoryId": 8, "description": "NgModel Best Practice", "content" : "Always use dot syntax when using NgModel! Treat Scope as read-only in templates & write-only in controllers. The purpose of the scope is to refer to the model, not be the model. The model is your javascript objects. When doing bidirectional binding with ngModel make sure you don't bind directly to the scope properties. This will cause unexpected behavior in the child scopes.", "title" : "NgModel BP"}, 6 | {"id":2 ,"userId": 2, "categoryId": 3, "description" : "Markers on a **DOM element** that tell AngularJS's HTML compiler `$compile` to attach a specified behavior to that DOM element.", "title" : "Directives", "content": "Markers on a **DOM element**"}, 7 | {"id":3 ,"userId": 1, "categoryId": 6, "description" : "Clarify the confusion between Service the term and `service` the angular method and to explain the 5 different Service recipes in Angular.", "title" : "Service Service? Really Angular?","content": "There are 5 Recipes used to create a Service. One of those *was* unfortunately named, Service. So yes, amongst its fellow peers such as Provider Service and Factory Service, there is in fact a Service Service."}, 8 | {"id":4 ,"userId": 2, "categoryId": 6, "description" : "QUESTIONABLE DESCRIPTION GOES HERE", "title" : "TEST TEST TEST", "content": "QUESTIONABLE CONTENT GOES HERE"}, 9 | {"id":5 ,"userId": 4, "categoryId": 6, "description" : "Define Service", "title" : "What is a Service", "content": "Service: Angular services are objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app."}, 10 | {"id":6 ,"userId": 5, "categoryId": 6, "description" : "Steps for Creating a Service", "title" : "How do you create a Service?", "content": "You can register a service to our Angular module `app` with a one of the following 5 recipes: \\n - **factory** \\n - **provider** \\n - **service** \\n - **value** \\n - **constant** "} 11 | ] 12 | var lastId = 6; 13 | 14 | var buildNotes = function() { 15 | // Make a deep copy so we don't change the main notes array 16 | var rawNotes = JSON.parse(JSON.stringify(notes)); 17 | var builtNotes = []; 18 | var note; 19 | 20 | for(var i=0, l=rawNotes.length; i < l; i++) { 21 | note = rawNotes[i]; 22 | note.user = User.get(note.userId); 23 | note.category = Category.get(note.categoryId); 24 | builtNotes.push(note); 25 | } 26 | return builtNotes 27 | } 28 | 29 | module.exports = { 30 | get: function(id) { 31 | return _.find(buildNotes(), function(note){ 32 | return note.id === id; 33 | }); 34 | }, 35 | all: function() { 36 | return buildNotes(); 37 | }, 38 | update: function(note) { 39 | var updatedNote; 40 | for(var i=0, l=notes.length; i < l; i++) { 41 | if(notes[i].id === note.id){ 42 | _.assign(notes[i], note); 43 | updatedNote = notes[i]; 44 | break; 45 | } 46 | } 47 | return updatedNote; 48 | }, 49 | delete: function(id) { 50 | var deletedNote; 51 | for(var i=0, l=notes.length; i < l; i++) { 52 | if(notes[i].id === id){ 53 | deletedNote = notes[i]; 54 | notes.splice(i, 1); 55 | break; 56 | } 57 | } 58 | return deletedNote; 59 | }, 60 | create: function(note) { 61 | lastId += 1; 62 | note.id = lastId; 63 | notes.push(note) 64 | return note; 65 | } 66 | } -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var users = [ 4 | {"id": 1, "email": "jeffrey@zeldman.com", "username": "zeldman", "name": "Jeffrey Zeldman", "bio": "Founder, Happy Cog studios. Author, Designing With Web Standards. Publisher, A List Apart, A Book Apart.", "twitter_handle": "@zeldman", "site": "zeldman.com"}, 5 | {"id": 2, "email": "brad@bradly.com", "username": "b_green", "name": "Brad Green", "bio": "I work at Google where I manage AngularJS and Google's internal sales productivity applications. I'm a dad.", "twitter_handle": "@bradlygreen", "site": "google.com/+BradGreen"}, 6 | {"id": 3, "email": "", "username": "Meyer the Eric", "name": "Eric A. Meyer", "bio": "Web standards | (X)HTML | CSS | microformats | community | writing | speaking | signing man.", "twitter_handle": "@meyerweb", "site": "meyerweb.com"}, 7 | {"id": 4, "email": "gregg@codeschool.com", "username": "GP", "name": "Gregg Pollack", "bio": "Founder of Envy Labs, Code School, Orlando Ruby Users Group, BarCamp Orlando, and the Orlando Tech Events newsletter.", "twitter_handle": "@greggpollack", "site": "EnvyLabs.com"}, 8 | {"id": 5, "email": "me@rachelhigley.com", "username": "r_higley", "name": "Rachel Higley", "bio": "A web developer located in central florida", "twitter_handle": "@RachelHigley", "site": ""}, 9 | {"id": 6, "email": "mail@zdn.me", "username": "zach", "name": "Zachary Nicoll", "bio": "Bio sections always intimidate me. I can never think of anything to say that will achieve that awe inspiring effect I want it to have.", "twitter_handle": "@turtleguyy", "site": "zacharynicoll.com"}, 10 | {"id": 7, "email": "adamrensel@codeschool.com", "username": "renz", "name": "Adam Rensel","bio": "Web Developer at @envylabs and @codeschool", "twitter_handle": "@adamrensel", "site": "adamrensel.com"}, 11 | {"id": 9, "email": "olivier@codeschool.com", "username": "OlivierLacan","name": "Olivier Lacan","bio": "Software bricoleur at @codeschool, word wrangler, scientific skeptic, and logic lumberjack.","twitter_handle": "@olivierlacan","site": "olivierlacan.com"}, 12 | {"id": 11, "email": "drew@codeschool.com", "username": "DrewBarontini", "name": "Drew Barontini", "bio": "Front-end developer @codeschool, descendant of @envylabs, real-life extrovert, internet introvert.", "twitter_handle": "@drewbarontini", "site": "drewbarontini.com"}, 13 | {"id": 12, "email": "jordan@codeschool.com", "username": "JordanWade", "name": "Jordan Wade", "bio": "Designer, Illustrator, and Front-End Developer @codeschool", "twitter_handle": "@jjordanwade", "site": "jamesjordanwade.com"}, 14 | {"id": 13, "email": "alyssa@codeschool.com", "username": "AlyssaNicoll", "name": "Alyssa Nicoll", "bio": "Code School Teacher. Angular Lover. Scuba Diver.", "twitter_handle": "@AlyssaNicoll", "site": "alyssa.io"} 15 | ]; 16 | 17 | module.exports = { 18 | get: function(id) { 19 | return _.find(users, function(user){ 20 | return user.id === id; 21 | }); 22 | }, 23 | all: function() { 24 | return users; 25 | } 26 | } -------------------------------------------------------------------------------- /server/routes.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | // Load Express Configuration 5 | require('./expressConfig')(app, express); 6 | 7 | // Root route 8 | app.get('/', function(req, res){ 9 | res.sendfile('index.html', {root: app.settings.views}); 10 | }); 11 | 12 | // Load routes 13 | require('./routes/user')(app); //user routes 14 | require('./routes/note')(app); // note routes 15 | require('./routes/category')(app); // category routes 16 | 17 | module.exports = app; 18 | -------------------------------------------------------------------------------- /server/routes/category.js: -------------------------------------------------------------------------------- 1 | var Category = require('../models/category'); 2 | 3 | module.exports = function(app) { 4 | // Return a list of available node types 5 | app.get('/categories', function(req, res) { 6 | res.json(Category.all()); 7 | }); 8 | 9 | app.get('/categories/:id', function(req, res) { 10 | var categoryId = parseInt(req.param('id'), 10); 11 | res.json(Category.get(categoryId) || {}); 12 | }); 13 | }; -------------------------------------------------------------------------------- /server/routes/note.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Note = require('../models/note') 3 | 4 | module.exports = function(app) { 5 | app.get('/notes', function(req, res) { 6 | res.json(Note.all()); 7 | }); 8 | 9 | app.post('/notes', function(req, res) { 10 | // Add a delay here to simulate the delay of a live server 11 | // So things like button isSubmitting states can be demonstrated 12 | setTimeout(function(){ 13 | res.json(Note.create(req.body)); 14 | }, 1000); 15 | }); 16 | 17 | app.put('/notes/:id', function(req, res) { 18 | // Add a delay here to simulate the delay of a live server 19 | // So things like button isSubmitting states can be demonstrated 20 | setTimeout(function(){ 21 | res.json(Note.update(req.body)); 22 | },1000) 23 | }); 24 | 25 | app.get('/notes/:id', function(req, res) { 26 | var noteId = parseInt(req.param('id'), 10); 27 | res.json(Note.get(noteId) || {}); 28 | }); 29 | 30 | app.delete('/notes/:id', function(req, res) { 31 | res.json(Note.delete(parseInt(req.param('id'), 10)) || {}); 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /server/routes/user.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var users = [ 4 | {"id": 1, "username": "zeldman", "name": "Jeffery Zeldman", "bio": "Founder, Happy Cog studios. Author, Designing With Web Standards. Publisher, A List Apart, A Book Apart.", "twitter_handle": "@zeldman", "site": "zeldman.com"}, 5 | {"id": 2, "username": "b_green", "name": "Brad Green", "bio": "I work at Google where I manage AngularJS and Google's internal sales productivity applications. I'm a dad.", "twitter_handle": "@bradlygreen", "site": "google.com/+BradGreen"}, 6 | {"id": 3, "username": "Meyer the Eric", "name": "Eric A. Meyer", "bio": "Web standards | (X)HTML | CSS | microformats | community | writing | speaking | signing man.", "twitter_handle": "@meyerweb", "site": "meyerweb.com"}, 7 | {"id": 4, "username": "GP", "name": "Gregg Pollack", "bio": "Founder of Envy Labs, Code School, Orlando Ruby Users Group, BarCamp Orlando, and the Orlando Tech Events newsletter.", "twitter_handle": "@greggpollack", "site": "EnvyLabs.com"}, 8 | {"id": 5, "username": "r_higley", "name": "Rachel Higley", "bio": "A web developer located in central florida", "twitter_handle": "@RachelHigley", "site": ""}, 9 | {"id": 6, "username": "zach", "name": "Zachary Nicoll", "bio": "Bio sections always intimidate me. I can never think of anything to say that will achieve that awe inspiring effect I want it to have.", "twitter_handle": "@turtleguyy", "site": "zacharynicoll.com"}, 10 | {"id": 7, "username": "renz", "name": "Adam Rensel","bio": "Web Developer at @envylabs and @codeschool", "twitter_handle": "@adamrensel", "site": "adamrensel.com"}, 11 | {"id": 8, "username": "ItsThrillhouse", "name": "Jason Millhouse", "bio": "Course builder. Aspiring writer. Comp Sci guy. Teacher. Sweetfiend. Corgi lover. Gamer who doesn't. Pro Series host. Voice of the UCF Marching Knights. Dork.", "twitter_handle": "@ItsThrillhouse", "site": ""}, 12 | {"id": 9, "username": "OlivierLacan","name": "Olivier Lacan","bio": "Software bricoleur at @codeschool, word wrangler, scientific skeptic, and logic lumberjack.","twitter_handle": "@olivierlacan","site": "olivierlacan.com"}, 13 | {"id": 10, "username": "theSmith", "name": "Andrew Smith", "bio": "iOS & Web Developer at @intelity. @fullsail graduate.", "twitter_handle": "@fullsailor", "site": "fullsailor.com"}, 14 | {"id": 11, "username": "DrewBarontini", "name": "Drew Barontini", "bio": "Front-end developer @codeschool, descendant of @envylabs, real-life extrovert, internet introvert.", "twitter_handle": "@drewbarontini", "site": "drewbarontini.com"}, 15 | {"id": 12, "username": "JordanWade", "name": "Jordan Wade", "bio": "Designer, Illustrator, and Front-End Developer @codeschool", "twitter_handle": "@jjordanwade", "site": "jamesjordanwade.com"}, 16 | {"id": 13, "username": "AlyssaNicoll", "name": "Alyssa Nicoll", "bio": "Code School Teacher. Angular Lover. Scuba Diver.", "twitter_handle": "@AlyssaNicoll", "site": "alyssa.io"} 17 | ]; 18 | 19 | var User = require('../models/user'); 20 | 21 | module.exports = function(app){ 22 | app.get('/users', function(req, res){ 23 | res.json(User.all()); 24 | }); 25 | 26 | app.get('/users/:id', function(req, res){ 27 | var userId = parseInt(req.params.id, 10); 28 | 29 | // var selectedUser = _.find(users, function(user){ 30 | // return user.id === userId; 31 | // }); 32 | 33 | res.json(User.get(userId) || {}); 34 | }); 35 | }; 36 | --------------------------------------------------------------------------------