├── .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