mvcoffee-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1556 @@
1
+ // Generated by CoffeeScript 1.9.3
2
+
3
+ /*
4
+
5
+ MVCoffee
6
+
7
+ Copyright 2014, Kirk Bowers
8
+ MIT License
9
+
10
+ Version 1.0.0
11
+ */
12
+
13
+ (function() {
14
+ var DEFAULT_OPTS, MVCoffee,
15
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
16
+ slice = [].slice,
17
+ hasProp = {}.hasOwnProperty,
18
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
19
+
20
+ if (typeof exports !== "undefined" && exports !== null) {
21
+ MVCoffee = exports;
22
+ } else {
23
+ this.MVCoffee || (this.MVCoffee = {});
24
+ MVCoffee = this.MVCoffee;
25
+ }
26
+
27
+ if (!Array.isArray) {
28
+ Array.isArray = function(arg) {
29
+ return Object.prototype.toString.call(arg) === '[object Array]';
30
+ };
31
+ }
32
+
33
+ MVCoffee.Pluralizer = (function() {
34
+ function Pluralizer() {}
35
+
36
+ Pluralizer.irregulars = {
37
+ man: "men",
38
+ woman: "women",
39
+ child: "children",
40
+ person: "people",
41
+ mouse: "mice",
42
+ goose: "geese",
43
+ datum: "data",
44
+ alumnus: "alumni",
45
+ hippopotamus: "hippopotami"
46
+ };
47
+
48
+ Pluralizer.addIrregulars = function(words) {
49
+ var plur, results, sing;
50
+ results = [];
51
+ for (sing in words) {
52
+ plur = words[sing];
53
+ results.push(this.irregulars[sing] = plur);
54
+ }
55
+ return results;
56
+ };
57
+
58
+ Pluralizer.addIrregular = Pluralizer.addIrregulars;
59
+
60
+ Pluralizer.pluralize = function(word) {
61
+ var lastIndex, lastLetter, lastTwo, lastWord, len, result, words;
62
+ words = word.split("_");
63
+ lastIndex = words.length - 1;
64
+ lastWord = words[lastIndex];
65
+ result = this.irregulars[lastWord];
66
+ if (result) {
67
+ words[lastIndex] = result;
68
+ return words.join("_");
69
+ }
70
+ len = lastWord.length;
71
+ lastLetter = lastWord[len - 1];
72
+ lastTwo = lastWord.slice(len - 2, +(len - 1) + 1 || 9e9);
73
+ if (lastLetter === "s" || lastLetter === "z") {
74
+ lastWord += "es";
75
+ } else if (lastTwo === "ch" || lastTwo === "sh") {
76
+ lastWord += "es";
77
+ } else if (lastLetter === "y") {
78
+ lastWord = lastWord.substring(0, len - 1) + "ies";
79
+ } else {
80
+ lastWord += "s";
81
+ }
82
+ words[lastIndex] = lastWord;
83
+ return words.join("_");
84
+ };
85
+
86
+ return Pluralizer;
87
+
88
+ })();
89
+
90
+ DEFAULT_OPTS = {
91
+ debug: false,
92
+ clientizeScope: 'body'
93
+ };
94
+
95
+ MVCoffee.Runtime = (function() {
96
+ function Runtime(opts) {
97
+ var opt, value;
98
+ if (opts == null) {
99
+ opts = {};
100
+ }
101
+ this._ajaxWithClientize = bind(this._ajaxWithClientize, this);
102
+ this.patch = bind(this.patch, this);
103
+ this["delete"] = bind(this["delete"], this);
104
+ this.post = bind(this.post, this);
105
+ this.submit = bind(this.submit, this);
106
+ this.fetch = bind(this.fetch, this);
107
+ this.redirect = bind(this.redirect, this);
108
+ this.visit = bind(this.visit, this);
109
+ this.dontClientize = bind(this.dontClientize, this);
110
+ this.clientize = bind(this.clientize, this);
111
+ this.log = bind(this.log, this);
112
+ this.processServerData = bind(this.processServerData, this);
113
+ this._preProcessServerData = bind(this._preProcessServerData, this);
114
+ this.getErrors = bind(this.getErrors, this);
115
+ this.getSession = bind(this.getSession, this);
116
+ this.setSession = bind(this.setSession, this);
117
+ this.getFlash = bind(this.getFlash, this);
118
+ this.setFlash = bind(this.setFlash, this);
119
+ this.broadcast = bind(this.broadcast, this);
120
+ this.narrowcast = bind(this.narrowcast, this);
121
+ this.opts = DEFAULT_OPTS;
122
+ for (opt in opts) {
123
+ value = opts[opt];
124
+ this.opts[opt] = value;
125
+ }
126
+ this.controllers = {};
127
+ this.modelStore = new MVCoffee.ModelStore;
128
+ this.active = [];
129
+ this.listeners = [];
130
+ this._flash = {};
131
+ this._oldFlash = {};
132
+ this._clientizeCustomizations = [];
133
+ this._neverClientizeSelectors = [];
134
+ this.session = {};
135
+ this.dataId = "mvcoffee_json";
136
+ this.onfocusId = null;
137
+ }
138
+
139
+ Runtime.prototype.register_controllers = function(contrs) {
140
+ var contr, id, results;
141
+ results = [];
142
+ for (id in contrs) {
143
+ contr = contrs[id];
144
+ results.push(this._addController(new contr(id, this), id));
145
+ }
146
+ return results;
147
+ };
148
+
149
+ Runtime.prototype._addController = function(contr, id) {
150
+ if (id != null) {
151
+ return this.controllers[id] = contr;
152
+ } else {
153
+ return this.controllers[contr.selector] = contr;
154
+ }
155
+ };
156
+
157
+ Runtime.prototype.register_listeners = function() {
158
+ var args, j, len1, listenerClass, results;
159
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
160
+ results = [];
161
+ for (j = 0, len1 = args.length; j < len1; j++) {
162
+ listenerClass = args[j];
163
+ results.push(this.listeners.push(new listenerClass(this)));
164
+ }
165
+ return results;
166
+ };
167
+
168
+ Runtime.prototype.register_models = function(models) {
169
+ return this.modelStore.register_models(models);
170
+ };
171
+
172
+ Runtime.prototype.narrowcast = function() {
173
+ var args, controller, i, message, messages, results, sent;
174
+ controller = arguments[0], messages = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
175
+ if (!Array.isArray(messages)) {
176
+ messages = [messages];
177
+ }
178
+ sent = false;
179
+ i = 0;
180
+ results = [];
181
+ while (!sent && i < messages.length) {
182
+ message = messages[i];
183
+ if (message && (controller[message] != null) && typeof controller[message] === 'function') {
184
+ sent = true;
185
+ controller[message].apply(controller, args);
186
+ }
187
+ results.push(i++);
188
+ }
189
+ return results;
190
+ };
191
+
192
+ Runtime.prototype.broadcast = function() {
193
+ var args, controller, j, k, len1, len2, listener, messages, ref, ref1, results;
194
+ messages = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
195
+ if (!Array.isArray(messages)) {
196
+ messages = [messages];
197
+ }
198
+ ref = this.active;
199
+ for (j = 0, len1 = ref.length; j < len1; j++) {
200
+ controller = ref[j];
201
+ this.narrowcast.apply(this, [controller, messages].concat(slice.call(args)));
202
+ }
203
+ ref1 = this.listeners;
204
+ results = [];
205
+ for (k = 0, len2 = ref1.length; k < len2; k++) {
206
+ listener = ref1[k];
207
+ results.push(this.narrowcast.apply(this, [listener, messages].concat(slice.call(args))));
208
+ }
209
+ return results;
210
+ };
211
+
212
+ Runtime.prototype._recycleFlash = function() {
213
+ this._oldFlash = this._flash;
214
+ return this._flash = {};
215
+ };
216
+
217
+ Runtime.prototype.setFlash = function(opts) {
218
+ var key, opt, results;
219
+ results = [];
220
+ for (key in opts) {
221
+ opt = opts[key];
222
+ results.push(this._flash[key] = opt);
223
+ }
224
+ return results;
225
+ };
226
+
227
+ Runtime.prototype.getFlash = function(key) {
228
+ var ref;
229
+ return (ref = this._flash[key]) != null ? ref : this._oldFlash[key];
230
+ };
231
+
232
+ Runtime.prototype.setSession = function(opts) {
233
+ var key, opt, results;
234
+ results = [];
235
+ for (key in opts) {
236
+ opt = opts[key];
237
+ results.push(this.session[key] = opt);
238
+ }
239
+ return results;
240
+ };
241
+
242
+ Runtime.prototype.getSession = function(key) {
243
+ return this.session[key];
244
+ };
245
+
246
+ Runtime.prototype.getErrors = function() {
247
+ return this.errors;
248
+ };
249
+
250
+ Runtime.prototype._preProcessServerData = function(data) {
251
+ var key, ref, value;
252
+ if (data) {
253
+ if (this.opts.debug) {
254
+ this.log("Got data from server: " + JSON.stringify(data));
255
+ }
256
+ this.modelStore.load(data);
257
+ if (data.flash != null) {
258
+ this.setFlash(data.flash);
259
+ }
260
+ if (data.session != null) {
261
+ ref = data.session;
262
+ for (key in ref) {
263
+ value = ref[key];
264
+ this.log("Setting session value " + key + " to value " + value + " from server");
265
+ this.session[key] = value;
266
+ }
267
+ }
268
+ this.errors = data.errors;
269
+ if (data.redirect != null) {
270
+ this.redirect(data.redirect);
271
+ return false;
272
+ } else {
273
+ return true;
274
+ }
275
+ } else {
276
+ return true;
277
+ }
278
+ };
279
+
280
+ Runtime.prototype.processServerData = function(data, callback_message) {
281
+ var error_callback_message;
282
+ if (callback_message == null) {
283
+ callback_message = "";
284
+ }
285
+ if (this._preProcessServerData(data)) {
286
+ if (this.errors) {
287
+ if (callback_message) {
288
+ error_callback_message = [callback_message + "_errors", "errors"];
289
+ } else {
290
+ error_callback_message = "errors";
291
+ }
292
+ end;
293
+ return this.broadcast(error_callback_message, this.errors);
294
+ } else {
295
+ return this.broadcast([callback_message, "render"]);
296
+ }
297
+ }
298
+ };
299
+
300
+ Runtime.prototype.log = function(message) {
301
+ if (this.opts.debug) {
302
+ return console.log(message);
303
+ }
304
+ };
305
+
306
+ Runtime.prototype.go = function() {
307
+ var contr, id, json, newActive, parsed, ref, token;
308
+ this.log("MVCoffee runtime firing 'go'");
309
+ token = jQuery("meta[name='csrf-token']");
310
+ if (token != null ? token.length : void 0) {
311
+ this.authenticity_token = token.attr("content");
312
+ }
313
+ this._recycleFlash();
314
+ this.resetClientizeCustomizations();
315
+ json = jQuery("#" + this.dataId).html();
316
+ parsed = null;
317
+ if (json) {
318
+ parsed = jQuery.parseJSON(json);
319
+ }
320
+ if (this._preProcessServerData(parsed)) {
321
+ newActive = [];
322
+ ref = this.controllers;
323
+ for (id in ref) {
324
+ contr = ref[id];
325
+ if (jQuery("#" + id).length > 0) {
326
+ this.log("Starting controller identified by " + id);
327
+ newActive.push(contr);
328
+ }
329
+ }
330
+ if (this.active.length) {
331
+ this.broadcast("stop");
332
+ window.onbeforeunload = null;
333
+ window.onfocus = null;
334
+ window.onblur = null;
335
+ if (this.onfocusId) {
336
+ clearInterval(this.onfocusId);
337
+ }
338
+ this.onfocusId = null;
339
+ }
340
+ if (newActive.length) {
341
+ this.active = newActive;
342
+ this.broadcast("start");
343
+ window.onfocus = (function(_this) {
344
+ return function() {
345
+ _this._startSafariKludge();
346
+ return _this.broadcast("resume");
347
+ };
348
+ })(this);
349
+ window.onblur = (function(_this) {
350
+ return function() {
351
+ _this._stopSafariKludge();
352
+ return _this.broadcast("pause");
353
+ };
354
+ })(this);
355
+ this._startSafariKludge();
356
+ } else {
357
+ this.active = [];
358
+ }
359
+ this.clientize();
360
+ return this.broadcast("render");
361
+ }
362
+ };
363
+
364
+ Runtime.prototype._startSafariKludge = function() {
365
+ this._stopSafariKludge();
366
+ this.lastFired = new Date().getTime();
367
+ return this.onfocusId = setInterval((function(_this) {
368
+ return function() {
369
+ var now;
370
+ now = new Date().getTime();
371
+ if (now - _this.lastFired > 2000) {
372
+ _this.broadcast("pause");
373
+ _this.broadcast("resume");
374
+ }
375
+ return _this.lastFired = now;
376
+ };
377
+ })(this), 500);
378
+ };
379
+
380
+ Runtime.prototype._stopSafariKludge = function() {
381
+ if (this.onfocusId != null) {
382
+ clearInterval(this.onfocusId);
383
+ }
384
+ return this.onfocusId = null;
385
+ };
386
+
387
+ Runtime.prototype.clientize = function(scope) {
388
+ var $searchInside, applyClientize, self;
389
+ if (scope == null) {
390
+ scope = null;
391
+ }
392
+ if (typeof Turbolinks !== "undefined" && Turbolinks !== null) {
393
+ self = this;
394
+ scope = scope != null ? scope : this.opts.clientizeScope;
395
+ $searchInside = jQuery(scope);
396
+ applyClientize = function(selector, event, validation, submission) {
397
+ return $searchInside.find(selector).each(function(index, element) {
398
+ var customization, j, len1, ref, thisCustom;
399
+ customization = {};
400
+ ref = self._clientizeCustomizations;
401
+ for (j = 0, len1 = ref.length; j < len1; j++) {
402
+ thisCustom = ref[j];
403
+ if (jQuery(element).is(thisCustom.selector)) {
404
+ customization = thisCustom;
405
+ }
406
+ }
407
+ if (!customization.ignore) {
408
+ return jQuery(element).on(event, function(eventObject) {
409
+ var callback, confirm, doPost;
410
+ callback = element.id;
411
+ if (customization.callback) {
412
+ callback = customization.callback;
413
+ }
414
+ doPost = validation(customization, callback);
415
+ if (doPost) {
416
+ confirm = jQuery(element).data("confirm");
417
+ if (customization.confirm) {
418
+ confirm = customization.confirm;
419
+ }
420
+ if (confirm) {
421
+ if (confirm instanceof Function) {
422
+ doPost = confirm();
423
+ } else {
424
+ doPost = window.confirm(confirm);
425
+ }
426
+ }
427
+ }
428
+ if (doPost) {
429
+ submission(element, callback);
430
+ }
431
+ return false;
432
+ });
433
+ }
434
+ });
435
+ };
436
+ applyClientize("form", "submit", function(customization, callback) {
437
+ var method, model;
438
+ model = customization.model;
439
+ if (model != null) {
440
+ model.populate();
441
+ method = "errors";
442
+ if (callback) {
443
+ method = [callback + "_errors", "errors"];
444
+ }
445
+ if (customization.controller != null) {
446
+ self.narrowcast(customization.controller, method, model.errors);
447
+ }
448
+ return model.isValid();
449
+ } else {
450
+ return true;
451
+ }
452
+ }, function(element, callback) {
453
+ var params, url;
454
+ if (element.method === "get" || element.method === "GET") {
455
+ params = jQuery(element).serialize();
456
+ url = element.action;
457
+ if (params) {
458
+ if (/\?/.test(url)) {
459
+ url += '&' + params;
460
+ } else {
461
+ url += '?' + params;
462
+ }
463
+ }
464
+ return self.visit(url);
465
+ } else {
466
+ return self.submit(element, callback);
467
+ }
468
+ });
469
+ return applyClientize("a", "click", function(customization, callback) {
470
+ return true;
471
+ }, function(element, callback) {
472
+ var method;
473
+ method = jQuery(element).data('method');
474
+ if (method === "post") {
475
+ return self.post(element.href, {}, callback);
476
+ } else if (method === "delete") {
477
+ return self["delete"](element.href, {}, callback);
478
+ } else if (method === "patch") {
479
+ return self.patch(element.href, {}, callback);
480
+ } else {
481
+ return self.visit(element.href);
482
+ }
483
+ });
484
+ }
485
+ };
486
+
487
+ Runtime.prototype.neverClientize = function() {
488
+ var arg, args, j, len1, results;
489
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
490
+ results = [];
491
+ for (j = 0, len1 = args.length; j < len1; j++) {
492
+ arg = args[j];
493
+ results.push(this._neverClientizeSelectors.push({
494
+ selector: arg,
495
+ ignore: true
496
+ }));
497
+ }
498
+ return results;
499
+ };
500
+
501
+ Runtime.prototype.resetClientizeCustomizations = function() {
502
+ return this._clientizeCustomizations = this._neverClientizeSelectors.slice();
503
+ };
504
+
505
+ Runtime.prototype.addClientizeCustomization = function(customization) {
506
+ return this._clientizeCustomizations.push(customization);
507
+ };
508
+
509
+ Runtime.prototype.dontClientize = function(selector) {
510
+ return this._clientizeCustomizations.push({
511
+ selector: selector,
512
+ ignore: true
513
+ });
514
+ };
515
+
516
+ Runtime.prototype._setSessionCookie = function() {
517
+ var cookie, expiration, params;
518
+ params = jQuery.param(this.session);
519
+ expiration = new Date();
520
+ expiration.setTime(expiration.getTime() + 1000);
521
+ cookie = "mvcoffee_session=" + params + "; path=/; expires=" + (expiration.toGMTString());
522
+ document.cookie = cookie;
523
+ return this.log("Sending cookie = " + cookie);
524
+ };
525
+
526
+ Runtime.prototype.visit = function(url) {
527
+ this._recycleFlash();
528
+ this._setSessionCookie();
529
+ return Turbolinks.visit(url);
530
+ };
531
+
532
+ Runtime.prototype.redirect = function(url) {
533
+ this._setSessionCookie();
534
+ return Turbolinks.visit(url);
535
+ };
536
+
537
+ Runtime.prototype.fetch = function(url, callback_message) {
538
+ if (callback_message == null) {
539
+ callback_message = "";
540
+ }
541
+ this._setSessionCookie();
542
+ return jQuery.get(url, null, (function(_this) {
543
+ return function(data) {
544
+ return _this.processServerData(data, callback_message);
545
+ };
546
+ })(this), 'json');
547
+ };
548
+
549
+ Runtime.prototype.submit = function(submitee, callback_message) {
550
+ var element;
551
+ if (callback_message == null) {
552
+ callback_message = "";
553
+ }
554
+ this._setSessionCookie();
555
+ element = submitee;
556
+ if (submitee instanceof jQuery) {
557
+ element = submitee.get(0);
558
+ }
559
+ jQuery.post(element.action, jQuery(element).serialize(), (function(_this) {
560
+ return function(data) {
561
+ return _this.processServerData(data, callback_message);
562
+ };
563
+ })(this), 'json');
564
+ return false;
565
+ };
566
+
567
+ Runtime.prototype.post = function(url, params, callback_message) {
568
+ if (params == null) {
569
+ params = {};
570
+ }
571
+ if (callback_message == null) {
572
+ callback_message = "";
573
+ }
574
+ return this._ajaxWithClientize("POST", url, params, callback_message);
575
+ };
576
+
577
+ Runtime.prototype["delete"] = function(url, params, callback_message) {
578
+ if (params == null) {
579
+ params = {};
580
+ }
581
+ if (callback_message == null) {
582
+ callback_message = "";
583
+ }
584
+ return this._ajaxWithClientize("DELETE", url, params, callback_message);
585
+ };
586
+
587
+ Runtime.prototype.patch = function(url, params, callback_message) {
588
+ if (params == null) {
589
+ params = {};
590
+ }
591
+ if (callback_message == null) {
592
+ callback_message = "";
593
+ }
594
+ return this._ajaxWithClientize("PATCH", url, params, callback_message);
595
+ };
596
+
597
+ Runtime.prototype._ajaxWithClientize = function(type, url, params, callback_message) {
598
+ var self;
599
+ this._setSessionCookie();
600
+ self = this;
601
+ return jQuery.ajax({
602
+ url: url,
603
+ data: params,
604
+ type: type,
605
+ success: (function(_this) {
606
+ return function(data) {
607
+ return self.processServerData(data, callback_message);
608
+ };
609
+ })(this),
610
+ dataType: "json"
611
+ });
612
+ };
613
+
614
+ Runtime.prototype.run = function() {
615
+ var self;
616
+ this.log("MVCoffee runtime run");
617
+ self = this;
618
+ jQuery(function() {
619
+ return self.go();
620
+ });
621
+ return jQuery(document).on('pagebeforeshow', function() {
622
+ return self.go();
623
+ });
624
+ };
625
+
626
+ return Runtime;
627
+
628
+ })();
629
+
630
+ MVCoffee.Controller = (function() {
631
+ function Controller(id1, _runtime) {
632
+ this.id = id1;
633
+ this._runtime = _runtime;
634
+ this.errors = bind(this.errors, this);
635
+ this.selector = "#" + this.id;
636
+ this.timerId = null;
637
+ this.isActive = false;
638
+ this.processServerData = this._runtime.processServerData;
639
+ this.getFlash = this._runtime.getFlash;
640
+ this.setFlash = this._runtime.setFlash;
641
+ this.getSession = this._runtime.getSession;
642
+ this.setSession = this._runtime.setSession;
643
+ this.getErrors = this._runtime.getErrors;
644
+ this.broadcast = this._runtime.broadcast;
645
+ this.dontClientize = this._runtime.dontClientize;
646
+ this.reclientize = this._runtime.clientize;
647
+ this.visit = this._runtime.visit;
648
+ this.fetch = this._runtime.fetch;
649
+ this.post = this._runtime.post;
650
+ this["delete"] = this._runtime["delete"];
651
+ this.patch = this._runtime.patch;
652
+ this.submit = this._runtime.submit;
653
+ this.log = this._runtime.log;
654
+ this.timerCount = 0;
655
+ }
656
+
657
+ Controller.prototype.addClientizeCustomization = function(customization) {
658
+ customization.controller = this;
659
+ return this._runtime.addClientizeCustomization(customization);
660
+ };
661
+
662
+ Controller.prototype.rerender = function(opts) {
663
+ var $element, as_var, collection, element, item, j, len1, locals, ref, ref1, template_path;
664
+ element = (ref = opts.selector) != null ? ref : opts.element;
665
+ $element = jQuery(element);
666
+ template_path = opts.template;
667
+ locals = (ref1 = opts.locals) != null ? ref1 : {};
668
+ $element.empty();
669
+ collection = opts.collection;
670
+ if (collection) {
671
+ as_var = opts.as;
672
+ if (as_var) {
673
+ for (j = 0, len1 = collection.length; j < len1; j++) {
674
+ item = collection[j];
675
+ locals[as_var] = item;
676
+ $element.append(JST[template_path](locals));
677
+ }
678
+ }
679
+ } else {
680
+ $element.append(JST[template_path](locals));
681
+ }
682
+ return this.reclientize($element);
683
+ };
684
+
685
+ Controller.prototype.start = function() {
686
+ this.isActive = true;
687
+ this.onStart();
688
+ if (this.refresh != null) {
689
+ return this.startTimer();
690
+ }
691
+ };
692
+
693
+ Controller.prototype.resume = function() {
694
+ this.onResume();
695
+ if ((this.refresh != null) && !this.isActive) {
696
+ this.isActive = true;
697
+ this.refresh();
698
+ return this.startTimer();
699
+ }
700
+ };
701
+
702
+ Controller.prototype.pause = function() {
703
+ this.onPause();
704
+ if (this.refresh != null) {
705
+ this.isActive = false;
706
+ return this.stopTimer();
707
+ }
708
+ };
709
+
710
+ Controller.prototype.stop = function() {
711
+ this.isActive = false;
712
+ this.onStop();
713
+ if (this.refresh != null) {
714
+ return this.stopTimer();
715
+ }
716
+ };
717
+
718
+ Controller.prototype.refreshInterval = 60000;
719
+
720
+ Controller.prototype.refresh = null;
721
+
722
+ Controller.prototype.onStart = function() {};
723
+
724
+ Controller.prototype.onPause = function() {};
725
+
726
+ Controller.prototype.onResume = function() {};
727
+
728
+ Controller.prototype.onStop = function() {};
729
+
730
+ Controller.prototype.render = function() {};
731
+
732
+ Controller.prototype.errors = function(errors) {
733
+ return console.log("!!!!! The errors method was called on controller " + (this.toString()) + " but not implemented!!!!!");
734
+ };
735
+
736
+ Controller.prototype.toString = function() {
737
+ return this.id;
738
+ };
739
+
740
+ Controller.prototype.startTimer = function() {
741
+ var self;
742
+ if (this.timerId != null) {
743
+ this.stopTimer();
744
+ }
745
+ if ((this.refreshInterval != null) && this.refreshInterval > 0) {
746
+ self = this;
747
+ this.timerCount += 1;
748
+ return this.timerId = setInterval(function() {
749
+ return self.refresh.call(self);
750
+ }, this.refreshInterval);
751
+ }
752
+ };
753
+
754
+ Controller.prototype.stopTimer = function() {
755
+ if (this.timerId != null) {
756
+ clearInterval(this.timerId);
757
+ }
758
+ return this.timerId = null;
759
+ };
760
+
761
+ return Controller;
762
+
763
+ })();
764
+
765
+ MVCoffee.ModelStore = (function() {
766
+ ModelStore.prototype.MIN_DATA_FORMAT_VERSION = "1.0.0";
767
+
768
+ function ModelStore(models) {
769
+ if (models == null) {
770
+ models = {};
771
+ }
772
+ this.modelDefs = {};
773
+ this.store = {};
774
+ this.register_models(models);
775
+ }
776
+
777
+ ModelStore.prototype.register_models = function(models) {
778
+ var classdef, name, results;
779
+ if (models == null) {
780
+ models = {};
781
+ }
782
+ results = [];
783
+ for (name in models) {
784
+ classdef = models[name];
785
+ results.push(this._addModel(name, classdef));
786
+ }
787
+ return results;
788
+ };
789
+
790
+ ModelStore.prototype._addModel = function(name, classdef) {
791
+ var base, base1;
792
+ this.modelDefs[name] = classdef;
793
+ (base = classdef.prototype).modelName || (base.modelName = name);
794
+ (base1 = classdef.prototype).modelNamePlural || (base1.modelNamePlural = MVCoffee.Pluralizer.pluralize(name));
795
+ classdef.prototype.modelStore = this;
796
+ return this.store[name] = {};
797
+ };
798
+
799
+ ModelStore.prototype.knowsAbout = function(name) {
800
+ return this.store[name] != null;
801
+ };
802
+
803
+ ModelStore.prototype.load_model_data = function(modelName, data) {
804
+ var j, len1, model, modelObj, results;
805
+ if (Array.isArray(data)) {
806
+ results = [];
807
+ for (j = 0, len1 = data.length; j < len1; j++) {
808
+ modelObj = data[j];
809
+ model = new this.modelDefs[modelName](modelObj);
810
+ results.push(this.store[modelName][model.id] = model);
811
+ }
812
+ return results;
813
+ } else {
814
+ model = new this.modelDefs[modelName](data);
815
+ return this.store[modelName][model.id] = model;
816
+ }
817
+ };
818
+
819
+ ModelStore.prototype.load = function(object) {
820
+ var commands, foreignKeys, j, k, len1, len2, modelId, modelName, record, ref, ref1, ref2, results, toBeRemoved;
821
+ if ((object.mvcoffee_version == null) || object.mvcoffee_version < this.MIN_DATA_FORMAT_VERSION) {
822
+ throw "MVCoffee.DataStore requires minimum data format " + this.MIN_DATA_FORMAT_VERSION;
823
+ }
824
+ ref = object.models;
825
+ for (modelName in ref) {
826
+ commands = ref[modelName];
827
+ if (this.modelDefs[modelName] != null) {
828
+ if (commands.replace_on != null) {
829
+ if (Array.isArray(commands.replace_on)) {
830
+ toBeRemoved = [];
831
+ ref1 = commands.replace_on;
832
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
833
+ foreignKeys = ref1[j];
834
+ toBeRemoved = toBeRemoved.concat(this.where(modelName, foreignKeys));
835
+ }
836
+ } else {
837
+ toBeRemoved = this.where(modelName, commands.replace_on);
838
+ }
839
+ for (k = 0, len2 = toBeRemoved.length; k < len2; k++) {
840
+ record = toBeRemoved[k];
841
+ this.remove(modelName, record.id);
842
+ }
843
+ }
844
+ }
845
+ }
846
+ ref2 = object.models;
847
+ results = [];
848
+ for (modelName in ref2) {
849
+ commands = ref2[modelName];
850
+ if (this.modelDefs[modelName] != null) {
851
+ if (commands.data != null) {
852
+ this.load_model_data(modelName, commands.data);
853
+ }
854
+ if (commands["delete"] != null) {
855
+ if (Array.isArray(commands["delete"])) {
856
+ results.push((function() {
857
+ var l, len3, ref3, results1;
858
+ ref3 = commands["delete"];
859
+ results1 = [];
860
+ for (l = 0, len3 = ref3.length; l < len3; l++) {
861
+ modelId = ref3[l];
862
+ results1.push(this._delete_with_cascade(modelName, modelId));
863
+ }
864
+ return results1;
865
+ }).call(this));
866
+ } else {
867
+ results.push(this._delete_with_cascade(modelName, commands["delete"]));
868
+ }
869
+ } else {
870
+ results.push(void 0);
871
+ }
872
+ } else {
873
+ results.push(void 0);
874
+ }
875
+ }
876
+ return results;
877
+ };
878
+
879
+ ModelStore.prototype.save = function(modelName, modelObj) {
880
+ var ref;
881
+ return (ref = this.store[modelName]) != null ? ref[modelObj.id] = modelObj : void 0;
882
+ };
883
+
884
+ ModelStore.prototype.find = function(model, id) {
885
+ var ref;
886
+ return (ref = this.store[model]) != null ? ref[id] : void 0;
887
+ };
888
+
889
+ ModelStore.prototype.findBy = function(model, conditions) {
890
+ var id, match, prop, record, records, ref, result, value;
891
+ records = (ref = this.store[model]) != null ? ref : {};
892
+ result = null;
893
+ for (id in records) {
894
+ record = records[id];
895
+ match = true;
896
+ for (prop in conditions) {
897
+ value = conditions[prop];
898
+ if (record[prop] !== value) {
899
+ match = false;
900
+ }
901
+ }
902
+ if (match) {
903
+ result || (result = record);
904
+ }
905
+ }
906
+ return result;
907
+ };
908
+
909
+ ModelStore.prototype.where = function(model, conditions) {
910
+ var id, match, prop, record, records, result, value;
911
+ records = this.store[model];
912
+ result = [];
913
+ for (id in records) {
914
+ record = records[id];
915
+ match = true;
916
+ for (prop in conditions) {
917
+ value = conditions[prop];
918
+ if (record[prop] !== value) {
919
+ match = false;
920
+ }
921
+ }
922
+ if (match) {
923
+ result.push(record);
924
+ }
925
+ }
926
+ return result;
927
+ };
928
+
929
+ ModelStore.prototype.all = function(model) {
930
+ var id, record, records, result;
931
+ records = this.store[model];
932
+ result = [];
933
+ for (id in records) {
934
+ record = records[id];
935
+ result.push(record);
936
+ }
937
+ return result;
938
+ };
939
+
940
+ ModelStore.prototype["delete"] = function(model, id) {
941
+ return delete this.store[model][id];
942
+ };
943
+
944
+ ModelStore.prototype.remove = function(model, id) {
945
+ return delete this.store[model][id];
946
+ };
947
+
948
+ ModelStore.prototype._delete_with_cascade = function(model, id) {
949
+ var record;
950
+ record = this.store[model][id];
951
+ if ((record["delete"] != null) && record["delete"] instanceof Function) {
952
+ return record["delete"]();
953
+ } else {
954
+ return delete this.store[model][id];
955
+ }
956
+ };
957
+
958
+ return ModelStore;
959
+
960
+ })();
961
+
962
+ MVCoffee.Model = (function() {
963
+ function Model(obj) {
964
+ this.addError = bind(this.addError, this);
965
+ if (obj != null) {
966
+ this.update(obj);
967
+ }
968
+ }
969
+
970
+ Model.prototype.modelName = null;
971
+
972
+ Model.prototype.fields = [];
973
+
974
+ Model.prototype._associations_children = [];
975
+
976
+ Model.prototype.errors = [];
977
+
978
+ Model.prototype.errorsForField = {};
979
+
980
+ Model.prototype.valid = true;
981
+
982
+ Model.order = function(array, order, opts) {
983
+ var desc, ignoreCase, prop, ref, result, value;
984
+ if (opts == null) {
985
+ opts = {};
986
+ }
987
+ result = array;
988
+ ref = order.split(/\s+/), prop = ref[0], desc = ref[1];
989
+ value = 1;
990
+ if ((desc != null) && desc === "desc") {
991
+ value = -1;
992
+ }
993
+ ignoreCase = false;
994
+ if (opts.ignoreCase) {
995
+ ignoreCase = true;
996
+ }
997
+ result.sort(function(a, b) {
998
+ a = a[prop];
999
+ b = b[prop];
1000
+ if (ignoreCase) {
1001
+ if (a.toLowerCase) {
1002
+ a = a.toLowerCase();
1003
+ }
1004
+ if (b.toLowerCase) {
1005
+ b = b.toLowerCase();
1006
+ }
1007
+ }
1008
+ if (a > b) {
1009
+ return value;
1010
+ } else if (a < b) {
1011
+ return -value;
1012
+ } else {
1013
+ return 0;
1014
+ }
1015
+ });
1016
+ return result;
1017
+ };
1018
+
1019
+ Model.all = function(options) {
1020
+ var result;
1021
+ if (options == null) {
1022
+ options = {};
1023
+ }
1024
+ result = this.prototype.modelStore.all(this.prototype.modelName);
1025
+ if (options.order) {
1026
+ result = this.order(result, options.order, options);
1027
+ }
1028
+ return result;
1029
+ };
1030
+
1031
+ Model.find = function(id) {
1032
+ return this.prototype.modelStore.find(this.prototype.modelName, id);
1033
+ };
1034
+
1035
+ Model.findBy = function(conditions) {
1036
+ return this.prototype.modelStore.findBy(this.prototype.modelName, conditions);
1037
+ };
1038
+
1039
+ Model.where = function(conditions) {
1040
+ return this.prototype.modelStore.where(this.prototype.modelName, conditions);
1041
+ };
1042
+
1043
+ Model.prototype.save = function() {
1044
+ if (this.validate()) {
1045
+ return this.modelStore.save(this.modelName, this);
1046
+ }
1047
+ };
1048
+
1049
+ Model.prototype.store = function() {
1050
+ return this.modelStore.save(this.modelName, this);
1051
+ };
1052
+
1053
+ Model.prototype.update = function(obj) {
1054
+ var field, results, value;
1055
+ results = [];
1056
+ for (field in obj) {
1057
+ if (!hasProp.call(obj, field)) continue;
1058
+ value = obj[field];
1059
+ if ((value instanceof Object || value instanceof Array) && this.modelStore.knowsAbout(field)) {
1060
+ results.push(this.modelStore.load_model_data(field, value));
1061
+ } else {
1062
+ results.push(this[field] = value);
1063
+ }
1064
+ }
1065
+ return results;
1066
+ };
1067
+
1068
+ Model.prototype["delete"] = function() {
1069
+ var assoc, child, children, j, k, len1, len2, ref;
1070
+ ref = this._associations_children;
1071
+ for (j = 0, len1 = ref.length; j < len1; j++) {
1072
+ assoc = ref[j];
1073
+ children = this[assoc]();
1074
+ if (Array.isArray(children)) {
1075
+ for (k = 0, len2 = children.length; k < len2; k++) {
1076
+ child = children[k];
1077
+ child["delete"]();
1078
+ }
1079
+ } else {
1080
+ if (children != null) {
1081
+ children["delete"]();
1082
+ }
1083
+ }
1084
+ }
1085
+ return this.modelStore["delete"](this.modelName, this.id);
1086
+ };
1087
+
1088
+ Model.prototype.destroy = function() {
1089
+ return this["delete"]();
1090
+ };
1091
+
1092
+ Model.prototype.remove = function() {
1093
+ return this.modelStore.remove(this.modelName, this.id);
1094
+ };
1095
+
1096
+ Model.findFieldIndex = function(field) {
1097
+ var fields, i, index, j, ref;
1098
+ fields = this.prototype.fields;
1099
+ index = -1;
1100
+ for (i = j = 0, ref = fields.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
1101
+ if (fields[i].name === field) {
1102
+ index = i;
1103
+ }
1104
+ }
1105
+ return index;
1106
+ };
1107
+
1108
+ Model.validates = function(field, test) {
1109
+ var fields, index;
1110
+ if (!this.prototype.hasOwnProperty("fields")) {
1111
+ this.prototype.fields = [];
1112
+ }
1113
+ fields = this.prototype.fields;
1114
+ index = this.findFieldIndex(field);
1115
+ if (index < 0) {
1116
+ return fields.push({
1117
+ name: field,
1118
+ validates: [test]
1119
+ });
1120
+ } else {
1121
+ field = fields[index];
1122
+ if (field.validates != null) {
1123
+ if (Array.isArray(field.validates)) {
1124
+ return field.validates.push(test);
1125
+ } else {
1126
+ return field.validates = [field.validates, test];
1127
+ }
1128
+ } else {
1129
+ return field.validates = [test];
1130
+ }
1131
+ }
1132
+ };
1133
+
1134
+ Model.types = function(field, type) {
1135
+ var fields, index;
1136
+ if (!this.prototype.hasOwnProperty("fields")) {
1137
+ this.prototype.fields = [];
1138
+ }
1139
+ fields = this.prototype.fields;
1140
+ index = this.findFieldIndex(field);
1141
+ if (index < 0) {
1142
+ return fields.push({
1143
+ name: field,
1144
+ type: type
1145
+ });
1146
+ } else {
1147
+ return fields[index].type = type;
1148
+ }
1149
+ };
1150
+
1151
+ Model.displays = function(field, display) {
1152
+ var fields, index;
1153
+ if (!this.prototype.hasOwnProperty("fields")) {
1154
+ this.prototype.fields = [];
1155
+ }
1156
+ fields = this.prototype.fields;
1157
+ index = this.findFieldIndex(field);
1158
+ if (index < 0) {
1159
+ return fields.push({
1160
+ name: field,
1161
+ display: display
1162
+ });
1163
+ } else {
1164
+ return fields[index].display = display;
1165
+ }
1166
+ };
1167
+
1168
+ Model.hasMany = function(name, options) {
1169
+ var methodName, self;
1170
+ if (options == null) {
1171
+ options = {};
1172
+ }
1173
+ methodName = options.as || MVCoffee.Pluralizer.pluralize(name);
1174
+ if (!this.prototype.hasOwnProperty("_associations_children")) {
1175
+ this.prototype._associations_children = [];
1176
+ }
1177
+ this.prototype._associations_children.push(methodName);
1178
+ self = this;
1179
+ return this.prototype[methodName] = function() {
1180
+ var constraints, foreignKey, j, join, joinTable, joins, len1, modelStore, record, result;
1181
+ modelStore = self.prototype.modelStore;
1182
+ foreignKey = options.foreignKey || options.foreign_key || (self.prototype.modelName + "_id");
1183
+ result = [];
1184
+ if (modelStore != null) {
1185
+ constraints = {};
1186
+ constraints[foreignKey] = this.id;
1187
+ if (options.through) {
1188
+ joinTable = options.through;
1189
+ joins = modelStore.where(joinTable, constraints);
1190
+ for (j = 0, len1 = joins.length; j < len1; j++) {
1191
+ join = joins[j];
1192
+ record = modelStore.find(name, join[name + "_id"]);
1193
+ if (record) {
1194
+ result.push(record);
1195
+ }
1196
+ }
1197
+ } else {
1198
+ result = modelStore.where(name, constraints);
1199
+ }
1200
+ }
1201
+ if (options.order) {
1202
+ result = self.order(result, options.order);
1203
+ }
1204
+ return result;
1205
+ };
1206
+ };
1207
+
1208
+ Model.has_many = Model.hasMany;
1209
+
1210
+ Model.hasOne = function(name, options) {
1211
+ var methodName, self;
1212
+ if (options == null) {
1213
+ options = {};
1214
+ }
1215
+ methodName = options.as || name;
1216
+ if (!this.prototype.hasOwnProperty("_associations_children")) {
1217
+ this.prototype._associations_children = [];
1218
+ }
1219
+ this.prototype._associations_children.push(methodName);
1220
+ self = this;
1221
+ return this.prototype[methodName] = function() {
1222
+ var constraints, foreignKey, modelStore, result;
1223
+ modelStore = self.prototype.modelStore;
1224
+ foreignKey = options.foreignKey || options.foreign_key || (self.prototype.modelName + "_id");
1225
+ result = null;
1226
+ if (modelStore != null) {
1227
+ constraints = {};
1228
+ constraints[foreignKey] = this.id;
1229
+ result = modelStore.findBy(name, constraints);
1230
+ }
1231
+ return result;
1232
+ };
1233
+ };
1234
+
1235
+ Model.has_one = Model.hasOne;
1236
+
1237
+ Model.belongsTo = function(name, options) {
1238
+ var foreignKey, methodName, self;
1239
+ if (options == null) {
1240
+ options = {};
1241
+ }
1242
+ methodName = options.as || name;
1243
+ foreignKey = options.foreignKey || options.foreign_key || (name + "_id");
1244
+ self = this;
1245
+ return this.prototype[methodName] = function() {
1246
+ var modelStore, result;
1247
+ modelStore = self.prototype.modelStore;
1248
+ result = null;
1249
+ if (modelStore != null) {
1250
+ result = modelStore.find(name, this[foreignKey]);
1251
+ }
1252
+ return result;
1253
+ };
1254
+ };
1255
+
1256
+ Model.belongs_to = Model.belongsTo;
1257
+
1258
+ Model.prototype.isValid = function() {
1259
+ return this.valid;
1260
+ };
1261
+
1262
+ Model.prototype.populate = function(obj) {
1263
+ var field, j, len1, ref, selector;
1264
+ if (obj != null) {
1265
+ this.update(obj);
1266
+ } else {
1267
+ ref = this.fields;
1268
+ for (j = 0, len1 = ref.length; j < len1; j++) {
1269
+ field = ref[j];
1270
+ if (this.modelName != null) {
1271
+ selector = "#" + this.modelName + "_" + field.name;
1272
+ } else {
1273
+ selector = "#" + field.name;
1274
+ }
1275
+ if ((field.type != null) && field.type === 'boolean') {
1276
+ this[field.name] = jQuery(selector).is(':checked');
1277
+ } else {
1278
+ this[field.name] = jQuery(selector).val();
1279
+ }
1280
+ }
1281
+ }
1282
+ return this.validate();
1283
+ };
1284
+
1285
+ Model.prototype.validate = function() {
1286
+ var confirm, field, isNumber, j, k, len1, len2, matches, ref, subval, tokenizer, validation, validations, value;
1287
+ this.valid = true;
1288
+ this.errors = [];
1289
+ this.errorsForField = {};
1290
+ ref = this.fields;
1291
+ for (j = 0, len1 = ref.length; j < len1; j++) {
1292
+ field = ref[j];
1293
+ if (field.validates != null) {
1294
+ validations = field.validates;
1295
+ if (!Array.isArray(validations)) {
1296
+ validations = [validations];
1297
+ }
1298
+ for (k = 0, len2 = validations.length; k < len2; k++) {
1299
+ validation = validations[k];
1300
+ if (validation.test === 'acceptance') {
1301
+ value = validation.accept;
1302
+ if (value == null) {
1303
+ value = '1';
1304
+ }
1305
+ this.__performValidation(field, validation, null, "must be accepted", function(val) {
1306
+ return val === value;
1307
+ });
1308
+ } else if (validation.test === 'confirmation') {
1309
+ confirm = this[field.name + "_confirmation"];
1310
+ this.__performValidation(field, validation, null, "doesn't match confirmation", function(val) {
1311
+ return (val != null) && val !== "" && (confirm != null) && val === confirm;
1312
+ });
1313
+ } else if (validation.test === 'email') {
1314
+ this.__performValidation(field, validation, null, "must be a valid email address", function(val) {
1315
+ return (val != null) && val.match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/);
1316
+ });
1317
+ } else if (validation.test === 'exclusion') {
1318
+ matches = validation["in"] || [];
1319
+ this.__performValidation(field, validation, null, "is reserved", function(val) {
1320
+ return (val != null) && !(indexOf.call(matches, val) >= 0);
1321
+ });
1322
+ } else if (validation.test === 'format') {
1323
+ matches = validation["with"] || /.*/;
1324
+ this.__performValidation(field, validation, null, "is invalid", function(val) {
1325
+ return (val != null) && matches.test(val);
1326
+ });
1327
+ } else if (validation.test === 'inclusion') {
1328
+ matches = validation["in"] || [];
1329
+ this.__performValidation(field, validation, null, "is not included in the list", function(val) {
1330
+ return (val != null) && indexOf.call(matches, val) >= 0;
1331
+ });
1332
+ } else if (validation.test === 'length') {
1333
+ tokenizer = function(val) {
1334
+ return val.split("");
1335
+ };
1336
+ if (validation.tokenizer != null) {
1337
+ tokenizer = validation.tokenizer;
1338
+ }
1339
+ if (validation.minimum != null) {
1340
+ if (typeof validation.minimum === "number") {
1341
+ subval = null;
1342
+ value = validation.minimum;
1343
+ } else {
1344
+ subval = validation.minimum;
1345
+ value = subval.value;
1346
+ }
1347
+ this.__performValidation(field, validation, subval, "is too short (minimum is " + value + " characters)", function(val) {
1348
+ return (val != null) && tokenizer(val).length >= value;
1349
+ });
1350
+ }
1351
+ if (validation.maximum != null) {
1352
+ if (typeof validation.maximum === "number") {
1353
+ subval = null;
1354
+ value = validation.maximum;
1355
+ } else {
1356
+ subval = validation.maximum;
1357
+ value = subval.value;
1358
+ }
1359
+ this.__performValidation(field, validation, subval, "is too long (maximum is " + value + " characters)", function(val) {
1360
+ return (val == null) || tokenizer(val).length <= value;
1361
+ });
1362
+ }
1363
+ if (validation["is"] != null) {
1364
+ if (typeof validation["is"] === "number") {
1365
+ subval = null;
1366
+ value = validation["is"];
1367
+ } else {
1368
+ subval = validation["is"];
1369
+ value = subval.value;
1370
+ }
1371
+ this.__performValidation(field, validation, subval, "is the wrong length (must be " + value + " characters)", function(val) {
1372
+ return (val != null) && tokenizer(val).length === value;
1373
+ });
1374
+ }
1375
+ } else if (validation.test === 'numericality') {
1376
+ if ((validation.only_integer != null) && validation.only_integer) {
1377
+ isNumber = this.__performValidation(field, validation, null, "must be an integer", function(val) {
1378
+ return /^[-+]?\d+$/.test(val);
1379
+ });
1380
+ } else {
1381
+ isNumber = this.__performValidation(field, validation, null, "must be a number", function(val) {
1382
+ var number;
1383
+ number = parseFloat(val);
1384
+ return /^[-+]?\d*\.?\d*(e\d+)?$/.test(val) && number === number;
1385
+ });
1386
+ }
1387
+ if (isNumber) {
1388
+ if (validation.greater_than != null) {
1389
+ if (typeof validation.greater_than === "number") {
1390
+ value = validation.greater_than;
1391
+ subval = null;
1392
+ } else {
1393
+ subval = validation.greater_than;
1394
+ value = subval.value;
1395
+ }
1396
+ this.__performValidation(field, validation, subval, "must be greater than " + value, function(val) {
1397
+ return parseFloat(val) > value;
1398
+ });
1399
+ }
1400
+ if (validation.greater_than_or_equal_to != null) {
1401
+ if (typeof validation.greater_than_or_equal_to === "number") {
1402
+ subval = null;
1403
+ value = validation.greater_than_or_equal_to;
1404
+ } else {
1405
+ subval = validation.greater_than_or_equal_to;
1406
+ value = subval.value;
1407
+ }
1408
+ this.__performValidation(field, validation, subval, "must be greater than or equal to " + value, function(val) {
1409
+ return parseFloat(val) >= value;
1410
+ });
1411
+ }
1412
+ if (validation.equal_to != null) {
1413
+ if (typeof validation.equal_to === "number") {
1414
+ subval = null;
1415
+ value = validation.equal_to;
1416
+ } else {
1417
+ subval = validation.equal_to;
1418
+ value = subval.value;
1419
+ }
1420
+ this.__performValidation(field, validation, subval, "must be equal to " + value, function(val) {
1421
+ return parseFloat(val) === value;
1422
+ });
1423
+ }
1424
+ if (validation.less_than != null) {
1425
+ if (typeof validation.less_than === "number") {
1426
+ value = validation.less_than;
1427
+ subval = null;
1428
+ } else {
1429
+ subval = validation.less_than;
1430
+ value = subval.value;
1431
+ }
1432
+ this.__performValidation(field, validation, subval, "must be less than " + value, function(val) {
1433
+ return parseFloat(val) < value;
1434
+ });
1435
+ }
1436
+ if (validation.less_than_or_equal_to != null) {
1437
+ if (typeof validation.less_than_or_equal_to === "number") {
1438
+ subval = null;
1439
+ value = validation.less_than_or_equal_to;
1440
+ } else {
1441
+ subval = validation.less_than_or_equal_to;
1442
+ value = subval.value;
1443
+ }
1444
+ this.__performValidation(field, validation, subval, "must be less than or equal to " + value, function(val) {
1445
+ return parseFloat(val) <= value;
1446
+ });
1447
+ }
1448
+ if (validation.odd != null) {
1449
+ if (typeof validation.odd === "boolean") {
1450
+ subval = null;
1451
+ value = validation.odd;
1452
+ } else {
1453
+ subval = validation.odd;
1454
+ value = true;
1455
+ }
1456
+ if (value) {
1457
+ this.__performValidation(field, validation, subval, "must be odd", function(val) {
1458
+ return Math.abs(parseFloat(val)) % 2 === 1;
1459
+ });
1460
+ }
1461
+ }
1462
+ if (validation.even != null) {
1463
+ if (typeof validation.even === "boolean") {
1464
+ subval = null;
1465
+ value = validation.even;
1466
+ } else {
1467
+ subval = validation.even;
1468
+ value = true;
1469
+ }
1470
+ if (value) {
1471
+ this.__performValidation(field, validation, subval, "must be even", function(val) {
1472
+ return parseFloat(val) % 2 === 0;
1473
+ });
1474
+ }
1475
+ }
1476
+ }
1477
+ } else if (validation.test === 'presence') {
1478
+ this.__performValidation(field, validation, null, "can't be empty", function(val) {
1479
+ return (val != null) && /\S+/.test(val);
1480
+ });
1481
+ } else if (validation.test === 'absence') {
1482
+ this.__performValidation(field, validation, null, "must be blank", function(val) {
1483
+ return !((val != null) && /\S+/.test(val));
1484
+ });
1485
+ } else {
1486
+ if (typeof this[validation.test] !== 'function') {
1487
+ throw "custom validation is not a function";
1488
+ }
1489
+ this.__performValidation(field, validation, null, "is invalid", this[validation.test]);
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+ return this.valid;
1495
+ };
1496
+
1497
+ Model.prototype.__performValidation = function(field, validation, subval, message, comparison) {
1498
+ var data;
1499
+ if ((validation.only_if != null) && !this[validation.only_if]()) {
1500
+ return true;
1501
+ }
1502
+ if ((validation.unless != null) && this[validation.unless]()) {
1503
+ return true;
1504
+ }
1505
+ if (subval != null) {
1506
+ if ((subval.only_if != null) && !this[subval.only_if]()) {
1507
+ return true;
1508
+ }
1509
+ if ((subval.unless != null) && this[subval.unless]()) {
1510
+ return true;
1511
+ }
1512
+ }
1513
+ data = this[field.name];
1514
+ if ((validation.allow_null != null) && validation.allow_null && (data == null)) {
1515
+ return true;
1516
+ }
1517
+ if ((validation.allow_blank != null) && validation.allow_blank && ((data == null) || !/\S+/.test(data))) {
1518
+ return true;
1519
+ }
1520
+ if (!comparison(data)) {
1521
+ this.addError(field, validation, subval, message);
1522
+ return false;
1523
+ }
1524
+ return true;
1525
+ };
1526
+
1527
+ Model.prototype.addError = function(field, validation, subval, message) {
1528
+ var base, errorMessage, name, name1;
1529
+ this.valid = false;
1530
+ name = field.display;
1531
+ if (name == null) {
1532
+ name = field.name;
1533
+ if (name.length > 0) {
1534
+ name = name[0].toUpperCase() + name.slice(1);
1535
+ name = name.split("_").join(" ");
1536
+ }
1537
+ }
1538
+ if ((subval != null ? subval.message : void 0) != null) {
1539
+ errorMessage = name + " " + subval.message;
1540
+ } else if ((validation != null ? validation.message : void 0) != null) {
1541
+ errorMessage = name + " " + validation.message;
1542
+ } else {
1543
+ errorMessage = name + " " + message;
1544
+ }
1545
+ this.errors.push(errorMessage);
1546
+ if ((base = this.errorsForField)[name1 = field.name] == null) {
1547
+ base[name1] = [];
1548
+ }
1549
+ return this.errorsForField[field.name].push(errorMessage);
1550
+ };
1551
+
1552
+ return Model;
1553
+
1554
+ })();
1555
+
1556
+ }).call(this);