serenade 0.4.1 → 0.4.2

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Serenade.js JavaScript Framework v0.4.1
3
- * Revision: ae7d321b18
2
+ * Serenade.js JavaScript Framework v0.4.2
3
+ * Revision: 0f6ce46ebe
4
4
  * http://github.com/elabs/serenade.js
5
5
  *
6
6
  * Copyright 2011, Jonas Nicklas, Elabs AB
@@ -354,10 +354,11 @@ exports.main = function commonjsMain(args) {
354
354
  if (typeof module !== 'undefined' && require.main === module) {
355
355
  exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
356
356
  }
357
- }var AssociationCollection, COMMENT, Cache, Collection, Compile, CompiledView, DynamicNode, Event, IDENTIFIER, KEYWORDS, LITERAL, Lexer, MULTI_DENT, Model, Node, Property, PropertyAccessor, PropertyDefinition, STRING, Serenade, View, WHITESPACE, assignUnlessEqual, capitalize, compile, def, defineEvent, defineOptions, defineProperty, extend, format, getValue, globalDependencies, idCounter, isArray, isArrayIndex, merge, normalize, pairToObject, safeDelete, safePush, serializeObject, settings, triggerGlobal,
357
+ }var AssociationCollection, COMMENT, Cache, Collection, Compile, CompiledView, DynamicNode, Event, IDENTIFIER, KEYWORDS, LITERAL, Lexer, MULTI_DENT, Model, Node, Property, PropertyAccessor, PropertyDefinition, STRING, Serenade, View, WHITESPACE, assignUnlessEqual, capitalize, compile, def, defineEvent, defineOptions, defineProperty, extend, format, getValue, idCounter, isArray, isArrayIndex, merge, normalize, pairToObject, safeDelete, safePush, serializeObject, settings,
358
358
  __hasProp = {}.hasOwnProperty,
359
359
  __slice = [].slice,
360
360
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
361
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
361
362
  __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; };
362
363
 
