├── .gitignore ├── static ├── css │ ├── bg.png │ ├── normalize.css │ └── style.css └── js │ ├── app.js │ └── libs │ └── handlebars-1.1.2.js ├── requirements.txt ├── README.md ├── app.py └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.pyc -------------------------------------------------------------------------------- /static/css/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Januzellij/flask-ember-todo/HEAD/static/css/bg.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==1.0.0 2 | Flask==0.10.1 3 | Flask-RESTful==0.3.4 4 | Flask-SQLAlchemy==2.0 5 | itsdangerous==0.24 6 | Jinja2==2.8 7 | MarkupSafe==0.23 8 | pytz==2015.6 9 | six==1.10.0 10 | SQLAlchemy==1.0.8 11 | Werkzeug==0.10.4 12 | wheel==0.24.0 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask-Ember-Todo 2 | = 3 | The Ember version of the TodoMVC application found here, backed by a Flask JSON API. 4 | 5 | Most of the code in this app is from the above mentioned tutorial. I only added the API to interface with the database. Ember expects JSON in a specific format, so this can be a template for that, as it took me a bit to figure out the specifics. 6 | 7 | ### Installation 8 | Clone this repository, navigate to that directory and use virtualenv: 9 | 10 | `virtualenv venv` 11 | 12 | `source venv/bin/activate` 13 | 14 | `pip install -r requirements.txt` 15 | 16 | `python app.py` 17 | 18 | And your server should be up and running. 19 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, send_file, request 2 | from flask.ext.restful import Api, Resource 3 | from flask.ext.sqlalchemy import SQLAlchemy 4 | 5 | app = Flask(__name__) 6 | app.debug=True 7 | api = Api(app) 8 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/todos.db' 9 | db = SQLAlchemy(app) 10 | 11 | class TodoModel(db.Model): 12 | id = db.Column(db.Integer, primary_key=True) 13 | title = db.Column(db.String(80)) 14 | isCompleted = db.Column(db.Boolean) 15 | 16 | def __init__(self, title, isCompleted=False): 17 | self.title = title 18 | self.isCompleted = isCompleted 19 | 20 | @property 21 | def to_json(self): 22 | return { 23 | "id": self.id, 24 | "isCompleted": self.isCompleted, 25 | "title": self.title 26 | } 27 | 28 | # write a module to handle this 29 | 30 | class Todos(Resource): 31 | def get(self): 32 | todos = TodoModel.query.all() 33 | return jsonify({ "todos" : [t.to_json for t in todos] }) 34 | 35 | def post(self): 36 | t = request.json['todo']['title'] 37 | new_todo = TodoModel(t) 38 | db.session.add(new_todo) 39 | db.session.commit() 40 | 41 | 42 | class Todo(Resource): 43 | def put(self, id): 44 | todo = TodoModel.query.get(id) 45 | req = request.json['todo'] 46 | todo.title = req['title'] 47 | todo.isCompleted = req['isCompleted'] 48 | db.session.commit() 49 | 50 | def delete(self, id): 51 | todo = TodoModel.query.get(id) 52 | db.session.delete(todo) 53 | db.session.commit() 54 | 55 | 56 | api.add_resource(Todos, '/api/todos', methods=['GET','POST']) 57 | api.add_resource(Todo, '/api/todos/', methods=['GET','POST','PUT', 'DELETE']) 58 | 59 | @app.route('/') 60 | def index(): 61 | return send_file('templates/index.html') 62 | 63 | if __name__ == '__main__': 64 | db.create_all() 65 | """db.session.add(TodoModel("Test")) 66 | db.session.commit()""" 67 | app.run() 68 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ember Starter Kit 6 | 7 | 8 | 9 | 10 | 24 | 25 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | Todos = Ember.Application.create(); 2 | 3 | Todos.ApplicationAdapter = DS.RESTAdapter.extend({ 4 | namespace: 'api' 5 | }); 6 | 7 | Todos.Router.map(function () { 8 | this.resource('todos', { path: '/' }, function () { 9 | this.route('active'); 10 | this.route('completed'); 11 | }); 12 | }); 13 | 14 | Todos.TodosRoute = Ember.Route.extend({ 15 | model: function () { 16 | return this.store.find('todo'); 17 | } 18 | }); 19 | 20 | Todos.TodosIndexRoute = Ember.Route.extend({ 21 | model: function () { 22 | return this.modelFor('todos'); 23 | } 24 | }); 25 | 26 | Todos.TodosActiveRoute = Ember.Route.extend({ 27 | model: function(){ 28 | return this.store.filter('todo', function (todo) { 29 | return !todo.get('isCompleted'); 30 | }); 31 | }, 32 | renderTemplate: function(controller){ 33 | this.render('todos/index', {controller: controller}); 34 | } 35 | }); 36 | 37 | Todos.TodosCompletedRoute = Ember.Route.extend({ 38 | model: function(){ 39 | return this.store.filter('todo', function (todo) { 40 | return todo.get('isCompleted'); 41 | }); 42 | }, 43 | renderTemplate: function(controller){ 44 | this.render('todos/index', {controller: controller}); 45 | } 46 | }); 47 | 48 | Todos.Todo = DS.Model.extend({ 49 | title: DS.attr('string'), 50 | isCompleted: DS.attr('boolean') 51 | }); 52 | 53 | Todos.TodoController = Ember.ObjectController.extend({ 54 | actions: { 55 | editTodo: function () { 56 | this.set('isEditing', true); 57 | }, 58 | acceptChanges: function () { 59 | this.set('isEditing', false); 60 | 61 | if (Ember.isEmpty(this.get('model.title'))) { 62 | this.send('removeTodo'); 63 | } else { 64 | this.get('model').save(); 65 | } 66 | }, 67 | removeTodo: function () { 68 | var todo = this.get('model'); 69 | todo.deleteRecord(); 70 | todo.save(); 71 | } 72 | }, 73 | 74 | isEditing: false, 75 | 76 | isCompleted: function(key, value){ 77 | var model = this.get('model'); 78 | 79 | if (value === undefined) { 80 | // property being used as a getter 81 | return model.get('isCompleted'); 82 | } else { 83 | // property being used as setter 84 | model.set('isCompleted', value); 85 | model.save(); 86 | return value; 87 | } 88 | }.property('model.isCompleted') 89 | }); 90 | 91 | Todos.TodosController = Ember.ArrayController.extend({ 92 | actions: { 93 | createTodo: function () { 94 | // Get the todo title set by the "New Todo" text field 95 | var title = this.get('newTitle'); 96 | if (!title.trim()) { return; } 97 | 98 | // Create the new Todo model 99 | var todo = this.store.createRecord('todo', { 100 | title: title, 101 | isCompleted: false 102 | }); 103 | 104 | // Clear the "New Todo" text field 105 | this.set('newTitle', ''); 106 | 107 | // Save the new model 108 | todo.save(); 109 | }, 110 | clearCompleted: function () { 111 | var completed = this.filterProperty('isCompleted', true); 112 | completed.invoke('deleteRecord'); 113 | completed.invoke('save'); 114 | } 115 | }, 116 | 117 | remaining: function () { 118 | return this.filterProperty('isCompleted', false).get('length'); 119 | }.property('@each.isCompleted'), 120 | 121 | inflection: function () { 122 | var remaining = this.get('remaining'); 123 | return remaining === 1 ? 'item' : 'items'; 124 | }.property('remaining'), 125 | 126 | hasCompleted: function () { 127 | return this.get('completed') > 0; 128 | }.property('completed'), 129 | 130 | completed: function () { 131 | return this.filterProperty('isCompleted', true).get('length'); 132 | }.property('@each.isCompleted'), 133 | 134 | allAreDone: function (key, value) { 135 | if (value === undefined) { 136 | return !!this.get('length') && this.everyProperty('isCompleted', true); 137 | } else { 138 | this.setEach('isCompleted', value); 139 | this.invoke('save'); 140 | return value; 141 | } 142 | }.property('@each.isCompleted') 143 | }); 144 | 145 | Todos.EditTodoView = Ember.TextField.extend({ 146 | didInsertElement: function () { 147 | this.$().focus(); 148 | } 149 | }); 150 | 151 | Ember.Handlebars.helper('edit-todo', Todos.EditTodoView); -------------------------------------------------------------------------------- /static/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address styling not present in IE 8/9. 48 | */ 49 | 50 | [hidden] { 51 | display: none; 52 | } 53 | 54 | /* ========================================================================== 55 | Base 56 | ========================================================================== */ 57 | 58 | /** 59 | * 1. Prevent system color scheme's background color being used in Firefox, IE, 60 | * and Opera. 61 | * 2. Prevent system color scheme's text color being used in Firefox, IE, and 62 | * Opera. 63 | * 3. Set default font family to sans-serif. 64 | * 4. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | background: #fff; /* 1 */ 70 | color: #000; /* 2 */ 71 | font-family: sans-serif; /* 3 */ 72 | -ms-text-size-adjust: 100%; /* 4 */ 73 | -webkit-text-size-adjust: 100%; /* 4 */ 74 | } 75 | 76 | /** 77 | * Remove default margin. 78 | */ 79 | 80 | body { 81 | margin: 0; 82 | } 83 | 84 | /* ========================================================================== 85 | Links 86 | ========================================================================== */ 87 | 88 | /** 89 | * Address `outline` inconsistency between Chrome and other browsers. 90 | */ 91 | 92 | a:focus { 93 | outline: thin dotted; 94 | } 95 | 96 | /** 97 | * Improve readability when focused and also mouse hovered in all browsers. 98 | */ 99 | 100 | a:active, 101 | a:hover { 102 | outline: 0; 103 | } 104 | 105 | /* ========================================================================== 106 | Typography 107 | ========================================================================== */ 108 | 109 | /** 110 | * Address variable `h1` font-size and margin within `section` and `article` 111 | * contexts in Firefox 4+, Safari 5, and Chrome. 112 | */ 113 | 114 | h1 { 115 | font-size: 2em; 116 | margin: 0.67em 0; 117 | } 118 | 119 | /** 120 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 121 | */ 122 | 123 | abbr[title] { 124 | border-bottom: 1px dotted; 125 | } 126 | 127 | /** 128 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 129 | */ 130 | 131 | b, 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | /** 137 | * Address styling not present in Safari 5 and Chrome. 138 | */ 139 | 140 | dfn { 141 | font-style: italic; 142 | } 143 | 144 | /** 145 | * Address differences between Firefox and other browsers. 146 | */ 147 | 148 | hr { 149 | -moz-box-sizing: content-box; 150 | box-sizing: content-box; 151 | height: 0; 152 | } 153 | 154 | /** 155 | * Address styling not present in IE 8/9. 156 | */ 157 | 158 | mark { 159 | background: #ff0; 160 | color: #000; 161 | } 162 | 163 | /** 164 | * Correct font family set oddly in Safari 5 and Chrome. 165 | */ 166 | 167 | code, 168 | kbd, 169 | pre, 170 | samp { 171 | font-family: monospace, serif; 172 | font-size: 1em; 173 | } 174 | 175 | /** 176 | * Improve readability of pre-formatted text in all browsers. 177 | */ 178 | 179 | pre { 180 | white-space: pre-wrap; 181 | } 182 | 183 | /** 184 | * Set consistent quote types. 185 | */ 186 | 187 | q { 188 | quotes: "\201C" "\201D" "\2018" "\2019"; 189 | } 190 | 191 | /** 192 | * Address inconsistent and variable font size in all browsers. 193 | */ 194 | 195 | small { 196 | font-size: 80%; 197 | } 198 | 199 | /** 200 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 201 | */ 202 | 203 | sub, 204 | sup { 205 | font-size: 75%; 206 | line-height: 0; 207 | position: relative; 208 | vertical-align: baseline; 209 | } 210 | 211 | sup { 212 | top: -0.5em; 213 | } 214 | 215 | sub { 216 | bottom: -0.25em; 217 | } 218 | 219 | /* ========================================================================== 220 | Embedded content 221 | ========================================================================== */ 222 | 223 | /** 224 | * Remove border when inside `a` element in IE 8/9. 225 | */ 226 | 227 | img { 228 | border: 0; 229 | } 230 | 231 | /** 232 | * Correct overflow displayed oddly in IE 9. 233 | */ 234 | 235 | svg:not(:root) { 236 | overflow: hidden; 237 | } 238 | 239 | /* ========================================================================== 240 | Figures 241 | ========================================================================== */ 242 | 243 | /** 244 | * Address margin not present in IE 8/9 and Safari 5. 245 | */ 246 | 247 | figure { 248 | margin: 0; 249 | } 250 | 251 | /* ========================================================================== 252 | Forms 253 | ========================================================================== */ 254 | 255 | /** 256 | * Define consistent border, margin, and padding. 257 | */ 258 | 259 | fieldset { 260 | border: 1px solid #c0c0c0; 261 | margin: 0 2px; 262 | padding: 0.35em 0.625em 0.75em; 263 | } 264 | 265 | /** 266 | * 1. Correct `color` not being inherited in IE 8/9. 267 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 268 | */ 269 | 270 | legend { 271 | border: 0; /* 1 */ 272 | padding: 0; /* 2 */ 273 | } 274 | 275 | /** 276 | * 1. Correct font family not being inherited in all browsers. 277 | * 2. Correct font size not being inherited in all browsers. 278 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 279 | */ 280 | 281 | button, 282 | input, 283 | select, 284 | textarea { 285 | font-family: inherit; /* 1 */ 286 | font-size: 100%; /* 2 */ 287 | margin: 0; /* 3 */ 288 | } 289 | 290 | /** 291 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 292 | * the UA stylesheet. 293 | */ 294 | 295 | button, 296 | input { 297 | line-height: normal; 298 | } 299 | 300 | /** 301 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 302 | * All other form control elements do not inherit `text-transform` values. 303 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 304 | * Correct `select` style inheritance in Firefox 4+ and Opera. 305 | */ 306 | 307 | button, 308 | select { 309 | text-transform: none; 310 | } 311 | 312 | /** 313 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 314 | * and `video` controls. 315 | * 2. Correct inability to style clickable `input` types in iOS. 316 | * 3. Improve usability and consistency of cursor style between image-type 317 | * `input` and others. 318 | */ 319 | 320 | button, 321 | html input[type="button"], /* 1 */ 322 | input[type="reset"], 323 | input[type="submit"] { 324 | -webkit-appearance: button; /* 2 */ 325 | cursor: pointer; /* 3 */ 326 | } 327 | 328 | /** 329 | * Re-set default cursor for disabled elements. 330 | */ 331 | 332 | button[disabled], 333 | html input[disabled] { 334 | cursor: default; 335 | } 336 | 337 | /** 338 | * 1. Address box sizing set to `content-box` in IE 8/9. 339 | * 2. Remove excess padding in IE 8/9. 340 | */ 341 | 342 | input[type="checkbox"], 343 | input[type="radio"] { 344 | box-sizing: border-box; /* 1 */ 345 | padding: 0; /* 2 */ 346 | } 347 | 348 | /** 349 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 350 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 351 | * (include `-moz` to future-proof). 352 | */ 353 | 354 | input[type="search"] { 355 | -webkit-appearance: textfield; /* 1 */ 356 | -moz-box-sizing: content-box; 357 | -webkit-box-sizing: content-box; /* 2 */ 358 | box-sizing: content-box; 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari 5 and Chrome 363 | * on OS X. 364 | */ 365 | 366 | input[type="search"]::-webkit-search-cancel-button, 367 | input[type="search"]::-webkit-search-decoration { 368 | -webkit-appearance: none; 369 | } 370 | 371 | /** 372 | * Remove inner padding and border in Firefox 4+. 373 | */ 374 | 375 | button::-moz-focus-inner, 376 | input::-moz-focus-inner { 377 | border: 0; 378 | padding: 0; 379 | } 380 | 381 | /** 382 | * 1. Remove default vertical scrollbar in IE 8/9. 383 | * 2. Improve readability and alignment in all browsers. 384 | */ 385 | 386 | textarea { 387 | overflow: auto; /* 1 */ 388 | vertical-align: top; /* 2 */ 389 | } 390 | 391 | /* ========================================================================== 392 | Tables 393 | ========================================================================== */ 394 | 395 | /** 396 | * Remove most spacing between table cells. 397 | */ 398 | 399 | table { 400 | border-collapse: collapse; 401 | border-spacing: 0; 402 | } 403 | -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | color: inherit; 16 | -webkit-appearance: none; 17 | /*-moz-appearance: none;*/ 18 | -ms-appearance: none; 19 | -o-appearance: none; 20 | appearance: none; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #eaeaea url('bg.png'); 27 | color: #4d4d4d; 28 | width: 550px; 29 | margin: 0 auto; 30 | -webkit-font-smoothing: antialiased; 31 | -moz-font-smoothing: antialiased; 32 | -ms-font-smoothing: antialiased; 33 | -o-font-smoothing: antialiased; 34 | font-smoothing: antialiased; 35 | } 36 | 37 | #todoapp { 38 | background: #fff; 39 | background: rgba(255, 255, 255, 0.9); 40 | margin: 130px 0 40px 0; 41 | border: 1px solid #ccc; 42 | position: relative; 43 | border-top-left-radius: 2px; 44 | border-top-right-radius: 2px; 45 | box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), 46 | 0 25px 50px 0 rgba(0, 0, 0, 0.15); 47 | } 48 | 49 | #todoapp:before { 50 | content: ''; 51 | border-left: 1px solid #f5d6d6; 52 | border-right: 1px solid #f5d6d6; 53 | width: 2px; 54 | position: absolute; 55 | top: 0; 56 | left: 40px; 57 | height: 100%; 58 | } 59 | 60 | #todoapp input::-webkit-input-placeholder { 61 | font-style: italic; 62 | } 63 | 64 | #todoapp input:-moz-placeholder { 65 | font-style: italic; 66 | color: #a9a9a9; 67 | } 68 | 69 | #todoapp h1 { 70 | position: absolute; 71 | top: -120px; 72 | width: 100%; 73 | font-size: 70px; 74 | font-weight: bold; 75 | text-align: center; 76 | color: #b3b3b3; 77 | color: rgba(255, 255, 255, 0.3); 78 | text-shadow: -1px -1px rgba(0, 0, 0, 0.2); 79 | -webkit-text-rendering: optimizeLegibility; 80 | -moz-text-rendering: optimizeLegibility; 81 | -ms-text-rendering: optimizeLegibility; 82 | -o-text-rendering: optimizeLegibility; 83 | text-rendering: optimizeLegibility; 84 | } 85 | 86 | #header { 87 | padding-top: 15px; 88 | border-radius: inherit; 89 | } 90 | 91 | #header:before { 92 | content: ''; 93 | position: absolute; 94 | top: 0; 95 | right: 0; 96 | left: 0; 97 | height: 15px; 98 | z-index: 2; 99 | border-bottom: 1px solid #6c615c; 100 | background: #8d7d77; 101 | background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); 102 | background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 103 | background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 104 | background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 105 | background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 106 | background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 107 | filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); 108 | border-top-left-radius: 1px; 109 | border-top-right-radius: 1px; 110 | } 111 | 112 | #new-todo, 113 | .edit { 114 | position: relative; 115 | margin: 0; 116 | width: 100%; 117 | font-size: 24px; 118 | font-family: inherit; 119 | line-height: 1.4em; 120 | border: 0; 121 | outline: none; 122 | color: inherit; 123 | padding: 6px; 124 | border: 1px solid #999; 125 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 126 | -webkit-box-sizing: border-box; 127 | -moz-box-sizing: border-box; 128 | -ms-box-sizing: border-box; 129 | -o-box-sizing: border-box; 130 | box-sizing: border-box; 131 | -webkit-font-smoothing: antialiased; 132 | -moz-font-smoothing: antialiased; 133 | -ms-font-smoothing: antialiased; 134 | -o-font-smoothing: antialiased; 135 | font-smoothing: antialiased; 136 | } 137 | 138 | #new-todo { 139 | padding: 16px 16px 16px 60px; 140 | border: none; 141 | background: rgba(0, 0, 0, 0.02); 142 | z-index: 2; 143 | box-shadow: none; 144 | } 145 | 146 | #main { 147 | position: relative; 148 | z-index: 2; 149 | border-top: 1px dotted #adadad; 150 | } 151 | 152 | label[for='toggle-all'] { 153 | display: none; 154 | } 155 | 156 | #toggle-all { 157 | position: absolute; 158 | top: -42px; 159 | left: -4px; 160 | width: 40px; 161 | text-align: center; 162 | border: none; /* Mobile Safari */ 163 | } 164 | 165 | #toggle-all:before { 166 | content: '�'; 167 | font-size: 28px; 168 | color: #d9d9d9; 169 | padding: 0 25px 7px; 170 | } 171 | 172 | #toggle-all:checked:before { 173 | color: #737373; 174 | } 175 | 176 | #todo-list { 177 | margin: 0; 178 | padding: 0; 179 | list-style: none; 180 | } 181 | 182 | #todo-list li { 183 | position: relative; 184 | font-size: 24px; 185 | border-bottom: 1px dotted #ccc; 186 | } 187 | 188 | #todo-list li:last-child { 189 | border-bottom: none; 190 | } 191 | 192 | #todo-list li.editing { 193 | border-bottom: none; 194 | padding: 0; 195 | } 196 | 197 | #todo-list li.editing .edit { 198 | display: block; 199 | width: 506px; 200 | padding: 13px 17px 12px 17px; 201 | margin: 0 0 0 43px; 202 | } 203 | 204 | #todo-list li.editing .view { 205 | display: none; 206 | } 207 | 208 | #todo-list li .toggle { 209 | text-align: center; 210 | width: 40px; 211 | /* auto, since non-WebKit browsers doesn't support input styling */ 212 | height: auto; 213 | position: absolute; 214 | top: 0; 215 | bottom: 0; 216 | margin: auto 0; 217 | border: none; /* Mobile Safari */ 218 | -webkit-appearance: none; 219 | /*-moz-appearance: none;*/ 220 | -ms-appearance: none; 221 | -o-appearance: none; 222 | appearance: none; 223 | } 224 | 225 | #todo-list li .toggle:after { 226 | content: '?'; 227 | line-height: 43px; /* 40 + a couple of pixels visual adjustment */ 228 | font-size: 20px; 229 | color: #d9d9d9; 230 | text-shadow: 0 -1px 0 #bfbfbf; 231 | } 232 | 233 | #todo-list li .toggle:checked:after { 234 | color: #85ada7; 235 | text-shadow: 0 1px 0 #669991; 236 | bottom: 1px; 237 | position: relative; 238 | } 239 | 240 | #todo-list li label { 241 | word-break: break-word; 242 | padding: 15px; 243 | margin-left: 45px; 244 | display: block; 245 | line-height: 1.2; 246 | -webkit-transition: color 0.4s; 247 | -moz-transition: color 0.4s; 248 | -ms-transition: color 0.4s; 249 | -o-transition: color 0.4s; 250 | transition: color 0.4s; 251 | } 252 | 253 | #todo-list li.completed label { 254 | color: #a9a9a9; 255 | text-decoration: line-through; 256 | } 257 | 258 | #todo-list li .destroy { 259 | display: none; 260 | position: absolute; 261 | top: 0; 262 | right: 10px; 263 | bottom: 0; 264 | width: 40px; 265 | height: 40px; 266 | margin: auto 0; 267 | font-size: 22px; 268 | color: #a88a8a; 269 | -webkit-transition: all 0.2s; 270 | -moz-transition: all 0.2s; 271 | -ms-transition: all 0.2s; 272 | -o-transition: all 0.2s; 273 | transition: all 0.2s; 274 | } 275 | 276 | #todo-list li .destroy:hover { 277 | text-shadow: 0 0 1px #000, 278 | 0 0 10px rgba(199, 107, 107, 0.8); 279 | -webkit-transform: scale(1.3); 280 | -moz-transform: scale(1.3); 281 | -ms-transform: scale(1.3); 282 | -o-transform: scale(1.3); 283 | transform: scale(1.3); 284 | } 285 | 286 | #todo-list li .destroy:after { 287 | content: '?'; 288 | } 289 | 290 | #todo-list li:hover .destroy { 291 | display: block; 292 | } 293 | 294 | #todo-list li .edit { 295 | display: none; 296 | } 297 | 298 | #todo-list li.editing:last-child { 299 | margin-bottom: -1px; 300 | } 301 | 302 | #footer { 303 | color: #777; 304 | padding: 0 15px; 305 | position: absolute; 306 | right: 0; 307 | bottom: -31px; 308 | left: 0; 309 | height: 20px; 310 | z-index: 1; 311 | text-align: center; 312 | } 313 | 314 | #footer:before { 315 | content: ''; 316 | position: absolute; 317 | right: 0; 318 | bottom: 31px; 319 | left: 0; 320 | height: 50px; 321 | z-index: -1; 322 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 323 | 0 6px 0 -3px rgba(255, 255, 255, 0.8), 324 | 0 7px 1px -3px rgba(0, 0, 0, 0.3), 325 | 0 43px 0 -6px rgba(255, 255, 255, 0.8), 326 | 0 44px 2px -6px rgba(0, 0, 0, 0.2); 327 | } 328 | 329 | #todo-count { 330 | float: left; 331 | text-align: left; 332 | } 333 | 334 | #filters { 335 | margin: 0; 336 | padding: 0; 337 | list-style: none; 338 | position: absolute; 339 | right: 0; 340 | left: 0; 341 | } 342 | 343 | #filters li { 344 | display: inline; 345 | } 346 | 347 | #filters li a { 348 | color: #83756f; 349 | margin: 2px; 350 | text-decoration: none; 351 | } 352 | 353 | #filters li a.selected { 354 | font-weight: bold; 355 | } 356 | 357 | #clear-completed { 358 | float: right; 359 | position: relative; 360 | line-height: 20px; 361 | text-decoration: none; 362 | background: rgba(0, 0, 0, 0.1); 363 | font-size: 11px; 364 | padding: 0 10px; 365 | border-radius: 3px; 366 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); 367 | } 368 | 369 | #clear-completed:hover { 370 | background: rgba(0, 0, 0, 0.15); 371 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); 372 | } 373 | 374 | #info { 375 | margin: 65px auto 0; 376 | color: #a6a6a6; 377 | font-size: 12px; 378 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); 379 | text-align: center; 380 | } 381 | 382 | #info a { 383 | color: inherit; 384 | } 385 | 386 | /* 387 | Hack to remove background from Mobile Safari. 388 | Can't use it globally since it destroys checkboxes in Firefox and Opera 389 | */ 390 | @media screen and (-webkit-min-device-pixel-ratio:0) { 391 | #toggle-all, 392 | #todo-list li .toggle { 393 | background: none; 394 | } 395 | 396 | #todo-list li .toggle { 397 | height: 40px; 398 | } 399 | 400 | #toggle-all { 401 | top: -56px; 402 | left: -15px; 403 | width: 65px; 404 | height: 41px; 405 | -webkit-transform: rotate(90deg); 406 | transform: rotate(90deg); 407 | -webkit-appearance: none; 408 | appearance: none; 409 | } 410 | } 411 | 412 | .hidden{ 413 | display:none; 414 | } -------------------------------------------------------------------------------- /static/js/libs/handlebars-1.1.2.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | handlebars v1.1.2 4 | 5 | Copyright (C) 2011 by Yehuda Katz 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | @license 26 | */ 27 | var Handlebars = (function() { 28 | // handlebars/safe-string.js 29 | var __module4__ = (function() { 30 | "use strict"; 31 | var __exports__; 32 | // Build out our basic SafeString type 33 | function SafeString(string) { 34 | this.string = string; 35 | } 36 | 37 | SafeString.prototype.toString = function() { 38 | return "" + this.string; 39 | }; 40 | 41 | __exports__ = SafeString; 42 | return __exports__; 43 | })(); 44 | 45 | // handlebars/utils.js 46 | var __module3__ = (function(__dependency1__) { 47 | "use strict"; 48 | var __exports__ = {}; 49 | var SafeString = __dependency1__; 50 | 51 | var escape = { 52 | "&": "&", 53 | "<": "<", 54 | ">": ">", 55 | '"': """, 56 | "'": "'", 57 | "`": "`" 58 | }; 59 | 60 | var badChars = /[&<>"'`]/g; 61 | var possible = /[&<>"'`]/; 62 | 63 | function escapeChar(chr) { 64 | return escape[chr] || "&"; 65 | } 66 | 67 | function extend(obj, value) { 68 | for(var key in value) { 69 | if(value.hasOwnProperty(key)) { 70 | obj[key] = value[key]; 71 | } 72 | } 73 | } 74 | 75 | __exports__.extend = extend;var toString = Object.prototype.toString; 76 | __exports__.toString = toString; 77 | // Sourced from lodash 78 | // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt 79 | var isFunction = function(value) { 80 | return typeof value === 'function'; 81 | }; 82 | // fallback for older versions of Chrome and Safari 83 | if (isFunction(/x/)) { 84 | isFunction = function(value) { 85 | return typeof value === 'function' && toString.call(value) === '[object Function]'; 86 | }; 87 | } 88 | var isFunction; 89 | __exports__.isFunction = isFunction; 90 | var isArray = Array.isArray || function(value) { 91 | return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; 92 | }; 93 | __exports__.isArray = isArray; 94 | 95 | function escapeExpression(string) { 96 | // don't escape SafeStrings, since they're already safe 97 | if (string instanceof SafeString) { 98 | return string.toString(); 99 | } else if (!string && string !== 0) { 100 | return ""; 101 | } 102 | 103 | // Force a string conversion as this will be done by the append regardless and 104 | // the regex test will do this transparently behind the scenes, causing issues if 105 | // an object's to string has escaped characters in it. 106 | string = "" + string; 107 | 108 | if(!possible.test(string)) { return string; } 109 | return string.replace(badChars, escapeChar); 110 | } 111 | 112 | __exports__.escapeExpression = escapeExpression;function isEmpty(value) { 113 | if (!value && value !== 0) { 114 | return true; 115 | } else if (isArray(value) && value.length === 0) { 116 | return true; 117 | } else { 118 | return false; 119 | } 120 | } 121 | 122 | __exports__.isEmpty = isEmpty; 123 | return __exports__; 124 | })(__module4__); 125 | 126 | // handlebars/exception.js 127 | var __module5__ = (function() { 128 | "use strict"; 129 | var __exports__; 130 | 131 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; 132 | 133 | function Exception(/* message */) { 134 | var tmp = Error.prototype.constructor.apply(this, arguments); 135 | 136 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. 137 | for (var idx = 0; idx < errorProps.length; idx++) { 138 | this[errorProps[idx]] = tmp[errorProps[idx]]; 139 | } 140 | } 141 | 142 | Exception.prototype = new Error(); 143 | 144 | __exports__ = Exception; 145 | return __exports__; 146 | })(); 147 | 148 | // handlebars/base.js 149 | var __module2__ = (function(__dependency1__, __dependency2__) { 150 | "use strict"; 151 | var __exports__ = {}; 152 | /*globals Exception, Utils */ 153 | var Utils = __dependency1__; 154 | var Exception = __dependency2__; 155 | 156 | var VERSION = "1.1.2"; 157 | __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; 158 | __exports__.COMPILER_REVISION = COMPILER_REVISION; 159 | var REVISION_CHANGES = { 160 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 161 | 2: '== 1.0.0-rc.3', 162 | 3: '== 1.0.0-rc.4', 163 | 4: '>= 1.0.0' 164 | }; 165 | __exports__.REVISION_CHANGES = REVISION_CHANGES; 166 | var isArray = Utils.isArray, 167 | isFunction = Utils.isFunction, 168 | toString = Utils.toString, 169 | objectType = '[object Object]'; 170 | 171 | function HandlebarsEnvironment(helpers, partials) { 172 | this.helpers = helpers || {}; 173 | this.partials = partials || {}; 174 | 175 | registerDefaultHelpers(this); 176 | } 177 | 178 | __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { 179 | constructor: HandlebarsEnvironment, 180 | 181 | logger: logger, 182 | log: log, 183 | 184 | registerHelper: function(name, fn, inverse) { 185 | if (toString.call(name) === objectType) { 186 | if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } 187 | Utils.extend(this.helpers, name); 188 | } else { 189 | if (inverse) { fn.not = inverse; } 190 | this.helpers[name] = fn; 191 | } 192 | }, 193 | 194 | registerPartial: function(name, str) { 195 | if (toString.call(name) === objectType) { 196 | Utils.extend(this.partials, name); 197 | } else { 198 | this.partials[name] = str; 199 | } 200 | } 201 | }; 202 | 203 | function registerDefaultHelpers(instance) { 204 | instance.registerHelper('helperMissing', function(arg) { 205 | if(arguments.length === 2) { 206 | return undefined; 207 | } else { 208 | throw new Error("Missing helper: '" + arg + "'"); 209 | } 210 | }); 211 | 212 | instance.registerHelper('blockHelperMissing', function(context, options) { 213 | var inverse = options.inverse || function() {}, fn = options.fn; 214 | 215 | if (isFunction(context)) { context = context.call(this); } 216 | 217 | if(context === true) { 218 | return fn(this); 219 | } else if(context === false || context == null) { 220 | return inverse(this); 221 | } else if (isArray(context)) { 222 | if(context.length > 0) { 223 | return instance.helpers.each(context, options); 224 | } else { 225 | return inverse(this); 226 | } 227 | } else { 228 | return fn(context); 229 | } 230 | }); 231 | 232 | instance.registerHelper('each', function(context, options) { 233 | var fn = options.fn, inverse = options.inverse; 234 | var i = 0, ret = "", data; 235 | 236 | if (isFunction(context)) { context = context.call(this); } 237 | 238 | if (options.data) { 239 | data = createFrame(options.data); 240 | } 241 | 242 | if(context && typeof context === 'object') { 243 | if (isArray(context)) { 244 | for(var j = context.length; i 0) { throw new Exception("Invalid path: " + original); } 614 | else if (part === "..") { depth++; } 615 | else { this.isScoped = true; } 616 | } 617 | else { dig.push(part); } 618 | } 619 | 620 | this.original = original; 621 | this.parts = dig; 622 | this.string = dig.join('.'); 623 | this.depth = depth; 624 | 625 | // an ID is simple if it only has one part, and that part is not 626 | // `..` or `this`. 627 | this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; 628 | 629 | this.stringModeValue = this.string; 630 | } 631 | 632 | __exports__.IdNode = IdNode;function PartialNameNode(name) { 633 | this.type = "PARTIAL_NAME"; 634 | this.name = name.original; 635 | } 636 | 637 | __exports__.PartialNameNode = PartialNameNode;function DataNode(id) { 638 | this.type = "DATA"; 639 | this.id = id; 640 | } 641 | 642 | __exports__.DataNode = DataNode;function StringNode(string) { 643 | this.type = "STRING"; 644 | this.original = 645 | this.string = 646 | this.stringModeValue = string; 647 | } 648 | 649 | __exports__.StringNode = StringNode;function IntegerNode(integer) { 650 | this.type = "INTEGER"; 651 | this.original = 652 | this.integer = integer; 653 | this.stringModeValue = Number(integer); 654 | } 655 | 656 | __exports__.IntegerNode = IntegerNode;function BooleanNode(bool) { 657 | this.type = "BOOLEAN"; 658 | this.bool = bool; 659 | this.stringModeValue = bool === "true"; 660 | } 661 | 662 | __exports__.BooleanNode = BooleanNode;function CommentNode(comment) { 663 | this.type = "comment"; 664 | this.comment = comment; 665 | } 666 | 667 | __exports__.CommentNode = CommentNode; 668 | return __exports__; 669 | })(__module5__); 670 | 671 | // handlebars/compiler/parser.js 672 | var __module9__ = (function() { 673 | "use strict"; 674 | var __exports__; 675 | /* Jison generated parser */ 676 | var handlebars = (function(){ 677 | var parser = {trace: function trace() { }, 678 | yy: {}, 679 | symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1}, 680 | terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"}, 681 | productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]], 682 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { 683 | 684 | var $0 = $$.length - 1; 685 | switch (yystate) { 686 | case 1: return new yy.ProgramNode($$[$0-1]); 687 | break; 688 | case 2: return new yy.ProgramNode([]); 689 | break; 690 | case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]); 691 | break; 692 | case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]); 693 | break; 694 | case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []); 695 | break; 696 | case 6:this.$ = new yy.ProgramNode($$[$0]); 697 | break; 698 | case 7:this.$ = new yy.ProgramNode([]); 699 | break; 700 | case 8:this.$ = new yy.ProgramNode([]); 701 | break; 702 | case 9:this.$ = [$$[$0]]; 703 | break; 704 | case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 705 | break; 706 | case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]); 707 | break; 708 | case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]); 709 | break; 710 | case 13:this.$ = $$[$0]; 711 | break; 712 | case 14:this.$ = $$[$0]; 713 | break; 714 | case 15:this.$ = new yy.ContentNode($$[$0]); 715 | break; 716 | case 16:this.$ = new yy.CommentNode($$[$0]); 717 | break; 718 | case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); 719 | break; 720 | case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); 721 | break; 722 | case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])}; 723 | break; 724 | case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); 725 | break; 726 | case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); 727 | break; 728 | case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0])); 729 | break; 730 | case 23:this.$ = stripFlags($$[$0-1], $$[$0]); 731 | break; 732 | case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]; 733 | break; 734 | case 25:this.$ = [[$$[$0]], null]; 735 | break; 736 | case 26:this.$ = $$[$0]; 737 | break; 738 | case 27:this.$ = new yy.StringNode($$[$0]); 739 | break; 740 | case 28:this.$ = new yy.IntegerNode($$[$0]); 741 | break; 742 | case 29:this.$ = new yy.BooleanNode($$[$0]); 743 | break; 744 | case 30:this.$ = $$[$0]; 745 | break; 746 | case 31:this.$ = new yy.HashNode($$[$0]); 747 | break; 748 | case 32:this.$ = [$$[$0-2], $$[$0]]; 749 | break; 750 | case 33:this.$ = new yy.PartialNameNode($$[$0]); 751 | break; 752 | case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0])); 753 | break; 754 | case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0])); 755 | break; 756 | case 36:this.$ = new yy.DataNode($$[$0]); 757 | break; 758 | case 37:this.$ = new yy.IdNode($$[$0]); 759 | break; 760 | case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; 761 | break; 762 | case 39:this.$ = [{part: $$[$0]}]; 763 | break; 764 | case 42:this.$ = []; 765 | break; 766 | case 43:$$[$0-1].push($$[$0]); 767 | break; 768 | case 46:this.$ = [$$[$0]]; 769 | break; 770 | case 47:$$[$0-1].push($$[$0]); 771 | break; 772 | } 773 | }, 774 | table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}], 775 | defaultActions: {3:[2,2],16:[2,1],50:[2,41]}, 776 | parseError: function parseError(str, hash) { 777 | throw new Error(str); 778 | }, 779 | parse: function parse(input) { 780 | var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; 781 | this.lexer.setInput(input); 782 | this.lexer.yy = this.yy; 783 | this.yy.lexer = this.lexer; 784 | this.yy.parser = this; 785 | if (typeof this.lexer.yylloc == "undefined") 786 | this.lexer.yylloc = {}; 787 | var yyloc = this.lexer.yylloc; 788 | lstack.push(yyloc); 789 | var ranges = this.lexer.options && this.lexer.options.ranges; 790 | if (typeof this.yy.parseError === "function") 791 | this.parseError = this.yy.parseError; 792 | function popStack(n) { 793 | stack.length = stack.length - 2 * n; 794 | vstack.length = vstack.length - n; 795 | lstack.length = lstack.length - n; 796 | } 797 | function lex() { 798 | var token; 799 | token = self.lexer.lex() || 1; 800 | if (typeof token !== "number") { 801 | token = self.symbols_[token] || token; 802 | } 803 | return token; 804 | } 805 | var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; 806 | while (true) { 807 | state = stack[stack.length - 1]; 808 | if (this.defaultActions[state]) { 809 | action = this.defaultActions[state]; 810 | } else { 811 | if (symbol === null || typeof symbol == "undefined") { 812 | symbol = lex(); 813 | } 814 | action = table[state] && table[state][symbol]; 815 | } 816 | if (typeof action === "undefined" || !action.length || !action[0]) { 817 | var errStr = ""; 818 | if (!recovering) { 819 | expected = []; 820 | for (p in table[state]) 821 | if (this.terminals_[p] && p > 2) { 822 | expected.push("'" + this.terminals_[p] + "'"); 823 | } 824 | if (this.lexer.showPosition) { 825 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; 826 | } else { 827 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); 828 | } 829 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 830 | } 831 | } 832 | if (action[0] instanceof Array && action.length > 1) { 833 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); 834 | } 835 | switch (action[0]) { 836 | case 1: 837 | stack.push(symbol); 838 | vstack.push(this.lexer.yytext); 839 | lstack.push(this.lexer.yylloc); 840 | stack.push(action[1]); 841 | symbol = null; 842 | if (!preErrorSymbol) { 843 | yyleng = this.lexer.yyleng; 844 | yytext = this.lexer.yytext; 845 | yylineno = this.lexer.yylineno; 846 | yyloc = this.lexer.yylloc; 847 | if (recovering > 0) 848 | recovering--; 849 | } else { 850 | symbol = preErrorSymbol; 851 | preErrorSymbol = null; 852 | } 853 | break; 854 | case 2: 855 | len = this.productions_[action[1]][1]; 856 | yyval.$ = vstack[vstack.length - len]; 857 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; 858 | if (ranges) { 859 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; 860 | } 861 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 862 | if (typeof r !== "undefined") { 863 | return r; 864 | } 865 | if (len) { 866 | stack = stack.slice(0, -1 * len * 2); 867 | vstack = vstack.slice(0, -1 * len); 868 | lstack = lstack.slice(0, -1 * len); 869 | } 870 | stack.push(this.productions_[action[1]][0]); 871 | vstack.push(yyval.$); 872 | lstack.push(yyval._$); 873 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; 874 | stack.push(newState); 875 | break; 876 | case 3: 877 | return true; 878 | } 879 | } 880 | return true; 881 | } 882 | }; 883 | 884 | 885 | function stripFlags(open, close) { 886 | return { 887 | left: open[2] === '~', 888 | right: close[0] === '~' || close[1] === '~' 889 | }; 890 | } 891 | 892 | /* Jison generated lexer */ 893 | var lexer = (function(){ 894 | var lexer = ({EOF:1, 895 | parseError:function parseError(str, hash) { 896 | if (this.yy.parser) { 897 | this.yy.parser.parseError(str, hash); 898 | } else { 899 | throw new Error(str); 900 | } 901 | }, 902 | setInput:function (input) { 903 | this._input = input; 904 | this._more = this._less = this.done = false; 905 | this.yylineno = this.yyleng = 0; 906 | this.yytext = this.matched = this.match = ''; 907 | this.conditionStack = ['INITIAL']; 908 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 909 | if (this.options.ranges) this.yylloc.range = [0,0]; 910 | this.offset = 0; 911 | return this; 912 | }, 913 | input:function () { 914 | var ch = this._input[0]; 915 | this.yytext += ch; 916 | this.yyleng++; 917 | this.offset++; 918 | this.match += ch; 919 | this.matched += ch; 920 | var lines = ch.match(/(?:\r\n?|\n).*/g); 921 | if (lines) { 922 | this.yylineno++; 923 | this.yylloc.last_line++; 924 | } else { 925 | this.yylloc.last_column++; 926 | } 927 | if (this.options.ranges) this.yylloc.range[1]++; 928 | 929 | this._input = this._input.slice(1); 930 | return ch; 931 | }, 932 | unput:function (ch) { 933 | var len = ch.length; 934 | var lines = ch.split(/(?:\r\n?|\n)/g); 935 | 936 | this._input = ch + this._input; 937 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1); 938 | //this.yyleng -= len; 939 | this.offset -= len; 940 | var oldLines = this.match.split(/(?:\r\n?|\n)/g); 941 | this.match = this.match.substr(0, this.match.length-1); 942 | this.matched = this.matched.substr(0, this.matched.length-1); 943 | 944 | if (lines.length-1) this.yylineno -= lines.length-1; 945 | var r = this.yylloc.range; 946 | 947 | this.yylloc = {first_line: this.yylloc.first_line, 948 | last_line: this.yylineno+1, 949 | first_column: this.yylloc.first_column, 950 | last_column: lines ? 951 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: 952 | this.yylloc.first_column - len 953 | }; 954 | 955 | if (this.options.ranges) { 956 | this.yylloc.range = [r[0], r[0] + this.yyleng - len]; 957 | } 958 | return this; 959 | }, 960 | more:function () { 961 | this._more = true; 962 | return this; 963 | }, 964 | less:function (n) { 965 | this.unput(this.match.slice(n)); 966 | }, 967 | pastInput:function () { 968 | var past = this.matched.substr(0, this.matched.length - this.match.length); 969 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 970 | }, 971 | upcomingInput:function () { 972 | var next = this.match; 973 | if (next.length < 20) { 974 | next += this._input.substr(0, 20-next.length); 975 | } 976 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 977 | }, 978 | showPosition:function () { 979 | var pre = this.pastInput(); 980 | var c = new Array(pre.length + 1).join("-"); 981 | return pre + this.upcomingInput() + "\n" + c+"^"; 982 | }, 983 | next:function () { 984 | if (this.done) { 985 | return this.EOF; 986 | } 987 | if (!this._input) this.done = true; 988 | 989 | var token, 990 | match, 991 | tempMatch, 992 | index, 993 | col, 994 | lines; 995 | if (!this._more) { 996 | this.yytext = ''; 997 | this.match = ''; 998 | } 999 | var rules = this._currentRules(); 1000 | for (var i=0;i < rules.length; i++) { 1001 | tempMatch = this._input.match(this.rules[rules[i]]); 1002 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 1003 | match = tempMatch; 1004 | index = i; 1005 | if (!this.options.flex) break; 1006 | } 1007 | } 1008 | if (match) { 1009 | lines = match[0].match(/(?:\r\n?|\n).*/g); 1010 | if (lines) this.yylineno += lines.length; 1011 | this.yylloc = {first_line: this.yylloc.last_line, 1012 | last_line: this.yylineno+1, 1013 | first_column: this.yylloc.last_column, 1014 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; 1015 | this.yytext += match[0]; 1016 | this.match += match[0]; 1017 | this.matches = match; 1018 | this.yyleng = this.yytext.length; 1019 | if (this.options.ranges) { 1020 | this.yylloc.range = [this.offset, this.offset += this.yyleng]; 1021 | } 1022 | this._more = false; 1023 | this._input = this._input.slice(match[0].length); 1024 | this.matched += match[0]; 1025 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 1026 | if (this.done && this._input) this.done = false; 1027 | if (token) return token; 1028 | else return; 1029 | } 1030 | if (this._input === "") { 1031 | return this.EOF; 1032 | } else { 1033 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 1034 | {text: "", token: null, line: this.yylineno}); 1035 | } 1036 | }, 1037 | lex:function lex() { 1038 | var r = this.next(); 1039 | if (typeof r !== 'undefined') { 1040 | return r; 1041 | } else { 1042 | return this.lex(); 1043 | } 1044 | }, 1045 | begin:function begin(condition) { 1046 | this.conditionStack.push(condition); 1047 | }, 1048 | popState:function popState() { 1049 | return this.conditionStack.pop(); 1050 | }, 1051 | _currentRules:function _currentRules() { 1052 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 1053 | }, 1054 | topState:function () { 1055 | return this.conditionStack[this.conditionStack.length-2]; 1056 | }, 1057 | pushState:function begin(condition) { 1058 | this.begin(condition); 1059 | }}); 1060 | lexer.options = {}; 1061 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 1062 | 1063 | 1064 | function strip(start, end) { 1065 | return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); 1066 | } 1067 | 1068 | 1069 | var YYSTATE=YY_START 1070 | switch($avoiding_name_collisions) { 1071 | case 0: 1072 | if(yy_.yytext.slice(-2) === "\\\\") { 1073 | strip(0,1); 1074 | this.begin("mu"); 1075 | } else if(yy_.yytext.slice(-1) === "\\") { 1076 | strip(0,1); 1077 | this.begin("emu"); 1078 | } else { 1079 | this.begin("mu"); 1080 | } 1081 | if(yy_.yytext) return 14; 1082 | 1083 | break; 1084 | case 1:return 14; 1085 | break; 1086 | case 2: 1087 | if(yy_.yytext.slice(-1) !== "\\") this.popState(); 1088 | if(yy_.yytext.slice(-1) === "\\") strip(0,1); 1089 | return 14; 1090 | 1091 | break; 1092 | case 3:strip(0,4); this.popState(); return 15; 1093 | break; 1094 | case 4:return 25; 1095 | break; 1096 | case 5:return 16; 1097 | break; 1098 | case 6:return 20; 1099 | break; 1100 | case 7:return 19; 1101 | break; 1102 | case 8:return 19; 1103 | break; 1104 | case 9:return 23; 1105 | break; 1106 | case 10:return 22; 1107 | break; 1108 | case 11:this.popState(); this.begin('com'); 1109 | break; 1110 | case 12:strip(3,5); this.popState(); return 15; 1111 | break; 1112 | case 13:return 22; 1113 | break; 1114 | case 14:return 39; 1115 | break; 1116 | case 15:return 38; 1117 | break; 1118 | case 16:return 38; 1119 | break; 1120 | case 17:return 42; 1121 | break; 1122 | case 18:/*ignore whitespace*/ 1123 | break; 1124 | case 19:this.popState(); return 24; 1125 | break; 1126 | case 20:this.popState(); return 18; 1127 | break; 1128 | case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32; 1129 | break; 1130 | case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32; 1131 | break; 1132 | case 23:return 40; 1133 | break; 1134 | case 24:return 34; 1135 | break; 1136 | case 25:return 34; 1137 | break; 1138 | case 26:return 33; 1139 | break; 1140 | case 27:return 38; 1141 | break; 1142 | case 28:yy_.yytext = strip(1,2); return 38; 1143 | break; 1144 | case 29:return 'INVALID'; 1145 | break; 1146 | case 30:return 5; 1147 | break; 1148 | } 1149 | }; 1150 | lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; 1151 | lexer.conditions = {"mu":{"rules":[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,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}}; 1152 | return lexer;})() 1153 | parser.lexer = lexer; 1154 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; 1155 | return new Parser; 1156 | })();__exports__ = handlebars; 1157 | return __exports__; 1158 | })(); 1159 | 1160 | // handlebars/compiler/base.js 1161 | var __module8__ = (function(__dependency1__, __dependency2__) { 1162 | "use strict"; 1163 | var __exports__ = {}; 1164 | var parser = __dependency1__; 1165 | var AST = __dependency2__; 1166 | 1167 | __exports__.parser = parser; 1168 | 1169 | function parse(input) { 1170 | // Just return if an already-compile AST was passed in. 1171 | if(input.constructor === AST.ProgramNode) { return input; } 1172 | 1173 | parser.yy = AST; 1174 | return parser.parse(input); 1175 | } 1176 | 1177 | __exports__.parse = parse; 1178 | return __exports__; 1179 | })(__module9__, __module7__); 1180 | 1181 | // handlebars/compiler/javascript-compiler.js 1182 | var __module11__ = (function(__dependency1__) { 1183 | "use strict"; 1184 | var __exports__; 1185 | var COMPILER_REVISION = __dependency1__.COMPILER_REVISION; 1186 | var REVISION_CHANGES = __dependency1__.REVISION_CHANGES; 1187 | var log = __dependency1__.log; 1188 | 1189 | function Literal(value) { 1190 | this.value = value; 1191 | } 1192 | 1193 | function JavaScriptCompiler() {} 1194 | 1195 | JavaScriptCompiler.prototype = { 1196 | // PUBLIC API: You can override these methods in a subclass to provide 1197 | // alternative compiled forms for name lookup and buffering semantics 1198 | nameLookup: function(parent, name /* , type*/) { 1199 | var wrap, 1200 | ret; 1201 | if (parent.indexOf('depth') === 0) { 1202 | wrap = true; 1203 | } 1204 | 1205 | if (/^[0-9]+$/.test(name)) { 1206 | ret = parent + "[" + name + "]"; 1207 | } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { 1208 | ret = parent + "." + name; 1209 | } 1210 | else { 1211 | ret = parent + "['" + name + "']"; 1212 | } 1213 | 1214 | if (wrap) { 1215 | return '(' + parent + ' && ' + ret + ')'; 1216 | } else { 1217 | return ret; 1218 | } 1219 | }, 1220 | 1221 | appendToBuffer: function(string) { 1222 | if (this.environment.isSimple) { 1223 | return "return " + string + ";"; 1224 | } else { 1225 | return { 1226 | appendToBuffer: true, 1227 | content: string, 1228 | toString: function() { return "buffer += " + string + ";"; } 1229 | }; 1230 | } 1231 | }, 1232 | 1233 | initializeBuffer: function() { 1234 | return this.quotedString(""); 1235 | }, 1236 | 1237 | namespace: "Handlebars", 1238 | // END PUBLIC API 1239 | 1240 | compile: function(environment, options, context, asObject) { 1241 | this.environment = environment; 1242 | this.options = options || {}; 1243 | 1244 | log('debug', this.environment.disassemble() + "\n\n"); 1245 | 1246 | this.name = this.environment.name; 1247 | this.isChild = !!context; 1248 | this.context = context || { 1249 | programs: [], 1250 | environments: [], 1251 | aliases: { } 1252 | }; 1253 | 1254 | this.preamble(); 1255 | 1256 | this.stackSlot = 0; 1257 | this.stackVars = []; 1258 | this.registers = { list: [] }; 1259 | this.compileStack = []; 1260 | this.inlineStack = []; 1261 | 1262 | this.compileChildren(environment, options); 1263 | 1264 | var opcodes = environment.opcodes, opcode; 1265 | 1266 | this.i = 0; 1267 | 1268 | for(var l=opcodes.length; this.i 0) { 1319 | this.source[1] = this.source[1] + ", " + locals.join(", "); 1320 | } 1321 | 1322 | // Generate minimizer alias mappings 1323 | if (!this.isChild) { 1324 | for (var alias in this.context.aliases) { 1325 | if (this.context.aliases.hasOwnProperty(alias)) { 1326 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; 1327 | } 1328 | } 1329 | } 1330 | 1331 | if (this.source[1]) { 1332 | this.source[1] = "var " + this.source[1].substring(2) + ";"; 1333 | } 1334 | 1335 | // Merge children 1336 | if (!this.isChild) { 1337 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; 1338 | } 1339 | 1340 | if (!this.environment.isSimple) { 1341 | this.pushSource("return buffer;"); 1342 | } 1343 | 1344 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; 1345 | 1346 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } 1912 | return this.topStackName(); 1913 | }, 1914 | topStackName: function() { 1915 | return "stack" + this.stackSlot; 1916 | }, 1917 | flushInline: function() { 1918 | var inlineStack = this.inlineStack; 1919 | if (inlineStack.length) { 1920 | this.inlineStack = []; 1921 | for (var i = 0, len = inlineStack.length; i < len; i++) { 1922 | var entry = inlineStack[i]; 1923 | if (entry instanceof Literal) { 1924 | this.compileStack.push(entry); 1925 | } else { 1926 | this.pushStack(entry); 1927 | } 1928 | } 1929 | } 1930 | }, 1931 | isInline: function() { 1932 | return this.inlineStack.length; 1933 | }, 1934 | 1935 | popStack: function(wrapped) { 1936 | var inline = this.isInline(), 1937 | item = (inline ? this.inlineStack : this.compileStack).pop(); 1938 | 1939 | if (!wrapped && (item instanceof Literal)) { 1940 | return item.value; 1941 | } else { 1942 | if (!inline) { 1943 | this.stackSlot--; 1944 | } 1945 | return item; 1946 | } 1947 | }, 1948 | 1949 | topStack: function(wrapped) { 1950 | var stack = (this.isInline() ? this.inlineStack : this.compileStack), 1951 | item = stack[stack.length - 1]; 1952 | 1953 | if (!wrapped && (item instanceof Literal)) { 1954 | return item.value; 1955 | } else { 1956 | return item; 1957 | } 1958 | }, 1959 | 1960 | quotedString: function(str) { 1961 | return '"' + str 1962 | .replace(/\\/g, '\\\\') 1963 | .replace(/"/g, '\\"') 1964 | .replace(/\n/g, '\\n') 1965 | .replace(/\r/g, '\\r') 1966 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 1967 | .replace(/\u2029/g, '\\u2029') + '"'; 1968 | }, 1969 | 1970 | setupHelper: function(paramSize, name, missingParams) { 1971 | var params = []; 1972 | this.setupParams(paramSize, params, missingParams); 1973 | var foundHelper = this.nameLookup('helpers', name, 'helper'); 1974 | 1975 | return { 1976 | params: params, 1977 | name: foundHelper, 1978 | callParams: ["depth0"].concat(params).join(", "), 1979 | helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") 1980 | }; 1981 | }, 1982 | 1983 | // the params and contexts arguments are passed in arrays 1984 | // to fill in 1985 | setupParams: function(paramSize, params, useRegister) { 1986 | var options = [], contexts = [], types = [], param, inverse, program; 1987 | 1988 | options.push("hash:" + this.popStack()); 1989 | 1990 | inverse = this.popStack(); 1991 | program = this.popStack(); 1992 | 1993 | // Avoid setting fn and inverse if neither are set. This allows 1994 | // helpers to do a check for `if (options.fn)` 1995 | if (program || inverse) { 1996 | if (!program) { 1997 | this.context.aliases.self = "this"; 1998 | program = "self.noop"; 1999 | } 2000 | 2001 | if (!inverse) { 2002 | this.context.aliases.self = "this"; 2003 | inverse = "self.noop"; 2004 | } 2005 | 2006 | options.push("inverse:" + inverse); 2007 | options.push("fn:" + program); 2008 | } 2009 | 2010 | for(var i=0; i