quick_script 0.0.47
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/lib/quick_script/assets/css/basics.css.sass +147 -0
- data/lib/quick_script/assets/css/buttons.css +137 -0
- data/lib/quick_script/assets/css/mixins.css.sass +78 -0
- data/lib/quick_script/assets/css/quick_script.css +4 -0
- data/lib/quick_script/assets/js/classes.js.coffee +226 -0
- data/lib/quick_script/assets/js/date.js +126 -0
- data/lib/quick_script/assets/js/framework.js.coffee +695 -0
- data/lib/quick_script/assets/js/fullcalendar.min.js +113 -0
- data/lib/quick_script/assets/js/jquery-ui.min.js +414 -0
- data/lib/quick_script/assets/js/jquery.fileupload.js +760 -0
- data/lib/quick_script/assets/js/jquery.history.js +195 -0
- data/lib/quick_script/assets/js/jquery.iframe-transport.js +156 -0
- data/lib/quick_script/assets/js/jquery.min.js +4 -0
- data/lib/quick_script/assets/js/jquery.qtip.min.js +13 -0
- data/lib/quick_script/assets/js/knockout.js +97 -0
- data/lib/quick_script/assets/js/quick_script.js +12 -0
- data/lib/quick_script/base.rb +4 -0
- data/lib/quick_script/helpers.rb +32 -0
- data/lib/quick_script/interaction.rb +60 -0
- data/lib/quick_script/railtie.rb +13 -0
- data/lib/quick_script/version.rb +3 -0
- data/lib/quick_script.rb +75 -0
- data/quick_script.gemspec +21 -0
- metadata +91 -0
@@ -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
|
+
|