363
364
  settings = {
@@ -370,6 +371,7 @@ defineOptions = function(object, name) {
370
371
  return def(object, name, {
371
372
  get: function() {
372
373
  var options;
374
+
373
375
  if (!this.hasOwnProperty("_" + name)) {
374
376
  options = name in Object.getPrototypeOf(this) ? Object.create(Object.getPrototypeOf(this)[name]) : {};
375
377
  def(this, "_" + name, {
@@ -385,6 +387,7 @@ defineOptions = function(object, name) {
385
387
 
386
388
  extend = function(target, source, enumerable) {
387
389
  var key, value;
390
+
388
391
  if (enumerable == null) {
389
392
  enumerable = true;
390
393
  }
@@ -430,6 +433,7 @@ isArray = function(object) {
430
433
 
431
434
  pairToObject = function(one, two) {
432
435
  var temp;
436
+
433
437
  temp = {};
434
438
  temp[one] = two;
435
439
  return temp;
@@ -437,6 +441,7 @@ pairToObject = function(one, two) {
437
441
 
438
442
  serializeObject = function(object) {
439
443
  var item, _i, _len, _results;
444
+
440
445
  if (object && typeof object.toJSON === 'function') {
441
446
  return object.toJSON();
442
447
  } else if (isArray(object)) {
@@ -473,6 +478,7 @@ safePush = function(object, collection, item) {
473
478
 
474
479
  safeDelete = function(object, collection, item) {
475
480
  var index;
481
+
476
482
  if (object[collection] && (index = object[collection].indexOf(item)) !== -1) {
477
483
  if (!object.hasOwnProperty(collection)) {
478
484
  def(object, collection, {
@@ -484,7 +490,6 @@ safeDelete = function(object, collection, item) {
484
490
  };
485
491
 
486
492
  Event = (function() {
487
-
488
493
  function Event(object, name, options) {
489
494
  this.object = object;
490
495
  this.name = name;
@@ -502,16 +507,20 @@ Event = (function() {
502
507
  });
503
508
 
504
509
  Event.prototype.trigger = function() {
505
- var args, _base,
510
+ var args,
506
511
  _this = this;
512
+
507
513
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
508
- this.queue.push(args);
509
- if (this.async) {
510
- return (_base = this.queue).timeout || (_base.timeout = setTimeout((function() {
511
- return _this.resolve();
512
- }), 0));
513
- } else {
514
- return this.resolve();
514
+ if (this.listeners.length) {
515
+ this.queue.push(args);
516
+ if (this.async) {
517
+ clearTimeout(this.queue.timeout);
518
+ return this.queue.timeout = setTimeout((function() {
519
+ return _this.resolve();
520
+ }), this.options.timeout || 0);
521
+ } else {
522
+ return this.resolve();
523
+ }
515
524
  }
516
525
  };
517
526
 
@@ -525,6 +534,7 @@ Event = (function() {
525
534
  Event.prototype.one = function(fun) {
526
535
  var unbind,
527
536
  _this = this;
537
+
528
538
  unbind = function(fun) {
529
539
  return _this.unbind(fun);
530
540
  };
@@ -544,20 +554,24 @@ Event = (function() {
544
554
  Event.prototype.resolve = function() {
545
555
  var args, perform, _i, _len, _ref,
546
556
  _this = this;
547
- perform = function(args) {
548
- if (_this.listeners) {
549
- return _this.listeners.forEach(function(listener) {
550
- return listener.apply(_this.object, args);
551
- });
552
- }
553
- };
554
- if (this.options.optimize) {
555
- perform(this.options.optimize(this.queue));
556
- } else {
557
- _ref = this.queue;
558
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
559
- args = _ref[_i];
560
- perform(args);
557
+
558
+ clearTimeout(this.queue.timeout);
559
+ if (this.queue.length) {
560
+ perform = function(args) {
561
+ if (_this.listeners) {
562
+ return ([].concat(_this.listeners)).forEach(function(listener) {
563
+ return listener.apply(_this.object, args);
564
+ });
565
+ }
566
+ };
567
+ if (this.options.optimize) {
568
+ perform(this.options.optimize(this.queue));
569
+ } else {
570
+ _ref = this.queue;
571
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
572
+ args = _ref[_i];
573
+ perform(args);
574
+ }
561
575
  }
562
576
  }
563
577
  return this.queue = [];
@@ -565,7 +579,7 @@ Event = (function() {
565
579
 
566
580
  def(Event.prototype, "listeners", {
567
581
  get: function() {
568
- return this.object._s["listeners_" + this.name];
582
+ return this.object._s["listeners_" + this.name] || [];
569
583
  }
570
584
  });
571
585
 
@@ -604,6 +618,7 @@ Cache = {
604
618
  _identityMap: {},
605
619
  get: function(ctor, id) {
606
620
  var name, _ref;
621
+
607
622
  name = ctor.uniqueId();
608
623
  if (name && id) {
609
624
  return (_ref = this._identityMap[name]) != null ? _ref[id] : void 0;
@@ -611,6 +626,7 @@ Cache = {
611
626
  },
612
627
  set: function(ctor, id, obj) {
613
628
  var name, _base;
629
+
614
630
  name = ctor.uniqueId();
615
631
  if (name && id) {
616
632
  (_base = this._identityMap)[name] || (_base[name] = {});
@@ -619,6 +635,7 @@ Cache = {
619
635
  },
620
636
  unset: function(ctor, id) {
621
637
  var name, _base;
638
+
622
639
  name = ctor.uniqueId();
623
640
  if (name && id) {
624
641
  (_base = this._identityMap)[name] || (_base[name] = {});
@@ -632,22 +649,25 @@ isArrayIndex = function(index) {
632
649
  };
633
650
 
634
651
  Collection = (function() {
635
- var fun, _i, _len, _ref;
636
-
637
- defineEvent(Collection.prototype, "change_set");
638
-
639
- defineEvent(Collection.prototype, "change_add");
652
+ var _this = this;
640
653
 
641
- defineEvent(Collection.prototype, "change_update");
642
-
643
- defineEvent(Collection.prototype, "change_insert");
654
+ defineEvent(Collection.prototype, "change");
644
655
 
645
- defineEvent(Collection.prototype, "change_delete");
656
+ def(Collection.prototype, "first", {
657
+ get: function() {
658
+ return this[0];
659
+ }
660
+ });
646
661
 
647
- defineEvent(Collection.prototype, "change");
662
+ def(Collection.prototype, "last", {
663
+ get: function() {
664
+ return this[this.length - 1];
665
+ }
666
+ });
648
667
 
649
668
  function Collection(list) {
650
669
  var index, val, _i, _len;
670
+
651
671
  if (list == null) {
652
672
  list = [];
653
673
  }
@@ -667,14 +687,12 @@ Collection = (function() {
667
687
  if (isArrayIndex(index)) {
668
688
  this.length = Math.max(this.length, index + 1);
669
689
  }
670
- this.change_set.trigger(index, value);
671
- this.change.trigger(this);
672
690
  return value;
673
691
  };
674
692
 
675
693
  Collection.prototype.update = function(list) {
676
- var index, old, val, _, _i, _len;
677
- old = this.clone();
694
+ var index, val, _, _i, _len;
695
+
678
696
  for (index in this) {
679
697
  _ = this[index];
680
698
  if (isArrayIndex(index)) {
@@ -686,8 +704,6 @@ Collection = (function() {
686
704
  this[index] = val;
687
705
  }
688
706
  this.length = (list != null ? list.length : void 0) || 0;
689
- this.change_update.trigger(old, this);
690
- this.change.trigger(this);
691
707
  return list;
692
708
  };
693
709
 
@@ -707,6 +723,7 @@ Collection = (function() {
707
723
 
708
724
  Collection.prototype.find = function(fun) {
709
725
  var item, _i, _len;
726
+
710
727
  for (_i = 0, _len = this.length; _i < _len; _i++) {
711
728
  item = this[_i];
712
729
  if (fun(item)) {
@@ -717,42 +734,50 @@ Collection = (function() {
717
734
 
718
735
  Collection.prototype.insertAt = function(index, value) {
719
736
  Array.prototype.splice.call(this, index, 0, value);
720
- this.change_insert.trigger(index, value);
721
- this.change.trigger(this);
722
737
  return value;
723
738
  };
724
739
 
725
740
  Collection.prototype.deleteAt = function(index) {
726
741
  var value;
742
+
727
743
  value = this[index];
728
744
  Array.prototype.splice.call(this, index, 1);
729
- this.change_delete.trigger(index, value);
730
- this.change.trigger(this);
731
745
  return value;
732
746
  };
733
747
 
734
748
  Collection.prototype["delete"] = function(item) {
735
749
  var index;
750
+
736
751
  index = this.indexOf(item);
737
752
  if (index !== -1) {
738
753
  return this.deleteAt(index);
739
754
  }
740
755
  };
741
756
 
742
- def(Collection.prototype, "first", {
743
- get: function() {
744
- return this[0];
745
- }
746
- });
757
+ Collection.prototype.concat = function() {
758
+ var arg, args, _ref;
747
759
 
748
- def(Collection.prototype, "last", {
749
- get: function() {
750
- return this[this.length - 1];
751
- }
752
- });
760
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
761
+ args = (function() {
762
+ var _i, _len, _results;
763
+
764
+ _results = [];
765
+ for (_i = 0, _len = args.length; _i < _len; _i++) {
766
+ arg = args[_i];
767
+ if (arg instanceof Collection) {
768
+ _results.push(arg.toArray());
769
+ } else {
770
+ _results.push(arg);
771
+ }
772
+ }
773
+ return _results;
774
+ })();
775
+ return new Collection((_ref = this.toArray()).concat.apply(_ref, args));
776
+ };
753
777
 
754
778
  Collection.prototype.toArray = function() {
755
779
  var array, index, val;
780
+
756
781
  array = [];
757
782
  for (index in this) {
758
783
  val = this[index];
@@ -767,122 +792,62 @@ Collection = (function() {
767
792
  return new Collection(this.toArray());
768
793
  };
769
794
 
770
- Collection.prototype.push = function(element) {
771
- this[this.length++] = element;
772
- this.change_add.trigger(element);
773
- this.change.trigger(this);
774
- return element;
775
- };
776
-
777
- Collection.prototype.pop = function() {
778
- return this.deleteAt(this.length - 1);
795
+ Collection.prototype.toString = function() {
796
+ return this.toArray().toString();
779
797
  };
780
798
 
781
- Collection.prototype.unshift = function(item) {
782
- return this.insertAt(0, item);
799
+ Collection.prototype.toLocaleString = function() {
800
+ return this.toArray().toLocaleString();
783
801
  };
784
802
 
785
- Collection.prototype.shift = function() {
786
- return this.deleteAt(0);
803
+ Collection.prototype.toJSON = function() {
804
+ return serializeObject(this.toArray());
787
805
  };
788
806
 
789
- Collection.prototype.splice = function() {
790
- var deleteCount, deleted, list, old, start;
791
- start = arguments[0], deleteCount = arguments[1], list = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
792
- old = this.clone();
793
- deleted = Array.prototype.splice.apply(this, [start, deleteCount].concat(__slice.call(list)));
794
- this.change_update.trigger(old, this);
795
- this.change.trigger(this);
796
- return new Collection(deleted);
797
- };
798
-
799
- Collection.prototype.sort = function(fun) {
800
- var old;
801
- old = this.clone();
802
- Array.prototype.sort.call(this, fun);
803
- this.change_update.trigger(old, this);
804
- this.change.trigger(this);
805
- return this;
806
- };
807
-
808
- Collection.prototype.reverse = function() {
809
- var old;
810
- old = this.clone();
811
- Array.prototype.reverse.call(this);
812
- this.change_update.trigger(old, this);
813
- this.change.trigger(this);
814
- return this;
815
- };
816
-
817
- _ref = ["forEach", "indexOf", "lastIndexOf", "join", "every", "some", "reduce", "reduceRight"];
818
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
819
- fun = _ref[_i];
820
- Collection.prototype[fun] = Array.prototype[fun];
821
- }
822
-
823
- Collection.prototype.map = function() {
824
- var args;
825
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
826
- return new Collection(Array.prototype.map.apply(this, args));
827
- };
807
+ Object.getOwnPropertyNames(Array.prototype).forEach(function(fun) {
808
+ var _base;
828
809
 
829
- Collection.prototype.filter = function() {
830
- var args;
831
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
832
- return new Collection(Array.prototype.filter.apply(this, args));
833
- };
810
+ return (_base = Collection.prototype)[fun] || (_base[fun] = Array.prototype[fun]);
811
+ });
834
812
 
835
- Collection.prototype.slice = function() {
836
- var args, _ref1;
837
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
838
- return new Collection((_ref1 = this.toArray()).slice.apply(_ref1, args));
839
- };
813
+ ["splice", "map", "filter", "slice"].forEach(function(fun) {
814
+ var original;
840
815
 
841
- Collection.prototype.concat = function() {
842
- var arg, args, _ref1;
843
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
844
- args = (function() {
845
- var _j, _len1, _results;
846
- _results = [];
847
- for (_j = 0, _len1 = args.length; _j < _len1; _j++) {
848
- arg = args[_j];
849
- if (arg instanceof Collection) {
850
- _results.push(arg.toArray());
851
- } else {
852
- _results.push(arg);
853
- }
854
- }
855
- return _results;
856
- })();
857
- return new Collection((_ref1 = this.toArray()).concat.apply(_ref1, args));
858
- };
816
+ original = Collection.prototype[fun];
817
+ return Collection.prototype[fun] = function() {
818
+ return new Collection(original.apply(this, arguments));
819
+ };
820
+ });
859
821
 
860
- Collection.prototype.toString = function() {
861
- return this.toArray().toString();
862
- };
822
+ ["push", "pop", "unshift", "shift", "splice", "sort", "reverse", "update", "set", "insertAt", "deleteAt"].forEach(function(fun) {
823
+ var original;
863
824
 
864
- Collection.prototype.toLocaleString = function() {
865
- return this.toArray().toLocaleString();
866
- };
825
+ original = Collection.prototype[fun];
826
+ return Collection.prototype[fun] = function() {
827
+ var old, val;
867
828
 
868
- Collection.prototype.toJSON = function() {
869
- return serializeObject(this.toArray());
870
- };
829
+ old = this.clone();
830
+ val = original.apply(this, arguments);
831
+ this.change.trigger(old, this);
832
+ return val;
833
+ };
834
+ });
871
835
 
872
836
  return Collection;
873
837
 
874
- })();
838
+ }).call(this);
875
839
 
876
840
  AssociationCollection = (function(_super) {
877
-
878
841
  __extends(AssociationCollection, _super);
879
842
 
880
843
  function AssociationCollection(owner, options, list) {
881
844
  var _this = this;
845
+
882
846
  this.owner = owner;
883
847
  this.options = options;
884
848
  this._convert.apply(this, __slice.call(list).concat([function() {
885
849
  var items;
850
+
886
851
  items = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
887
852
  return AssociationCollection.__super__.constructor.call(_this, items);
888
853
  }]));
@@ -890,6 +855,7 @@ AssociationCollection = (function(_super) {
890
855
 
891
856
  AssociationCollection.prototype.set = function(index, item) {
892
857
  var _this = this;
858
+
893
859
  return this._convert(item, function(item) {
894
860
  return AssociationCollection.__super__.set.call(_this, index, item);
895
861
  });
@@ -897,6 +863,7 @@ AssociationCollection = (function(_super) {
897
863
 
898
864
  AssociationCollection.prototype.push = function(item) {
899
865
  var _this = this;
866
+
900
867
  return this._convert(item, function(item) {
901
868
  return AssociationCollection.__super__.push.call(_this, item);
902
869
  });
@@ -904,8 +871,10 @@ AssociationCollection = (function(_super) {
904
871
 
905
872
  AssociationCollection.prototype.update = function(list) {
906
873
  var _this = this;
874
+
907
875
  return this._convert.apply(this, __slice.call(list).concat([function() {
908
876
  var items;
877
+
909
878
  items = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
910
879
  return AssociationCollection.__super__.update.call(_this, items);
911
880
  }]));
@@ -914,9 +883,11 @@ AssociationCollection = (function(_super) {
914
883
  AssociationCollection.prototype.splice = function() {
915
884
  var deleteCount, list, start,
916
885
  _this = this;
886
+
917
887
  start = arguments[0], deleteCount = arguments[1], list = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
918
888
  return this._convert.apply(this, __slice.call(list).concat([function() {
919
889
  var items;
890
+
920
891
  items = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
921
892
  return AssociationCollection.__super__.splice.apply(_this, [start, deleteCount].concat(__slice.call(items)));
922
893
  }]));
@@ -924,6 +895,7 @@ AssociationCollection = (function(_super) {
924
895
 
925
896
  AssociationCollection.prototype.insertAt = function(index, item) {
926
897
  var _this = this;
898
+
927
899
  return this._convert(item, function(item) {
928
900
  return AssociationCollection.__super__.insertAt.call(_this, index, item);
929
901
  });
@@ -931,9 +903,11 @@ AssociationCollection = (function(_super) {
931
903
 
932
904
  AssociationCollection.prototype._convert = function() {
933
905
  var fn, item, items, returnValue, _i, _j, _len;
906
+
934
907
  items = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), fn = arguments[_i++];
935
908
  items = (function() {
936
909
  var _j, _len, _results;
910
+
937
911
  _results = [];
938
912
  for (_j = 0, _len = items.length; _j < _len; _j++) {
939
913
  item = items[_j];
@@ -959,49 +933,10 @@ AssociationCollection = (function(_super) {
959
933
 
960
934
  })(Collection);
961
935
 
962
- globalDependencies = {};
963
-
964
- triggerGlobal = function(target, names) {
965
- var dependency, name, object, subname, type, _i, _len, _results;
966
- _results = [];
967
- for (_i = 0, _len = names.length; _i < _len; _i++) {
968
- name = names[_i];
969
- if (globalDependencies.hasOwnProperty(name)) {
970
- _results.push((function() {
971
- var _j, _len1, _ref, _ref1, _ref2, _ref3, _results1;
972
- _ref = globalDependencies[name];
973
- _results1 = [];
974
- for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
975
- _ref1 = _ref[_j], name = _ref1.name, type = _ref1.type, object = _ref1.object, subname = _ref1.subname, dependency = _ref1.dependency;
976
- if (type === "singular") {
977
- if (target === object[name]) {
978
- _results1.push((_ref2 = object[dependency + "_property"]) != null ? typeof _ref2.trigger === "function" ? _ref2.trigger(object) : void 0 : void 0);
979
- } else {
980
- _results1.push(void 0);
981
- }
982
- } else if (type === "collection") {
983
- if (__indexOf.call(object[name], target) >= 0) {
984
- _results1.push((_ref3 = object[dependency + "_property"]) != null ? typeof _ref3.trigger === "function" ? _ref3.trigger(object) : void 0 : void 0);
985
- } else {
986
- _results1.push(void 0);
987
- }
988
- } else {
989
- _results1.push(void 0);
990
- }
991
- }
992
- return _results1;
993
- })());
994
- } else {
995
- _results.push(void 0);
996
- }
997
- }
998
- return _results;
999
- };
1000
-
1001
936
  PropertyDefinition = (function() {
1002
-
1003
937
  function PropertyDefinition(name, options) {
1004
938
  var _i, _len, _ref;
939
+
1005
940
  this.name = name;
1006
941
  extend(this, options);
1007
942
  this.dependencies = [];
@@ -1019,14 +954,19 @@ PropertyDefinition = (function() {
1019
954
  def(PropertyDefinition.prototype, "eventOptions", {
1020
955
  get: function() {
1021
956
  var name;
957
+
1022
958
  name = this.name;
1023
959
  return {
1024
960
  async: this.async != null ? this.async : settings.async,
961
+ timeout: this.timeout,
1025
962
  bind: function() {
1026
- return this[name];
963
+ this[name];
964
+ return this[name + "_property"].registerGlobal();
1027
965
  },
1028
966
  optimize: function(queue) {
1029
- return queue[queue.length - 1];
967
+ var _ref, _ref1;
968
+
969
+ return [(_ref = queue[0]) != null ? _ref[0] : void 0, (_ref1 = queue[queue.length - 1]) != null ? _ref1[1] : void 0];
1030
970
  }
1031
971
  };
1032
972
  }
@@ -1034,6 +974,7 @@ PropertyDefinition = (function() {
1034
974
 
1035
975
  PropertyDefinition.prototype.addDependency = function(name) {
1036
976
  var subname, type, _ref, _ref1;
977
+
1037
978
  if (this.dependencies.indexOf(name) === -1) {
1038
979
  this.dependencies.push(name);
1039
980
  if (name.match(/\./)) {
@@ -1062,23 +1003,22 @@ PropertyDefinition = (function() {
1062
1003
  })();
1063
1004
 
1064
1005
  PropertyAccessor = (function() {
1006
+ var _this = this;
1065
1007
 
1066
1008
  function PropertyAccessor(definition, object) {
1067
1009
  this.definition = definition;
1068
1010
  this.object = object;
1011
+ this.trigger = __bind(this.trigger, this);
1069
1012
  this.name = this.definition.name;
1070
1013
  this.valueName = "val_" + this.name;
1071
1014
  this.event = new Event(this.object, this.name + "_change", this.definition.eventOptions);
1015
+ this._gcQueue = [];
1072
1016
  }
1073
1017
 
1074
1018
  PropertyAccessor.prototype.set = function(value) {
1075
- var val;
1076
1019
  if (typeof value === "function") {
1077
1020
  return this.definition.get = value;
1078
1021
  } else {
1079
- if (this.definition.changed) {
1080
- val = this.get();
1081
- }
1082
1022
  if (this.definition.set) {
1083
1023
  this.definition.set.call(this.object, value);
1084
1024
  } else {
@@ -1094,7 +1034,7 @@ PropertyAccessor = (function() {
1094
1034
  PropertyAccessor.prototype.get = function() {
1095
1035
  var listener, value,
1096
1036
  _this = this;
1097
- this.registerGlobal();
1037
+
1098
1038
  if (this.definition.get && !(this.definition.cache && this.valueName in this.object._s)) {
1099
1039
  listener = function(name) {
1100
1040
  return _this.definition.addDependency(name);
@@ -1103,12 +1043,16 @@ PropertyAccessor = (function() {
1103
1043
  this.object._s.property_access.bind(listener);
1104
1044
  }
1105
1045
  value = this.definition.get.call(this.object);
1106
- if (this.definition.cache) {
1107
- this.object._s[this.valueName] = value;
1108
- }
1109
1046
  if (!("dependsOn" in this.definition)) {
1110
1047
  this.object._s.property_access.unbind(listener);
1111
1048
  }
1049
+ if (this.definition.cache) {
1050
+ this.object._s[this.valueName] = value;
1051
+ if (!this._isCached) {
1052
+ this._isCached = true;
1053
+ this.bind(function() {});
1054
+ }
1055
+ }
1112
1056
  } else {
1113
1057
  value = this.object._s[this.valueName];
1114
1058
  }
@@ -1125,78 +1069,166 @@ PropertyAccessor = (function() {
1125
1069
  };
1126
1070
 
1127
1071
  PropertyAccessor.prototype.registerGlobal = function() {
1128
- var name, subname, type, _i, _len, _ref, _ref1, _results;
1129
- if (!this.object._s["glb_" + this.name]) {
1130
- this.object._s["glb_" + this.name] = true;
1131
- _ref = this.definition.globalDependencies;
1132
- _results = [];
1072
+ var dependency, _i, _len, _ref, _ref1, _results,
1073
+ _this = this;
1074
+
1075
+ if (this._isRegistered) {
1076
+ return;
1077
+ }
1078
+ this._isRegistered = true;
1079
+ this.definition.globalDependencies.forEach(function(dep) {
1080
+ var name, subname, type, updateCollectionBindings, updateItemBinding, updateItemBindings, _ref, _ref1;
1081
+
1082
+ name = dep.name, type = dep.type, subname = dep.subname;
1083
+ switch (type) {
1084
+ case "singular":
1085
+ updateItemBinding = function(before, after) {
1086
+ var _ref, _ref1;
1087
+
1088
+ if (before != null) {
1089
+ if ((_ref = before[subname + "_property"]) != null) {
1090
+ _ref.unbind(_this.trigger);
1091
+ }
1092
+ }
1093
+ return after != null ? (_ref1 = after[subname + "_property"]) != null ? _ref1.bind(_this.trigger) : void 0 : void 0;
1094
+ };
1095
+ if ((_ref = _this.object[name + "_property"]) != null) {
1096
+ _ref.bind(updateItemBinding);
1097
+ }
1098
+ updateItemBinding(void 0, _this.object[name]);
1099
+ return _this._gcQueue.push(function() {
1100
+ var _ref1;
1101
+
1102
+ updateItemBinding(_this.object[name], void 0);
1103
+ return (_ref1 = _this.object[name + "_property"]) != null ? _ref1.unbind(updateItemBinding) : void 0;
1104
+ });
1105
+ case "collection":
1106
+ updateItemBindings = function(before, after) {
1107
+ if (before != null) {
1108
+ if (typeof before.forEach === "function") {
1109
+ before.forEach(function(item) {
1110
+ var _ref1;
1111
+
1112
+ return (_ref1 = item[subname + "_property"]) != null ? _ref1.unbind(_this.trigger) : void 0;
1113
+ });
1114
+ }
1115
+ }
1116
+ return after != null ? typeof after.forEach === "function" ? after.forEach(function(item) {
1117
+ var _ref1;
1118
+
1119
+ return (_ref1 = item[subname + "_property"]) != null ? _ref1.bind(_this.trigger) : void 0;
1120
+ }) : void 0 : void 0;
1121
+ };
1122
+ updateCollectionBindings = function(before, after) {
1123
+ var _ref1, _ref2, _ref3, _ref4;
1124
+
1125
+ updateItemBindings(before, after);
1126
+ if (before != null) {
1127
+ if ((_ref1 = before.change) != null) {
1128
+ _ref1.unbind(_this.trigger);
1129
+ }
1130
+ }
1131
+ if (after != null) {
1132
+ if ((_ref2 = after.change) != null) {
1133
+ _ref2.bind(_this.trigger);
1134
+ }
1135
+ }
1136
+ if (before != null) {
1137
+ if ((_ref3 = before.change) != null) {
1138
+ _ref3.unbind(updateItemBindings);
1139
+ }
1140
+ }
1141
+ return after != null ? (_ref4 = after.change) != null ? _ref4.bind(updateItemBindings) : void 0 : void 0;
1142
+ };
1143
+ if ((_ref1 = _this.object[name + "_property"]) != null) {
1144
+ _ref1.bind(updateCollectionBindings);
1145
+ }
1146
+ updateCollectionBindings(void 0, _this.object[name]);
1147
+ return _this._gcQueue.push(function() {
1148
+ var _ref2;
1149
+
1150
+ updateCollectionBindings(_this.object[name], void 0);
1151
+ return (_ref2 = _this.object[name + "_property"]) != null ? _ref2.unbind(updateCollectionBindings) : void 0;
1152
+ });
1153
+ }
1154
+ });
1155
+ _ref = this.definition.localDependencies;
1156
+ _results = [];
1157
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1158
+ dependency = _ref[_i];
1159
+ _results.push((_ref1 = this.object[dependency + "_property"]) != null ? _ref1.registerGlobal() : void 0);
1160
+ }
1161
+ return _results;
1162
+ };
1163
+
1164
+ PropertyAccessor.prototype.gc = function() {
1165
+ var dependency, fn, _i, _j, _len, _len1, _ref, _ref1, _ref2, _results,
1166
+ _this = this;
1167
+
1168
+ if (this.listeners.length === 0 && !this.dependentProperties.find(function(prop) {
1169
+ return prop.listeners.length !== 0;
1170
+ })) {
1171
+ _ref = this._gcQueue;
1133
1172
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1134
- _ref1 = _ref[_i], name = _ref1.name, type = _ref1.type, subname = _ref1.subname;
1135
- globalDependencies[subname] || (globalDependencies[subname] = []);
1136
- _results.push(globalDependencies[subname].push({
1137
- object: this.object,
1138
- subname: subname,
1139
- name: name,
1140
- type: type,
1141
- dependency: this.name
1142
- }));
1173
+ fn = _ref[_i];
1174
+ fn();
1143
1175
  }
1144
- return _results;
1176
+ this._isRegistered = false;
1145
1177
  }
1178
+ _ref1 = this.definition.localDependencies;
1179
+ _results = [];
1180
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
1181
+ dependency = _ref1[_j];
1182
+ _results.push((_ref2 = this.object[dependency + "_property"]) != null ? _ref2.gc() : void 0);
1183
+ }
1184
+ return _results;
1146
1185
  };
1147
1186
 
1148
1187
  PropertyAccessor.prototype.trigger = function() {
1149
- var changes, name, prop, value, _i, _len, _ref, _ref1;
1188
+ var changes, name, newValue, _i, _len, _ref, _ref1;
1189
+
1150
1190
  this.clearCache();
1151
1191
  if (this.hasChanged()) {
1152
- value = this.get();
1192
+ newValue = this.get();
1193
+ this.event.trigger(this._oldValue, newValue);
1153
1194
  changes = {};
1154
- _ref = this.dependents;
1155
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1156
- name = _ref[_i];
1157
- if (name !== this.name) {
1158
- changes[name] = this.object[name];
1159
- }
1160
- }
1161
- this.event.trigger(value);
1162
- for (name in changes) {
1163
- if (!__hasProp.call(changes, name)) continue;
1164
- value = changes[name];
1165
- prop = this.object[name + "_property"];
1166
- prop.clearCache();
1167
- if (prop.hasChanged()) {
1168
- prop.event.trigger(value);
1195
+ changes[this.name] = newValue;
1196
+ if ((_ref = this.object.changed) != null) {
1197
+ if (typeof _ref.trigger === "function") {
1198
+ _ref.trigger(changes);
1169
1199
  }
1170
1200
  }
1171
- changes[this.name] = value;
1172
- if ((_ref1 = this.object.changed) != null) {
1173
- if (typeof _ref1.trigger === "function") {
1174
- _ref1.trigger(changes);
1175
- }
1201
+ _ref1 = this.dependents;
1202
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1203
+ name = _ref1[_i];
1204
+ this.object[name + "_property"].trigger();
1176
1205
  }
1177
- return triggerGlobal(this.object, Object.keys(changes));
1206
+ return this._oldValue = newValue;
1178
1207
  }
1179
1208
  };
1180
1209
 
1181
- PropertyAccessor.prototype.bind = function(fun) {
1182
- return this.event.bind(fun);
1183
- };
1210
+ ["bind", "one", "resolve"].forEach(function(fn) {
1211
+ return PropertyAccessor.prototype[fn] = function() {
1212
+ var _ref;
1184
1213
 
1185
- PropertyAccessor.prototype.unbind = function(fun) {
1186
- return this.event.unbind(fun);
1187
- };
1214
+ return (_ref = this.event)[fn].apply(_ref, arguments);
1215
+ };
1216
+ });
1188
1217
 
1189
- PropertyAccessor.prototype.one = function(fun) {
1190
- return this.event.one(fun);
1218
+ PropertyAccessor.prototype.unbind = function(fn) {
1219
+ this.event.unbind(fn);
1220
+ return this.gc();
1191
1221
  };
1192
1222
 
1193
1223
  def(PropertyAccessor.prototype, "dependents", {
1194
1224
  get: function() {
1195
1225
  var deps, findDependencies,
1196
1226
  _this = this;
1227
+
1197
1228
  deps = [];
1198
1229
  findDependencies = function(name) {
1199
1230
  var property, _i, _len, _ref, _ref1, _results;
1231
+
1200
1232
  _ref = _this.object._s.properties;
1201
1233
  _results = [];
1202
1234
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -1228,26 +1260,48 @@ PropertyAccessor = (function() {
1228
1260
  };
1229
1261
 
1230
1262
  PropertyAccessor.prototype.hasChanged = function() {
1231
- var changed, oldValueName, value;
1232
- if (this.definition.changed === false) {
1233
- return false;
1234
- } else if (this.definition.changed) {
1235
- value = this.get();
1236
- oldValueName = "old_val_" + this.name;
1237
- changed = this.object._s.hasOwnProperty(oldValueName) ? this.definition.changed.call(this.object, this.object._s[oldValueName], value) : true;
1238
- this.object._s[oldValueName] = value;
1239
- return changed;
1263
+ var _ref;
1264
+
1265
+ if ((_ref = this.definition.changed) === true || _ref === false) {
1266
+ return this.definition.changed;
1240
1267
  } else {
1241
- return true;
1268
+ if ("_oldValue" in this) {
1269
+ if (this.definition.changed) {
1270
+ return this.definition.changed.call(this.object, this._oldValue, this.get());
1271
+ } else {
1272
+ return this._oldValue !== this.get();
1273
+ }
1274
+ } else {
1275
+ return true;
1276
+ }
1242
1277
  }
1243
1278
  };
1244
1279
 
1280
+ def(PropertyAccessor.prototype, "dependentProperties", {
1281
+ get: function() {
1282
+ var name;
1283
+
1284
+ return new Serenade.Collection((function() {
1285
+ var _i, _len, _ref, _results;
1286
+
1287
+ _ref = this.dependents;
1288
+ _results = [];
1289
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1290
+ name = _ref[_i];
1291
+ _results.push(this.object[name + "_property"]);
1292
+ }
1293
+ return _results;
1294
+ }).call(this));
1295
+ }
1296
+ });
1297
+
1245
1298
  return PropertyAccessor;
1246
1299
 
1247
- })();
1300
+ }).call(this);
1248
1301
 
1249
1302
  defineProperty = function(object, name, options) {
1250
- var definition;
1303
+ var accessorName, definition;
1304
+
1251
1305
  if (options == null) {
1252
1306
  options = {};
1253
1307
  }
@@ -1267,9 +1321,13 @@ defineProperty = function(object, name, options) {
1267
1321
  configurable: true,
1268
1322
  enumerable: "enumerable" in options ? options.enumerable : true
1269
1323
  });
1270
- def(object, name + "_property", {
1324
+ accessorName = name + "_property";
1325
+ def(object, accessorName, {
1271
1326
  get: function() {
1272
- return new PropertyAccessor(definition, this);
1327
+ if (!this._s.hasOwnProperty(accessorName)) {
1328
+ this._s[accessorName] = new PropertyAccessor(definition, this);
1329
+ }
1330
+ return this._s[accessorName];
1273
1331
  },
1274
1332
  configurable: true
1275
1333
  });
@@ -1292,7 +1350,6 @@ defineProperty = function(object, name, options) {
1292
1350
  idCounter = 1;
1293
1351
 
1294
1352
  Model = (function() {
1295
-
1296
1353
  Model.identityMap = true;
1297
1354
 
1298
1355
  Model.find = function(id) {
@@ -1303,12 +1360,13 @@ Model = (function() {
1303
1360
 
1304
1361
  Model.extend = function(ctor) {
1305
1362
  var New;
1306
- return New = (function(_super) {
1307
1363
 
1364
+ return New = (function(_super) {
1308
1365
  __extends(New, _super);
1309
1366
 
1310
1367
  function New() {
1311
1368
  var val;
1369
+
1312
1370
  val = New.__super__.constructor.apply(this, arguments);
1313
1371
  if (val) {
1314
1372
  return val;
@@ -1325,6 +1383,7 @@ Model = (function() {
1325
1383
 
1326
1384
  Model.property = function() {
1327
1385
  var name, names, options, _i, _j, _len, _results;
1386
+
1328
1387
  names = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), options = arguments[_i++];
1329
1388
  if (typeof options === "string") {
1330
1389
  names.push(options);
@@ -1345,10 +1404,12 @@ Model = (function() {
1345
1404
  Model.delegate = function() {
1346
1405
  var names, options, to, _i,
1347
1406
  _this = this;
1407
+
1348
1408
  names = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), options = arguments[_i++];
1349
1409
  to = options.to;
1350
1410
  return names.forEach(function(name) {
1351
1411
  var propName, propOptions;
1412
+
1352
1413
  propName = name;
1353
1414
  if (options.prefix === true) {
1354
1415
  propName = to + capitalize(name);
@@ -1364,11 +1425,18 @@ Model = (function() {
1364
1425
  dependsOn: options.dependsOn || ("" + to + "." + name),
1365
1426
  get: function() {
1366
1427
  var _ref;
1428
+
1367
1429
  return (_ref = this[to]) != null ? _ref[name] : void 0;
1368
1430
  },
1369
1431
  set: function(value) {
1370
1432
  var _ref;
1433
+
1371
1434
  return (_ref = this[to]) != null ? _ref[name] = value : void 0;
1435
+ },
1436
+ format: function() {
1437
+ if (this[to] != null) {
1438
+ return Serenade.format(this[to], name);
1439
+ }
1372
1440
  }
1373
1441
  });
1374
1442
  return _this.property(propName, propOptions);
@@ -1377,19 +1445,19 @@ Model = (function() {
1377
1445
 
1378
1446
  Model.collection = function(name, options) {
1379
1447
  var propOptions;
1448
+
1380
1449
  if (options == null) {
1381
1450
  options = {};
1382
1451
  }
1383
1452
  propOptions = merge(options, {
1453
+ changed: true,
1384
1454
  get: function() {
1385
- var valueName,
1386
- _this = this;
1455
+ var valueName;
1456
+
1387
1457
  valueName = "val_" + name;
1388
1458
  if (!this._s[valueName]) {
1389
1459
  this._s[valueName] = new Collection([]);
1390
- this._s[valueName].change.bind(function() {
1391
- return _this[name + "_property"].trigger();
1392
- });
1460
+ this._s[valueName].change.bind(this[name + "_property"].trigger);
1393
1461
  }
1394
1462
  return this._s[valueName];
1395
1463
  },
@@ -1408,12 +1476,14 @@ Model = (function() {
1408
1476
 
1409
1477
  Model.belongsTo = function(name, options) {
1410
1478
  var propOptions;
1479
+
1411
1480
  if (options == null) {
1412
1481
  options = {};
1413
1482
  }
1414
1483
  propOptions = merge(options, {
1415
1484
  set: function(model) {
1416
1485
  var previous, valueName;
1486
+
1417
1487
  valueName = "val_" + name;
1418
1488
  if (model && model.constructor === Object && options.as) {
1419
1489
  model = new (options.as())(model);
@@ -1432,6 +1502,7 @@ Model = (function() {
1432
1502
  return this.property(name + 'Id', {
1433
1503
  get: function() {
1434
1504
  var _ref;
1505
+
1435
1506
  return (_ref = this[name]) != null ? _ref.id : void 0;
1436
1507
  },
1437
1508
  set: function(id) {
@@ -1446,19 +1517,19 @@ Model = (function() {
1446
1517
 
1447
1518
  Model.hasMany = function(name, options) {
1448
1519
  var propOptions;
1520
+
1449
1521
  if (options == null) {
1450
1522
  options = {};
1451
1523
  }
1452
1524
  propOptions = merge(options, {
1525
+ changed: true,
1453
1526
  get: function() {
1454
- var valueName,
1455
- _this = this;
1527
+ var valueName;
1528
+
1456
1529
  valueName = "val_" + name;
1457
1530
  if (!this._s[valueName]) {
1458
1531
  this._s[valueName] = new AssociationCollection(this, options, []);
1459
- this._s[valueName].change.bind(function() {
1460
- return _this[name + "_property"].trigger();
1461
- });
1532
+ this._s[valueName].change.bind(this[name + "_property"].trigger);
1462
1533
  }
1463
1534
  return this._s[valueName];
1464
1535
  },
@@ -1475,8 +1546,10 @@ Model = (function() {
1475
1546
  },
1476
1547
  set: function(ids) {
1477
1548
  var id, objects;
1549
+
1478
1550
  objects = (function() {
1479
1551
  var _i, _len, _results;
1552
+
1480
1553
  _results = [];
1481
1554
  for (_i = 0, _len = ids.length; _i < _len; _i++) {
1482
1555
  id = ids[_i];
@@ -1499,6 +1572,7 @@ Model = (function() {
1499
1572
 
1500
1573
  Model.selection = function(name, options) {
1501
1574
  var propOptions;
1575
+
1502
1576
  if (options == null) {
1503
1577
  options = {};
1504
1578
  }
@@ -1544,6 +1618,7 @@ Model = (function() {
1544
1618
  Model.event("changed", {
1545
1619
  optimize: function(queue) {
1546
1620
  var item, result, _i, _len;
1621
+
1547
1622
  result = {};
1548
1623
  for (_i = 0, _len = queue.length; _i < _len; _i++) {
1549
1624
  item = queue[_i];
@@ -1555,6 +1630,7 @@ Model = (function() {
1555
1630
 
1556
1631
  function Model(attributes) {
1557
1632
  var fromCache;
1633
+
1558
1634
  if (this.constructor.identityMap && (attributes != null ? attributes.id : void 0)) {
1559
1635
  fromCache = Cache.get(this.constructor, attributes.id);
1560
1636
  if (fromCache) {
@@ -1569,6 +1645,7 @@ Model = (function() {
1569
1645
 
1570
1646
  Model.prototype.set = function(attributes) {
1571
1647
  var name, value, _results;
1648
+
1572
1649
  _results = [];
1573
1650
  for (name in attributes) {
1574
1651
  if (!__hasProp.call(attributes, name)) continue;
@@ -1587,6 +1664,7 @@ Model = (function() {
1587
1664
 
1588
1665
  Model.prototype.toJSON = function() {
1589
1666
  var key, property, serialized, value, _i, _len, _ref, _ref1;
1667
+
1590
1668
  serialized = {};
1591
1669
  _ref = this._s.properties;
1592
1670
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -1626,11 +1704,11 @@ COMMENT = /^\s*\/\/[^\n]*/;
1626
1704
  KEYWORDS = ["IF", "ELSE", "COLLECTION", "IN", "VIEW", "UNLESS"];
1627
1705
 
1628
1706
  Lexer = (function() {
1629
-
1630
1707
  function Lexer() {}
1631
1708
 
1632
1709
  Lexer.prototype.tokenize = function(code, opts) {
1633
1710
  var tag;
1711
+
1634
1712
  if (opts == null) {
1635
1713
  opts = {};
1636
1714
  }
@@ -1662,6 +1740,7 @@ Lexer = (function() {
1662
1740
 
1663
1741
  Lexer.prototype.commentToken = function() {
1664
1742
  var match;
1743
+
1665
1744
  if (match = COMMENT.exec(this.chunk)) {
1666
1745
  return match[0].length;
1667
1746
  } else {
@@ -1671,6 +1750,7 @@ Lexer = (function() {
1671
1750
 
1672
1751
  Lexer.prototype.whitespaceToken = function() {
1673
1752
  var match;
1753
+
1674
1754
  if (match = WHITESPACE.exec(this.chunk)) {
1675
1755
  this.token('WHITESPACE', match[0].length);
1676
1756
  return match[0].length;
@@ -1685,6 +1765,7 @@ Lexer = (function() {
1685
1765
 
1686
1766
  Lexer.prototype.identifierToken = function() {
1687
1767
  var match, name;
1768
+
1688
1769
  if (match = IDENTIFIER.exec(this.chunk)) {
1689
1770
  name = match[0].toUpperCase();
1690
1771
  if (name === "ELSE" && this.last(this.tokens, 2)[0] === "TERMINATOR") {
@@ -1703,6 +1784,7 @@ Lexer = (function() {
1703
1784
 
1704
1785
  Lexer.prototype.stringToken = function() {
1705
1786
  var match;
1787
+
1706
1788
  if (match = STRING.exec(this.chunk)) {
1707
1789
  this.token('STRING_LITERAL', match[1]);
1708
1790
  return match[0].length;
@@ -1713,6 +1795,7 @@ Lexer = (function() {
1713
1795
 
1714
1796
  Lexer.prototype.lineToken = function() {
1715
1797
  var diff, indent, match, prev, size;
1798
+
1716
1799
  if (!(match = MULTI_DENT.exec(this.chunk))) {
1717
1800
  return 0;
1718
1801
  }
@@ -1741,6 +1824,7 @@ Lexer = (function() {
1741
1824
 
1742
1825
  Lexer.prototype.literalToken = function() {
1743
1826
  var match;
1827
+
1744
1828
  if (match = LITERAL.exec(this.chunk)) {
1745
1829
  this.token(match[0]);
1746
1830
  return 1;
@@ -1757,22 +1841,26 @@ Lexer = (function() {
1757
1841
 
1758
1842
  Lexer.prototype.tag = function(index, tag) {
1759
1843
  var tok;
1844
+
1760
1845
  return (tok = this.last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]);
1761
1846
  };
1762
1847
 
1763
1848
  Lexer.prototype.value = function(index, val) {
1764
1849
  var tok;
1850
+
1765
1851
  return (tok = this.last(this.tokens, index)) && (val ? tok[1] = val : tok[1]);
1766
1852
  };
1767
1853
 
1768
1854
  Lexer.prototype.error = function(message) {
1769
1855
  var chunk;
1856
+
1770
1857
  chunk = this.code.slice(Math.max(0, this.i - 10), Math.min(this.code.length, this.i + 10));
1771
1858
  throw SyntaxError("" + message + " on line " + (this.line + 1) + " near " + (JSON.stringify(chunk)));
1772
1859
  };
1773
1860
 
1774
1861
  Lexer.prototype.count = function(string, substr) {
1775
1862
  var num, pos;
1863
+
1776
1864
  num = pos = 0;
1777
1865
  if (!substr.length) {
1778
1866
  return 1 / 0;
@@ -1792,16 +1880,20 @@ Lexer = (function() {
1792
1880
  })();
1793
1881
 
1794
1882
  Node = (function() {
1883
+ defineEvent(Node.prototype, "load", {
1884
+ async: false
1885
+ });
1795
1886
 
1796
- defineEvent(Node.prototype, "load");
1797
-
1798
- defineEvent(Node.prototype, "unload");
1887
+ defineEvent(Node.prototype, "unload", {
1888
+ async: false
1889
+ });
1799
1890
 
1800
1891
  function Node(ast, element) {
1801
1892
  this.ast = ast;
1802
1893
  this.element = element;
1803
1894
  this.children = new Collection([]);
1804
1895
  this.boundClasses = new Collection([]);
1896
+ this.boundEvents = new Collection([]);
1805
1897
  }
1806
1898
 
1807
1899
  Node.prototype.append = function(inside) {
@@ -1814,7 +1906,8 @@ Node = (function() {
1814
1906
 
1815
1907
  Node.prototype.remove = function() {
1816
1908
  var _ref;
1817
- this.unbindEvents();
1909
+
1910
+ this.detach();
1818
1911
  return (_ref = this.element.parentNode) != null ? _ref.removeChild(this.element) : void 0;
1819
1912
  };
1820
1913
 
@@ -1831,7 +1924,6 @@ Node = (function() {
1831
1924
 
1832
1925
  Node.prototype.bindEvent = function(event, fun) {
1833
1926
  if (event) {
1834
- this.boundEvents || (this.boundEvents = []);
1835
1927
  this.boundEvents.push({
1836
1928
  event: event,
1837
1929
  fun: fun
@@ -1840,13 +1932,21 @@ Node = (function() {
1840
1932
  }
1841
1933
  };
1842
1934
 
1843
- Node.prototype.unbindEvents = function() {
1935
+ Node.prototype.unbindEvent = function(event, fun) {
1936
+ if (event) {
1937
+ this.boundEvents["delete"](fun);
1938
+ return event.unbind(fun);
1939
+ }
1940
+ };
1941
+
1942
+ Node.prototype.detach = function() {
1844
1943
  var event, fun, node, _i, _j, _len, _len1, _ref, _ref1, _ref2, _results;
1944
+
1845
1945
  this.unload.trigger();
1846
1946
  _ref = this.nodes();
1847
1947
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1848
1948
  node = _ref[_i];
1849
- node.unbindEvents();
1949
+ node.detach();
1850
1950
  }
1851
1951
  if (this.boundEvents) {
1852
1952
  _ref1 = this.boundEvents;
@@ -1861,6 +1961,7 @@ Node = (function() {
1861
1961
 
1862
1962
  Node.prototype.updateClass = function() {
1863
1963
  var classes;
1964
+
1864
1965
  classes = this.ast.classes;
1865
1966
  if (this.attributeClasses) {
1866
1967
  classes = classes.concat(this.attributeClasses);
@@ -1881,17 +1982,18 @@ Node = (function() {
1881
1982
  })();
1882
1983
 
1883
1984
  DynamicNode = (function(_super) {
1884
-
1885
1985
  __extends(DynamicNode, _super);
1886
1986
 
1887
1987
  function DynamicNode(ast) {
1888
1988
  this.ast = ast;
1889
1989
  this.anchor = Serenade.document.createTextNode('');
1890
1990
  this.nodeSets = new Collection([]);
1991
+ this.boundEvents = new Collection([]);
1891
1992
  }
1892
1993
 
1893
1994
  DynamicNode.prototype.nodes = function() {
1894
1995
  var node, nodes, set, _i, _j, _len, _len1, _ref;
1996
+
1895
1997
  nodes = [];
1896
1998
  _ref = this.nodeSets;
1897
1999
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -1906,6 +2008,7 @@ DynamicNode = (function(_super) {
1906
2008
 
1907
2009
  DynamicNode.prototype.rebuild = function() {
1908
2010
  var last, node, _i, _len, _ref, _results;
2011
+
1909
2012
  if (this.anchor.parentNode) {
1910
2013
  last = this.anchor;
1911
2014
  _ref = this.nodes();
@@ -1921,9 +2024,11 @@ DynamicNode = (function(_super) {
1921
2024
 
1922
2025
  DynamicNode.prototype.replace = function(sets) {
1923
2026
  var set;
2027
+
1924
2028
  this.clear();
1925
2029
  this.nodeSets.update((function() {
1926
2030
  var _i, _len, _results;
2031
+
1927
2032
  _results = [];
1928
2033
  for (_i = 0, _len = sets.length; _i < _len; _i++) {
1929
2034
  set = sets[_i];
@@ -1940,6 +2045,7 @@ DynamicNode = (function(_super) {
1940
2045
 
1941
2046
  DynamicNode.prototype.deleteNodeSet = function(index) {
1942
2047
  var node, _i, _len, _ref;
2048
+
1943
2049
  _ref = this.nodeSets[index];
1944
2050
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1945
2051
  node = _ref[_i];
@@ -1950,6 +2056,7 @@ DynamicNode = (function(_super) {
1950
2056
 
1951
2057
  DynamicNode.prototype.insertNodeSet = function(index, nodes) {
1952
2058
  var last, node, _i, _len, _ref, _ref1;
2059
+
1953
2060
  last = ((_ref = this.nodeSets[index - 1]) != null ? (_ref1 = _ref.last) != null ? _ref1.lastElement : void 0 : void 0) || this.anchor;
1954
2061
  for (_i = 0, _len = nodes.length; _i < _len; _i++) {
1955
2062
  node = nodes[_i];
@@ -1961,6 +2068,7 @@ DynamicNode = (function(_super) {
1961
2068
 
1962
2069
  DynamicNode.prototype.clear = function() {
1963
2070
  var node, _i, _len, _ref;
2071
+
1964
2072
  _ref = this.nodes();
1965
2073
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1966
2074
  node = _ref[_i];
@@ -1970,7 +2078,7 @@ DynamicNode = (function(_super) {
1970
2078
  };
1971
2079
 
1972
2080
  DynamicNode.prototype.remove = function() {
1973
- this.unbindEvents();
2081
+ this.detach();
1974
2082
  this.clear();
1975
2083
  return this.anchor.parentNode.removeChild(this.anchor);
1976
2084
  };
@@ -1989,6 +2097,7 @@ DynamicNode = (function(_super) {
1989
2097
  configurable: true,
1990
2098
  get: function() {
1991
2099
  var _ref, _ref1;
2100
+
1992
2101
  return ((_ref = this.nodeSets.last) != null ? (_ref1 = _ref.last) != null ? _ref1.lastElement : void 0 : void 0) || this.anchor;
1993
2102
  }
1994
2103
  });
@@ -2010,6 +2119,7 @@ getValue = function(ast, model) {
2010
2119
  Property = {
2011
2120
  style: function(ast, node, model, controller) {
2012
2121
  var update;
2122
+
2013
2123
  update = function() {
2014
2124
  return assignUnlessEqual(node.element.style, ast.name, getValue(ast, model));
2015
2125
  };
@@ -2028,6 +2138,7 @@ Property = {
2028
2138
  },
2029
2139
  "class": function(ast, node, model, controller) {
2030
2140
  var update;
2141
+
2031
2142
  update = function() {
2032
2143
  if (model[ast.value]) {
2033
2144
  if (!node.boundClasses.includes(ast.name)) {
@@ -2043,6 +2154,7 @@ Property = {
2043
2154
  },
2044
2155
  binding: function(ast, node, model, controller) {
2045
2156
  var domUpdated, element, handler, modelUpdated, _ref;
2157
+
2046
2158
  element = node.element;
2047
2159
  ((_ref = node.ast.name) === "input" || _ref === "textarea" || _ref === "select") || (function() {
2048
2160
  throw SyntaxError("invalid node type " + node.ast.name + " for two way binding");
@@ -2055,6 +2167,7 @@ Property = {
2055
2167
  };
2056
2168
  modelUpdated = function() {
2057
2169
  var val;
2170
+
2058
2171
  val = model[ast.value];
2059
2172
  if (element.type === "checkbox") {
2060
2173
  return element.checked = !!val;
@@ -2087,12 +2200,14 @@ Property = {
2087
2200
  },
2088
2201
  attribute: function(ast, node, model, controller) {
2089
2202
  var element, update;
2203
+
2090
2204
  if (ast.name === "binding") {
2091
2205
  return Property.binding(ast, node, model, controller);
2092
2206
  }
2093
2207
  element = node.element;
2094
2208
  update = function() {
2095
2209
  var value;
2210
+
2096
2211
  value = getValue(ast, model);
2097
2212
  if (ast.name === 'value') {
2098
2213
  return assignUnlessEqual(element, "value", value || '');
@@ -2121,6 +2236,7 @@ Property = {
2121
2236
  },
2122
2237
  on: function(ast, node, model, controller) {
2123
2238
  var _ref;
2239
+
2124
2240
  if ((_ref = ast.name) === "load" || _ref === "unload") {
2125
2241
  return node[ast.name].bind(function() {
2126
2242
  return controller[ast.value](node.element, model);
@@ -2134,6 +2250,7 @@ Property = {
2134
2250
  Compile = {
2135
2251
  element: function(ast, model, controller) {
2136
2252
  var action, child, element, node, property, _i, _j, _len, _len1, _ref, _ref1, _ref2;
2253
+
2137
2254
  element = Serenade.document.createElement(ast.name);
2138
2255
  node = new Node(ast, element);
2139
2256
  if (ast.id) {
@@ -2163,6 +2280,7 @@ Compile = {
2163
2280
  },
2164
2281
  view: function(ast, model, parent) {
2165
2282
  var controller, dynamic, skipCallback;
2283
+
2166
2284
  controller = Serenade.controllerFor(ast.argument);
2167
2285
  if (!controller) {
2168
2286
  skipCallback = true;
@@ -2174,9 +2292,11 @@ Compile = {
2174
2292
  },
2175
2293
  helper: function(ast, model, controller) {
2176
2294
  var argument, context, dynamic, helperFunction, render, update, _i, _len, _ref;
2295
+
2177
2296
  dynamic = new DynamicNode(ast);
2178
2297
  render = function(model, controller) {
2179
2298
  var child, children, fragment, _i, _len;
2299
+
2180
2300
  if (model == null) {
2181
2301
  model = model;
2182
2302
  }
@@ -2201,6 +2321,7 @@ Compile = {
2201
2321
  };
2202
2322
  update = function() {
2203
2323
  var args, element, nodes;
2324
+
2204
2325
  args = ast["arguments"].map(function(a) {
2205
2326
  if (a.bound) {
2206
2327
  return model[a.value];
@@ -2210,6 +2331,7 @@ Compile = {
2210
2331
  });
2211
2332
  nodes = (function() {
2212
2333
  var _i, _len, _ref, _results;
2334
+
2213
2335
  _ref = normalize(helperFunction.apply(context, args));
2214
2336
  _results = [];
2215
2337
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -2232,8 +2354,10 @@ Compile = {
2232
2354
  },
2233
2355
  text: function(ast, model, controller) {
2234
2356
  var getText, node, textNode;
2357
+
2235
2358
  getText = function() {
2236
2359
  var value;
2360
+
2237
2361
  value = getValue(ast, model);
2238
2362
  if (value === 0) {
2239
2363
  value = "0";
@@ -2250,59 +2374,33 @@ Compile = {
2250
2374
  return node;
2251
2375
  },
2252
2376
  collection: function(ast, model, controller) {
2253
- var collection, compileItem, dynamic, update,
2254
- _this = this;
2377
+ var collection, compileItem, dynamic, update, updateCollection;
2378
+
2379
+ collection = model[ast.argument];
2255
2380
  compileItem = function(item) {
2256
2381
  return compile(ast.children, item, controller);
2257
2382
  };
2258
- update = function(dynamic, collection) {
2259
- var item;
2260
- return dynamic.replace((function() {
2261
- var _i, _len, _results;
2262
- _results = [];
2263
- for (_i = 0, _len = collection.length; _i < _len; _i++) {
2264
- item = collection[_i];
2265
- _results.push(compileItem(item));
2266
- }
2267
- return _results;
2268
- })());
2383
+ updateCollection = function(before, after) {
2384
+ return update(dynamic, after);
2269
2385
  };
2270
- dynamic = this.bound(ast, model, controller, update);
2271
- collection = model[ast.argument];
2272
- dynamic.bindEvent(collection['change_set'], function() {
2273
- var item;
2274
- return dynamic.replace((function() {
2275
- var _i, _len, _results;
2276
- _results = [];
2277
- for (_i = 0, _len = collection.length; _i < _len; _i++) {
2278
- item = collection[_i];
2279
- _results.push(compileItem(item));
2280
- }
2281
- return _results;
2282
- })());
2283
- });
2284
- dynamic.bindEvent(collection['change_update'], function() {
2386
+ update = function(dynamic, newCollection) {
2285
2387
  var item;
2286
- return dynamic.replace((function() {
2388
+
2389
+ dynamic.unbindEvent(collection.change, updateCollection);
2390
+ dynamic.replace((function() {
2287
2391
  var _i, _len, _results;
2392
+
2288
2393
  _results = [];
2289
- for (_i = 0, _len = collection.length; _i < _len; _i++) {
2290
- item = collection[_i];
2394
+ for (_i = 0, _len = newCollection.length; _i < _len; _i++) {
2395
+ item = newCollection[_i];
2291
2396
  _results.push(compileItem(item));
2292
2397
  }
2293
2398
  return _results;
2294
2399
  })());
2295
- });
2296
- dynamic.bindEvent(collection['change_add'], function(item) {
2297
- return dynamic.appendNodeSet(compileItem(item));
2298
- });
2299
- dynamic.bindEvent(collection['change_insert'], function(index, item) {
2300
- return dynamic.insertNodeSet(index, compileItem(item));
2301
- });
2302
- dynamic.bindEvent(collection['change_delete'], function(index) {
2303
- return dynamic.deleteNodeSet(index);
2304
- });
2305
- return dynamic;
2400
+ dynamic.bindEvent(newCollection.change, updateCollection);
2401
+ return collection = newCollection;
2402
+ };
2403
+ return dynamic = this.bound(ast, model, controller, update);
2306
2404
  },
2307
2405
  "in": function(ast, model, controller) {
2308
2406
  return this.bound(ast, model, controller, function(dynamic, value) {
@@ -2327,6 +2425,7 @@ Compile = {
2327
2425
  unless: function(ast, model, controller) {
2328
2426
  return this.bound(ast, model, controller, function(dynamic, value) {
2329
2427
  var nodes;
2428
+
2330
2429
  if (value) {
2331
2430
  return dynamic.clear();
2332
2431
  } else {
@@ -2337,10 +2436,12 @@ Compile = {
2337
2436
  },
2338
2437
  bound: function(ast, model, controller, callback) {
2339
2438
  var dynamic, lastValue, update;
2439
+
2340
2440
  dynamic = new DynamicNode(ast);
2341
2441
  lastValue = {};
2342
2442
  update = function() {
2343
2443
  var value;
2444
+
2344
2445
  value = model[ast.argument];
2345
2446
  if (value !== lastValue) {
2346
2447
  callback(dynamic, value);
@@ -2355,11 +2456,13 @@ Compile = {
2355
2456
 
2356
2457
  normalize = function(val) {
2357
2458
  var reduction;
2459
+
2358
2460
  if (!val) {
2359
2461
  return [];
2360
2462
  }
2361
2463
  reduction = function(aggregate, element) {
2362
2464
  var child, div, _i, _j, _len, _len1, _ref, _ref1;
2465
+
2363
2466
  if (typeof element === "string") {
2364
2467
  div = Serenade.document.createElement("div");
2365
2468
  div.innerHTML = element;
@@ -2384,6 +2487,7 @@ normalize = function(val) {
2384
2487
 
2385
2488
  compile = function(asts, model, controller) {
2386
2489
  var ast, _i, _len, _results;
2490
+
2387
2491
  _results = [];
2388
2492
  for (_i = 0, _len = asts.length; _i < _len; _i++) {
2389
2493
  ast = asts[_i];
@@ -2395,6 +2499,7 @@ compile = function(asts, model, controller) {
2395
2499
  parser.lexer = {
2396
2500
  lex: function() {
2397
2501
  var tag, _ref;
2502
+
2398
2503
  _ref = this.tokens[this.pos++] || [''], tag = _ref[0], this.yytext = _ref[1], this.yylineno = _ref[2];
2399
2504
  return tag;
2400
2505
  },
@@ -2408,13 +2513,13 @@ parser.lexer = {
2408
2513
  };
2409
2514
 
2410
2515
  CompiledView = (function() {
2411
-
2412
2516
  function CompiledView(nodes) {
2413
2517
  this.nodes = nodes;
2414
2518
  }
2415
2519
 
2416
2520
  CompiledView.prototype.remove = function() {
2417
2521
  var node, _i, _len, _ref, _results;
2522
+
2418
2523
  _ref = this.nodes;
2419
2524
  _results = [];
2420
2525
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -2428,6 +2533,7 @@ CompiledView = (function() {
2428
2533
  enumerable: true,
2429
2534
  get: function() {
2430
2535
  var fragment, node, _i, _len, _ref;
2536
+
2431
2537
  fragment = Serenade.document.createDocumentFragment();
2432
2538
  _ref = this.nodes;
2433
2539
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -2443,17 +2549,19 @@ CompiledView = (function() {
2443
2549
  })();
2444
2550
 
2445
2551
  View = (function() {
2446
-
2447
2552
  function View(name, view) {
2448
2553
  this.name = name;
2449
2554
  this.view = view;
2450
2555
  }
2451
2556
 
2452
2557
  View.prototype.parse = function() {
2558
+ var e;
2559
+
2453
2560
  if (typeof this.view === 'string') {
2454
2561
  try {
2455
2562
  return this.view = parser.parse(new Lexer().tokenize(this.view));
2456
- } catch (e) {
2563
+ } catch (_error) {
2564
+ e = _error;
2457
2565
  if (this.name) {
2458
2566
  e.message = "In view '" + this.name + "': " + e.message;
2459
2567
  }
@@ -2466,12 +2574,14 @@ View = (function() {
2466
2574
 
2467
2575
  View.prototype.render = function() {
2468
2576
  var args;
2577
+
2469
2578
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
2470
2579
  return this.compile.apply(this, args).fragment;
2471
2580
  };
2472
2581
 
2473
2582
  View.prototype.nodes = function(model, controller, parent, skipCallback) {
2474
2583
  var nodes;
2584
+
2475
2585
  if (this.name) {
2476
2586
  controller || (controller = Serenade.controllerFor(this.name, model));
2477
2587
  }
@@ -2492,6 +2602,7 @@ View = (function() {
2492
2602
 
2493
2603
  View.prototype.compile = function() {
2494
2604
  var args;
2605
+
2495
2606
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
2496
2607
  return new CompiledView(this.nodes.apply(this, args));
2497
2608
  };
@@ -2502,6 +2613,7 @@ View = (function() {
2502
2613
 
2503
2614
  Serenade = function(wrapped) {
2504
2615
  var key, object, value;
2616
+
2505
2617
  object = Object.create(wrapped);
2506
2618
  for (key in wrapped) {
2507
2619
  value = wrapped[key];
@@ -2513,7 +2625,7 @@ Serenade = function(wrapped) {
2513
2625
  };
2514
2626
 
2515
2627
  extend(Serenade, {
2516
- VERSION: '0.4.1',
2628
+ VERSION: '0.4.2',
2517
2629
  _views: {},
2518
2630
  _controllers: {},
2519
2631
  document: typeof window !== "undefined" && window !== null ? window.document : void 0,
@@ -2531,6 +2643,9 @@ extend(Serenade, {
2531
2643
  render: function(name, model, controller, parent, skipCallback) {
2532
2644
  return this._views[name].render(model, controller, parent, skipCallback);
2533
2645
  },
2646
+ compile: function(name, model, controller, parent, skipCallback) {
2647
+ return this._views[name].compile(model, controller, parent, skipCallback);
2648
+ },
2534
2649
  controller: function(name, klass) {
2535
2650
  return this._controllers[name] = klass;
2536
2651
  },
@@ -2541,14 +2656,7 @@ extend(Serenade, {
2541
2656
  return Cache._identityMap = {};
2542
2657
  },
2543
2658
  clearCache: function() {
2544
- var key, value, _i, _len, _results;
2545
- Serenade.clearIdentityMap();
2546
- _results = [];
2547
- for (key = _i = 0, _len = globalDependencies.length; _i < _len; key = ++_i) {
2548
- value = globalDependencies[key];
2549
- _results.push(delete globalDependencies[key]);
2550
- }
2551
- return _results;
2659
+ return Serenade.clearIdentityMap();
2552
2660
  },
2553
2661
  unregisterAll: function() {
2554
2662
  Serenade._views = {};