serenade 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = {};