quick_script 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,695 @@
1
+ @initKO = ->
2
+ ko.bindingHandlers.fadeVisible =
3
+ init : (element, valueAccessor) ->
4
+ shouldDisplay = ko.utils.unwrapObservable(valueAccessor())
5
+ if shouldDisplay then $(element).show() else $(element).hide()
6
+ update : (element, value) ->
7
+ shouldDisplay = value()
8
+ if shouldDisplay then $(element).fadeIn('slow') else $(element).hide()
9
+
10
+ ko.bindingHandlers.slideVisible =
11
+ init : (element, valueAccessor) ->
12
+ shouldDisplay = ko.utils.unwrapObservable(valueAccessor())
13
+ if shouldDisplay then $(element).show() else $(element).hide()
14
+ update : (element, valueAccessor) ->
15
+ shouldDisplay = ko.utils.unwrapObservable(valueAccessor())
16
+ if shouldDisplay then $(element).slideDown('slow') else $(element).slideUp()
17
+
18
+ ko.bindingHandlers.handleEnter =
19
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
20
+ $(element).keypress (ev)->
21
+ if (ev.keyCode == 13)
22
+ action = valueAccessor()
23
+ val = bindingsAccessor().value
24
+ val($(element).val())
25
+ action.call(viewModel)
26
+ return false
27
+
28
+ ko.bindingHandlers.validate =
29
+ init : (element, valueAccessor) ->
30
+ opts = valueAccessor()
31
+ $(element).blur ->
32
+ if opts.test()
33
+ $(element).removeClass(opts.err_css)
34
+ $(element).addClass(opts.ok_css)
35
+ else
36
+ $(element).removeClass(opts.ok_css)
37
+ $(element).addClass(opts.err_css)
38
+ opts.on_err() if opts.on_err?
39
+
40
+ ko.bindingHandlers.cropImage =
41
+ init : (element, valueAccessor) ->
42
+ opts = valueAccessor()
43
+ $(element).css
44
+ background : 'url(' + ko.utils.unwrapObservable(opts[0]) + ')',
45
+ backgroundSize: 'cover',
46
+ 'background-position': 'center',
47
+ backgroundColor: '#FFF',
48
+ width: opts[1],
49
+ height: opts[2],
50
+ display: 'inline-block'
51
+
52
+ ko.bindingHandlers.tinymce =
53
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
54
+ options = {
55
+ width : $(element).width(),
56
+ height : $(element).height(),
57
+ content_css : '/assets/screen/tinymce.css',
58
+ theme : 'advanced',
59
+ theme_advanced_toolbar_location : 'top',
60
+ theme_advanced_buttons1 : 'bold, italic, underline, separator, undo, redo, separator, bullist, numlist, blockquote, separator, justifyleft, justifycenter, justifyright, separator, image, link, unlink, separator, code',
61
+ theme_advanced_buttons2 : '',
62
+ theme_advanced_buttons3 : ''
63
+ }
64
+ val = valueAccessor()
65
+ options.setup = (ed) ->
66
+ ed.onChange.add (ed, l) ->
67
+ val(l.content)
68
+ # handle destroying an editor (based on what jQuery plugin does)
69
+ ko.utils.domNodeDisposal.addDisposeCallback element, ->
70
+ ed = tinyMCE.get(element.id)
71
+ if (ed)
72
+ ed.remove()
73
+ console.log('removing tinymce')
74
+
75
+ setTimeout ->
76
+ $(element).tinymce(options)
77
+ if ($(element).attr('name') != 'undefined')
78
+ ko.editors[$(element).attr('name')] = element.id
79
+ , 100
80
+ console.log('init tinymce')
81
+ update : (element, valueAccessor) ->
82
+ $(element).html(ko.utils.unwrapObservable(valueAccessor()))
83
+
84
+ ko.bindingHandlers.jsfileupload =
85
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
86
+ model = valueAccessor()
87
+ $(element).fileupload(model.input.options)
88
+ $(element).change (evt)->
89
+ model.input.files(evt.target.files)
90
+ model.fileupload = $(element).fileupload.bind($(element))
91
+ model.selectFile = ->
92
+ $(element).click()
93
+
94
+ ko.bindingHandlers.fileupload =
95
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
96
+ model = valueAccessor()
97
+ $(element).change (evt)->
98
+ model.input.files(evt.target.files)
99
+ model.selectFile = ->
100
+ $(element).click()
101
+
102
+ ko.bindingHandlers.calendar =
103
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
104
+ $(element).fullCalendar('destroy')
105
+ $(element).fullCalendar(ko.utils.unwrapObservable(valueAccessor()))
106
+ viewModel.calendar = $(element).fullCalendar.bind($(element))
107
+
108
+ ko.bindingHandlers.center =
109
+ init : (element, valueAccessor, bindingsAccessor, viewModel) ->
110
+ setTimeout ->
111
+ $(element).center()
112
+ , 1
113
+
114
+ ko.bindingHandlers.progressbar =
115
+ update: (element, valueAccessor) ->
116
+ $(element).progressbar({value : ko.utils.unwrapObservable(valueAccessor())})
117
+
118
+ ko.bindingHandlers.placeholder =
119
+ init: (element, valueAccessor) ->
120
+ fn = ->
121
+ if ($(element).val().length > 0)
122
+ $(element).siblings('label').hide()
123
+ else
124
+ $(element).siblings('label').show()
125
+ $(element).live('blur change keyup', fn)
126
+ update: (element, valueAccessor) ->
127
+ if ($(element).val().length > 0)
128
+ $(element).siblings('label').hide()
129
+ else
130
+ $(element).siblings('label').show()
131
+
132
+
133
+ ko.absorbModel = (data, self) ->
134
+ for prop, val of data
135
+ continue if typeof(val) == "function"
136
+ if !self[prop]?
137
+ self[prop] = ko.observable(val)
138
+ else if (typeof(self[prop].handleData) == "function")
139
+ self[prop].handleData(val)
140
+ else
141
+ self[prop](val)
142
+ self.fields.pushOnce(prop)
143
+ self.model_state(ko.modelStates.READY)
144
+
145
+ ko.addFields = (fields, val, self) ->
146
+ for prop in fields
147
+ ko.addField prop, val, self
148
+
149
+ ko.addField = (field, val, valid_fn, self) ->
150
+ if !self?
151
+ self = valid_fn
152
+ valid_fn = null
153
+ if (typeof(self[field]) != "function")
154
+ if (val instanceof Array)
155
+ self[field] = ko.observableArray()
156
+ else
157
+ self[field] = ko.observable(val)
158
+
159
+ self["#{field}_valid"] = ko.computed( (-> (valid_fn.bind(self))(self[field]())), self) if valid_fn?
160
+ else
161
+ self[field](val)
162
+ if (typeof(field) == "string")
163
+ self.fields.pushOnce(field)
164
+
165
+ ko.addSubModel = (field, model, self) ->
166
+ if self[field]?
167
+ self[field].reset()
168
+ else
169
+ self[field] = new model({}, self)
170
+ self.fields.pushOnce(field) if typeof(field) == "string"
171
+
172
+ ko.intercepter = (observable, write_fn, self) ->
173
+ underlying_observable = observable
174
+ return ko.dependentObservable
175
+ read: underlying_observable,
176
+ write: (val) ->
177
+ if (val != underlying_observable())
178
+ write_fn.call(self, underlying_observable, underlying_observable(), val)
179
+
180
+ ko.dirtyFlag = (root, isInitiallyDirty) ->
181
+ result = ->
182
+ _initialState = ko.observable(ko.toJSON(root))
183
+ _isInitiallyDirty = ko.observable(isInitiallyDirty)
184
+
185
+ result.isDirty = ko.dependentObservable ->
186
+ return _isInitiallyDirty() || (_initialState() != ko.toJSON(root))
187
+
188
+ result.reset = ->
189
+ _initialState(ko.toJSON(root))
190
+ _isInitiallyDirty(false)
191
+
192
+ return result
193
+
194
+ ko.copyObject = (obj, fields) ->
195
+ ret = {}
196
+ for prop in fields
197
+ ret[prop] = obj[prop]
198
+ return ret
199
+
200
+ ko.modelStates = {}
201
+ ko.modelStates.READY = 1
202
+ ko.modelStates.LOADING = 2
203
+ ko.modelStates.SAVING = 3
204
+ ko.modelStates.EDITING = 4
205
+ ko.modelStates.INSERTING = 5
206
+ ko.modelStates.APPENDING = 6
207
+ ko.editors = {}
208
+
209
+ jQuery.fn.extend
210
+ to_s : ->
211
+ $('<div>').append(this.clone()).remove().html()
212
+ center : ->
213
+ this.css("position","absolute")
214
+ this.css("top", (($(window).height() - this.outerHeight()) / 2) + $(window).scrollTop() + "px")
215
+ this.css("left", (($(window).width() - this.outerWidth()) / 2) + $(window).scrollLeft() + "px")
216
+ return this
217
+ koBind : (viewModel) ->
218
+ this.each ->
219
+ ko.cleanNode(this)
220
+ ko.applyBindings(viewModel, this)
221
+ koClean : ->
222
+ this.each ->
223
+ ko.cleanNode(this)
224
+
225
+ jQuery.ajax_qs = (opts)->
226
+ data = new FormData()
227
+ req = new XMLHttpRequest()
228
+ for key, val of opts.data
229
+ data.append key, val
230
+ req.onreadystatechange = (ev)->
231
+ if req.readyState == 4
232
+ if req.status == 200
233
+ resp = eval("(" + req.responseText + ")")
234
+ opts.success(resp)
235
+ else
236
+ opts.error() if opts.error?
237
+ req.upload.addEventListener('error', opts.error) if opts.error?
238
+ req.upload.addEventListener('progress', opts.progress) if opts.progress?
239
+ req.open opts.type, opts.url, true
240
+ req.setRequestHeader 'X-CSRF-Token', jQuery('meta[name="csrf-token"]').attr('content')
241
+ req.send(data)
242
+ return req
243
+
244
+ class @Model
245
+ init : ->
246
+ extend : ->
247
+ constructor: (data, collection) ->
248
+ @fields = []
249
+ ko.addFields(['id'], '', this)
250
+ @events = {}
251
+ @adapter = new ModelAdapter()
252
+ @collection = collection
253
+ @db_state = ko.observable({})
254
+ @errors = ko.observable([])
255
+ @model_state = ko.observable(0)
256
+ @saveProgress = ko.observable(0)
257
+ @extend()
258
+ @init()
259
+ @is_ready = ko.dependentObservable ->
260
+ @model_state() == ko.modelStates.READY
261
+ , this
262
+ @is_loading = ko.dependentObservable ->
263
+ @model_state() == ko.modelStates.LOADING
264
+ , this
265
+ @is_saving = ko.dependentObservable ->
266
+ @model_state() == ko.modelStates.SAVING
267
+ , this
268
+ @is_editing = ko.dependentObservable ->
269
+ @model_state() == ko.modelStates.EDITING
270
+ , this
271
+ @is_new = ko.dependentObservable ->
272
+ @id() == ''
273
+ , this
274
+ @is_dirty = ko.dependentObservable ->
275
+ JSON.stringify(@db_state()) != JSON.stringify(@toJS())
276
+ , this
277
+ @has_errors = ko.dependentObservable ->
278
+ @errors().length > 0
279
+ , this
280
+ @handleData(data || {})
281
+ handleData : (resp) ->
282
+ ko.absorbModel(resp, this)
283
+ @db_state(@toJS())
284
+ load : (opts, callback)->
285
+ @adapter.load
286
+ data : opts
287
+ success : (resp)=>
288
+ ret_data = if opts.fields? then ko.copyObject(resp.data, opts.fields) else resp.data
289
+ @handleData(ret_data)
290
+ callback(resp) if callback?
291
+ @model_state(ko.modelStates.LOADING)
292
+ reloadFields : (fields, callback)->
293
+ opts = ko.copyObject(@toJS(), @load_fields)
294
+ opts['fields'] = fields
295
+ @load(opts, callback)
296
+ reload : (callback)->
297
+ opts = ko.copyObject(@toJS(), @load_fields)
298
+ @load(opts, callback)
299
+ save : (fields, callback) ->
300
+ console.log("Saving fields #{fields}")
301
+ if (@model_state() != ko.modelStates.READY)
302
+ console.log("Save postponed.")
303
+ return
304
+ opts = @toJS(fields)
305
+ opts['id'] = @id()
306
+ @adapter.save
307
+ data: opts
308
+ progress : (ev)=>
309
+ @saveProgress( Math.floor( ev.loaded / ev.total * 100 ) )
310
+ success : (resp)=>
311
+ @handleData(resp.data)
312
+ callback(resp) if callback?
313
+ error : =>
314
+ console.log("Save error encountered")
315
+ @model_state(ko.modelStates.READY)
316
+ @model_state(ko.modelStates.SAVING)
317
+ reset : ->
318
+ @model_state(ko.modelStates.LOADING)
319
+ @id('')
320
+ @init()
321
+ @db_state(@toJS())
322
+ @saveProgress(0)
323
+ @model_state(ko.modelStates.READY)
324
+ delete : (fields, callback)=>
325
+ fields ||= ['id']
326
+ if (@model_state() != ko.modelStates.READY)
327
+ console.log("Delete postponed.")
328
+ return
329
+ opts = @toJS(fields)
330
+ opts['id'] = @id()
331
+ @adapter.delete
332
+ data : opts
333
+ success : (resp)=>
334
+ @handleData(resp.data)
335
+ callback(resp) if callback?
336
+ error : =>
337
+ console.log("Delete error encountered")
338
+ @model_state(ko.modelStates.READY)
339
+ @model_state(ko.modelStates.SAVING)
340
+ toJS : (flds)=>
341
+ flds ||= @fields
342
+ obj = {}
343
+ for prop in flds
344
+ if typeof(@[prop].toJS) == 'function'
345
+ obj[prop] = @[prop].toJS()
346
+ else
347
+ obj[prop] = @[prop]()
348
+ obj
349
+ absorb : (model) =>
350
+ @reset()
351
+ @handleData(model.toJS())
352
+
353
+ class @FileModel extends @Model
354
+ extend : ->
355
+ @input = {}
356
+ @input.files = ko.observable([])
357
+ @input.present = ko.computed ->
358
+ @input.files().length > 0
359
+ , this
360
+ @input.file = ko.computed ->
361
+ if @input.present() then @input.files()[0] else null
362
+ , this
363
+ @input.filename = ko.computed ->
364
+ if @input.present() then @input.file().name else ""
365
+ , this
366
+ @input.isImage = ->
367
+ if @input.present() then @input.file().type.match('image.*') else false
368
+ reset : ->
369
+ super
370
+ @input.files([])
371
+ toJS : =>
372
+ @input.file()
373
+
374
+ class @Collection
375
+ init : ->
376
+ constructor: (opts, parent) ->
377
+ @opts = opts || {}
378
+ @events = {}
379
+ @_reqid = 0
380
+ @parent = parent
381
+ @scope = ko.observable(@opts.scope || [])
382
+ @items = ko.observableArray([])
383
+ @views = ko.observableArray([])
384
+ @view_model = ko.observable(@opts.view || View)
385
+ @view_owner = ko.observable(@opts.view_owner || null)
386
+ @page = ko.observable(1)
387
+ @limit = ko.observable(@opts.limit || 4)
388
+ @title = ko.observable(@opts.title || 'Collection')
389
+ @extra_params = ko.observable(@opts.extra_params || {})
390
+ @model = @opts.model
391
+ @adapter = new ModelAdapter()
392
+ @template = ko.observable(@opts.template)
393
+ @model_state = ko.observable(0)
394
+ @is_ready = ko.dependentObservable ->
395
+ @model_state() == ko.modelStates.READY
396
+ , this
397
+ @is_loading = ko.dependentObservable ->
398
+ @model_state() == ko.modelStates.LOADING
399
+ , this
400
+ @is_appending = ko.dependentObservable ->
401
+ @model_state() == ko.modelStates.APPENDING
402
+ , this
403
+ @is_inserting = ko.dependentObservable ->
404
+ @model_state() == ko.modelStates.INSERTING
405
+ , this
406
+ @loadOptions = ko.dependentObservable ->
407
+ opts = @extra_params()
408
+ opts['scope'] = @scope()
409
+ opts['limit'] = @limit()
410
+ opts['page'] = @page()
411
+ opts
412
+ , this
413
+ @scope = ko.intercepter @scope, (obs, prev, curr) ->
414
+ obs(curr)
415
+ console.log("Scope changed from #{prev} to #{curr}")
416
+ #@load()
417
+ , this
418
+ @scopeSelector = ko.observable()
419
+ @scopeSelector.subscribe (val) ->
420
+ opts = @scope()
421
+ opts[@scopeSelector()] = []
422
+ @scope(opts)
423
+ , this
424
+ @hasItems = ko.dependentObservable ->
425
+ @items().length > 0
426
+ , this
427
+ @init()
428
+ setScope : (scp, args) =>
429
+ opts = args
430
+ opts.unshift(scp)
431
+ @scope(opts)
432
+ setView : (view_model, view_owner) =>
433
+ @view_model(view_model)
434
+ @view_owner(view_owner)
435
+ _load : (scope, op, callback)->
436
+ console.log("Loading items for #{scope}")
437
+ op ||= Collection.REPLACE
438
+ reqid = ++@_reqid
439
+ opts = @loadOptions()
440
+ opts.scope = scope
441
+ @adapter.index
442
+ data : opts
443
+ success : (resp)=>
444
+ return if @_reqid != reqid
445
+ @handleData(resp.data, op)
446
+ callback(resp) if callback?
447
+ @events.onchange() if @events.onchange?
448
+ if op == Collection.REPLACE
449
+ @model_state(ko.modelStates.LOADING)
450
+ else if op == Collection.APPEND
451
+ @model_state(ko.modelStates.APPENDING)
452
+ else if op == Collection.INSERT
453
+ @model_state(ko.modelStates.INSERTING)
454
+ load : (scope, callback)->
455
+ @scope(scope) if scope?
456
+ @_load(@scope(), Collection.REPLACE, callback)
457
+ update : (callback)->
458
+ @_load(@scope(), Collection.REPLACE, callback)
459
+ insert : (scope, callback)->
460
+ @_load(scope, Collection.INSERT, callback)
461
+ append : (scope, callback)->
462
+ @_load(scope, Collection.APPEND, callback)
463
+ handleData : (resp, op) =>
464
+ models = []
465
+ views = []
466
+ op ||= Collection.REPLACE
467
+ cls = @view_model()
468
+ if op == Collection.REPLACE
469
+ @items([]); @views([])
470
+ for item, idx in resp
471
+ model = new @model(item, this)
472
+ models.push(model)
473
+ views.push(new cls("view-#{model.id()}", @view_owner(), model))
474
+
475
+ if !op? || op == Collection.REPLACE
476
+ @items(models)
477
+ @views(views)
478
+ console.log("Items loaded")
479
+ else if op == Collection.INSERT
480
+ @items(models.concat(@items()))
481
+ @views(views.concat(@views()))
482
+ else if op == Collection.APPEND
483
+ @items(@items().concat(models))
484
+ @views(@views().concat(views))
485
+ @model_state(ko.modelStates.READY)
486
+ nextPage : ->
487
+ @page(@page() + 1)
488
+ @update()
489
+ prevPage : ->
490
+ @page(@page() - 1)
491
+ @update()
492
+ hasItems : ->
493
+ @items().length > 0
494
+ getItemById : (id)->
495
+ list = @items().filter ((item)=> item.id() == id)
496
+ ret = if list.length > 0 then list[0] else null
497
+ removeDuplicates : ->
498
+ ids = []
499
+ @items().forEach (item, idx, array)->
500
+ if ids.includes(item.id())
501
+ @items.splice(idx, 1)
502
+ @views.splice(idx, 1)
503
+ else
504
+ ids.push(item.id())
505
+ getTemplate : ->
506
+ @template()
507
+ reset : ->
508
+ @page(1)
509
+ @items([])
510
+ @views([])
511
+ toJS : =>
512
+ objs = []
513
+ for item in @items()
514
+ objs.push(item.toJS())
515
+ objs
516
+
517
+ Collection.REPLACE = 0
518
+ Collection.INSERT = 1
519
+ Collection.APPEND = 2
520
+
521
+ class @View
522
+ init : ->
523
+ constructor : (@name, @owner, @model)->
524
+ @app = @owner.app if @owner?
525
+ @views = {}
526
+ @events = {}
527
+ @templateID = "view-#{@name}"
528
+ @fields = []
529
+ @view_name = ko.computed ->
530
+ @templateID
531
+ , this
532
+ @is_visible = ko.observable(false)
533
+ @is_loading = ko.observable(false)
534
+ @errors = ko.observable([])
535
+ @view = null
536
+ @task = ko.observable(null)
537
+ @init()
538
+ show : ->
539
+ @is_visible(true)
540
+ hide : ->
541
+ @events.before_hide() if @events.before_hide?
542
+ @is_visible(false)
543
+ load : ->
544
+ addView : (name, view_class, tpl) ->
545
+ @views[name] = new view_class(name, this)
546
+ @views[name].templateID = tpl
547
+ @["is_task_#{name}"] = ko.computed ->
548
+ @task() == name
549
+ , this
550
+ @["select_task_#{name}"] = =>
551
+ @selectView(name)
552
+ viewList : ->
553
+ list = for name, view of @views
554
+ view
555
+ selectView : (view_name) ->
556
+ args = Array.prototype.slice.call(arguments)
557
+ last_view = @view
558
+ view = @views[view_name]
559
+ if (last_view != view)
560
+ console.log("View [#{view.name}] selected.")
561
+ @view = view
562
+ @task(view.name)
563
+ last_view.hide() if last_view?
564
+ view.show()
565
+ view.load.apply(view, args[1..])
566
+ window.onbeforeunload = @view.events.before_unload
567
+ else
568
+ @view.load.apply(@view, args[1..])
569
+ isTask : (task) ->
570
+ @task() == task
571
+ getViewName : (view) ->
572
+ view.templateID
573
+ showAsOverlay : (tmp, opts, cls)=>
574
+ overlay.add(this, tmp, opts, cls)
575
+ hideOverlay : =>
576
+ overlay.remove(@name)
577
+
578
+ class @ModelAdapter
579
+ constructor : (opts)->
580
+ @save_url = null
581
+ @load_url = null
582
+ @index_url = null
583
+ for prop,val of opts
584
+ @[prop] = val
585
+ load : (opts)->
586
+ $.getJSON @load_url, opts.data, (resp)->
587
+ opts.success(resp)
588
+ index : (opts)->
589
+ $.getJSON (@index_url || @load_url), opts.data, (resp)->
590
+ opts.success(resp)
591
+ save_old : (opts)->
592
+ $.ajax
593
+ type : 'POST'
594
+ url : @save_url
595
+ data : opts.data
596
+ success : opts.success
597
+ error : opts.error
598
+ save : (opts)->
599
+ $.ajax_qs
600
+ type : 'POST'
601
+ url : @save_url
602
+ data : opts.data
603
+ progress : opts.progress
604
+ success : opts.success
605
+ error : opts.error
606
+ send : (opts)->
607
+ $.ajax
608
+ type : 'POST'
609
+ url : opts.url
610
+ data : opts.data
611
+ success : opts.success
612
+ error : opts.error
613
+ delete : (opts)->
614
+ $.ajax
615
+ type : 'DELETE'
616
+ url : @save_url
617
+ data : opts.data
618
+ success : opts.success
619
+ error : opts.error
620
+ add_method : (fn_name, fn)->
621
+ @[fn_name] = fn.bind(this)
622
+
623
+ class @AccountAdapter
624
+ constructor : (opts)->
625
+ @login_url = "/account/login"
626
+ @register_url = "/account/register"
627
+ @enter_code_url = "/account/enter_code"
628
+ @reset_url = "/account/reset"
629
+ @save_url = "/account/save"
630
+ @login_key = "email"
631
+ @password_key = "password"
632
+ for prop,val of opts
633
+ @[prop] = val
634
+ login : (username, password, callback)->
635
+ opts = {}
636
+ opts[@login_key] = username
637
+ opts[@password_key] = password
638
+ $.post @login_url, opts, (resp) =>
639
+ callback(resp)
640
+ register : (opts, callback)->
641
+ $.post @register_url, opts, (resp) =>
642
+ callback(resp)
643
+ sendInviteCode : (code, callback)->
644
+ $.post @enter_code_url, {code : code}, (resp) =>
645
+ callback(resp)
646
+ save : (opts, callback) ->
647
+ $.post @save_url, opts, (resp) =>
648
+ callback(resp)
649
+ resetPassword : (callback)->
650
+ @is_loading(true)
651
+ opts = {}
652
+ opts[@username_key] = @username()
653
+ $.post @reset_url, opts, (resp) =>
654
+ @is_loading(false)
655
+ callback(resp) if callback?
656
+
657
+ class @AppView extends @View
658
+ constructor : (user_model)->
659
+ @app = this
660
+ @path = ko.observable(null)
661
+ @path_parts = []
662
+ @account_model = Model
663
+ super('app', null)
664
+ @current_user = new @account_model()
665
+ @is_logged_in = ko.dependentObservable ->
666
+ !@current_user.is_new()
667
+ , this
668
+ route : (path) ->
669
+ console.log("Loading path '#{path}'")
670
+ @path(path)
671
+ @path_parts = @path().split('/')
672
+ @handlePath(path)
673
+ handlePath : (path) ->
674
+ setUser : (data)->
675
+ @current_user.handleData(data) if data != null
676
+ redirectTo : (path) ->
677
+ $.history.load(path)
678
+
679
+ @initApp = ->
680
+ appViewModel = @appViewModel
681
+ overlay = @overlay
682
+
683
+ appViewModel.setUser(@CURRENT_USER)
684
+
685
+ # navigation
686
+ $.history.init (hash) ->
687
+ if hash == ""
688
+ appViewModel.route('/')
689
+ else
690
+ appViewModel.route(hash)
691
+ , { unescape : ",/" }
692
+
693
+ # layout bindings
694
+ $('body').koBind(appViewModel)
695
+