Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Deprecate mix/merge and merge the logic into set, use 5 arg signature…

… everywhere

this.set(undefined, value) now merges object into this
this.watch(undefined, callback) now observes all values in an object
Add _join callback to be called instead of _hash when watch or set is called without a key
Objects are now live-merged by default
Remove all the inline docs. They were useless and badly written, unfortunately.
Fix Cookies bug
Move dynamic properties assignment to Struct.
  • Loading branch information...
commit eecc2de6b64ba0786effc4f4d270faa8dbc5dad1 1 parent c6ed8d2
authored January 19, 2013
28  Source/DOM/Element.js
@@ -87,7 +87,7 @@ LSD.Element = new LSD.Struct({
87 87
         if (typeof value == 'string')
88 88
           value = typeof roles[value] == 'undefined' ? roles.get(value) : roles[value];
89 89
         if (typeof old == 'string') old = roles[old] || undefined;
90  
-        this.mix(undefined, value, old, meta, false, true);
  90
+        this.mix(undefined, value, old, meta, 'under');
91 91
       }
92 92
     },
93 93
     chunked: true
@@ -397,9 +397,10 @@ LSD.Element = new LSD.Struct({
397 397
         if (classes && classes._name != element.className)
398 398
           element.className = classes._name;
399 399
       }
400  
-      this.set('element', element)
  400
+      this._set('element', element)
401 401
     }
402  
-    this.mix('childNodes.built', value, old, meta);
  402
+    if (value || old)
  403
+      this._set('childNodes.built', value, old, meta);
403 404
   },
404 405
 /*
405 406
   Javascript DOM is known for its unfriendly implementation of accessibility
@@ -427,8 +428,8 @@ LSD.Element = new LSD.Struct({
427 428
   environments, in focus-driven navigation and lots of other applications.
428 429
 */
429 430
   focused: function(value, old, meta) {
430  
-    if (meta === this) return;
431  
-    this.mix('parentNode.focused', value, old, meta || this);
  431
+    if (meta === this || (!value && !old)) return;
  432
+    this.set('parentNode.focused', value, old, meta || this);
432 433
     if (value && !meta && this.ownerDocument)
433 434
       this.ownerDocument.change('activeElement', this, undefined, false);
434 435
   },
@@ -558,7 +559,7 @@ LSD.Element = new LSD.Struct({
558 559
     if (meta === 'variables') return;
559 560
     for (var child, i = 0, children = this.childNodes; child = children[i++];)
560 561
       if (child.nodeType != 3 && (!child.fragment || child.fragment == this.fragment))
561  
-        child.mix('variables', value, old, this, true);
  562
+        child.mix('variables', value, old, this);
562 563
   },
563 564
   multiple: function(value, old) {
564 565
     if (value) {
@@ -641,8 +642,7 @@ LSD.Element = new LSD.Struct({
641 642
   element.
642 643
 */
643 644
   microdata: function(value, old, meta) {
644  
-    if ((value && !this.variables) || (old && old == this.variables))
645  
-      this.mix('variables', value, old, meta, true);
  645
+    this.mix('variables', value, old, meta);
646 646
   },
647 647
   itemscope: function(value, old, meta) {
648 648
     value = value && this._construct('microdata') || undefined;
@@ -651,9 +651,9 @@ LSD.Element = new LSD.Struct({
651 651
   },
652 652
   itemprop: function(value, old, meta) {
653 653
     if (value) 
654  
-      this.mix('parentNode.microdata.' + value, this, undefined, meta, true);
  654
+      this._set('parentNode.microdata.' + value, this, undefined, meta);
655 655
     if (old)
656  
-      this.mix('parentNode.microdata.' + old, undefined, this, meta, true);
  656
+      this._set('parentNode.microdata.' + old, undefined, this, meta);
657 657
   },
658 658
   itemtype: function(value, old) {
659 659
 
@@ -944,7 +944,7 @@ LSD.Document.prototype.mix('states', {
944 944
   invoked:   ['invoke',     'revoke']
945 945
 })
946 946
 
947  
-LSD.Element.prototype.set('built', false);
948  
-LSD.Element.prototype.set('hidden', false);
949  
-LSD.Element.prototype.set('disabled', false);
950  
-LSD.Element.prototype.set('focused', false);
  947
+LSD.Element.prototype._set('built', false);
  948
+LSD.Element.prototype._set('hidden', false);
  949
+LSD.Element.prototype._set('disabled', false);
  950
+LSD.Element.prototype._set('focused', false);
2  Source/DOM/Node.js
@@ -67,7 +67,7 @@ LSD.Node.prototype.setVariables = function(value, old, meta) {
67 67
   this.mix('variables', 
68 68
            value && (fragment && fragment != value.fragment && fragment.variables || value.get('variables', true, 'variables')), 
69 69
            old && (fragment && fragment != old.fragment && fragment || old).variables,
70  
-           'variables', true);
  70
+           'variables');
71 71
 };
72 72
 LSD.Node.prototype.$family = function() {
73 73
   return 'widget';
6  Source/DOM/NodeList.js
@@ -30,7 +30,7 @@ LSD.NodeList = function() {
30 30
     return collection;
31 31
   } else {
32 32
     if (this._sortBy)
33  
-      this.watch(this._observeIndex);
  33
+      this.watch(undefined, this._observeIndex);
34 34
     return LSD.Array.apply(this, arguments);
35 35
   }
36 36
 }
@@ -73,8 +73,8 @@ LSD.NodeList.prototype.fn = function(collection, key, value, old, meta) {
73 73
 };
74 74
 
75 75
 LSD.Relation = new LSD.Struct({
76  
-  match: '_owner.matches.set manager',
77  
-  proxy: '_owner.proxies.set manager'
  76
+  match: '_owner.matches manager',
  77
+  proxy: '_owner.proxies manager'
78 78
 }, 'NodeList');
79 79
 LSD.Relation.prototype._aggregate = true;
80 80
 LSD.Relation.prototype._object = false;
4  Source/DOM/Textnode.js
@@ -24,8 +24,6 @@ LSD.Textnode = LSD.Struct({
24 24
   textContent: function(value, old, meta) {
25 25
     if (typeof value != 'undefined') {
26 26
       value = String(value)
27  
-      if ((meta && meta._calculated) || value === '')
28  
-        debugger
29 27
       if (!meta || !(meta.push || meta._calculated)) {
30 28
         for (var previous = -1, start, end, bits, substr; (start = value.indexOf('${', previous + 1)) > -1;) {
31 29
           if ((end = value.indexOf('}', start)) == -1) continue;
@@ -78,7 +76,7 @@ LSD.Textnode.prototype.__initialize = function() {
78 76
             this.fragment = arg;
79 77
             break;
80 78
           default:
81  
-            this.mix(undefined, arg);
  79
+            this._mix(undefined, arg);
82 80
         }
83 81
     }
84 82
   }
1  Source/Properties/Attributes.js
@@ -45,7 +45,6 @@ LSD.Properties.Attributes.prototype.__cast = function(key, value, old, meta) {
45 45
   if (((!meta || meta !== 'states') && ns.states[key]) || owner._properties[key])
46 46
     owner.set(key, value, old, 'attributes');
47 47
   if (key.substr(0, 5) == 'data-') {
48  
-    if (value === undefined) debugger
49 48
     owner.mix('variables.' + key.substring(5), value, old, meta);
50 49
   }
51 50
   if (owner.matches) {
10  Source/Properties/Matches.js
@@ -109,7 +109,7 @@ LSD.Properties.Matches.prototype.__cast = function(key, value, old, meta, extra,
109 109
     if (vdef) {
110 110
       hash.push([key, value, stateful]);
111 111
       if (this._results) {
112  
-        var group = this._hash(key, null, null, null, this._results);
  112
+        var group = this.__hash(key, null, null, null, null, this._results);
113 113
         for (var i = 0, widget; widget = group[i++];) {
114 114
           if (!stateful) {
115 115
             if (typeof value == 'function') value(widget);
@@ -124,7 +124,7 @@ LSD.Properties.Matches.prototype.__cast = function(key, value, old, meta, extra,
124 124
       if (hash) for (var i = hash.length, fn; i--;) {
125 125
         if ((fn = hash[i]) && (fn = fn[1]) && (fn === old || fn.callback === old)) {
126 126
           if (this._results) {
127  
-            var group = this._hash(key, null, null, null, this._results);
  127
+            var group = this.__hash(key, null, null, null, null, this._results);
128 128
             for (var j = 0, result; result = group[j++];) {
129 129
               if (!stateful) {
130 130
                 if (typeof fn == 'function') fn(undefined, old);
@@ -172,7 +172,7 @@ LSD.Properties.Matches.prototype._advancer = function(call, value, old) {
172 172
   the array of expression from left to right and handles each expression separately
173 173
   storing a callback that advances the selector to the next expression. 
174 174
 */
175  
-LSD.Properties.Matches.prototype._hash = function(key, value, old, meta, storage) {
  175
+LSD.Properties.Matches.prototype.__hash = function(key, value, old, meta, mode, storage) {
176 176
   if (typeof key == 'string') {
177 177
     if (this._nonenumerable[key]) return;
178 178
     var parsed = this._parsed;
@@ -275,5 +275,5 @@ LSD.Properties.Matches.prototype.remove = function(combinator, tag, value, wildc
275 275
 }
276 276
 LSD.Properties.Matches.prototype._types = {pseudos: 1, classes: 1, attributes: 1};
277 277
 LSD.Properties.Matches.prototype._parsed = {};
278  
-LSD.Properties.Matches.prototype._composite = false;
279  
-LSD.Properties.Matches.prototype._watchable = null;
  278
+LSD.Properties.Matches.prototype._parser = Slick.parse;
  279
+LSD.Properties.Matches.prototype._composite = true;
4  Source/Properties/Microdata.js
@@ -25,6 +25,7 @@ LSD.Properties.Microdata.prototype.__cast = function(key, value, old, meta) {
25 25
   if (meta !== 'microdata' && meta !== 'textContent') {
26 26
     if (!this._elements) return;
27 27
     var element = this._elements[key];
  28
+    if (element == null) return;
28 29
     var storage = this._values;
29 30
     if (!storage) storage = this._values = {};
30 31
     if (odef && old !== storage[key]) odef = old = undefined;
@@ -35,6 +36,8 @@ LSD.Properties.Microdata.prototype.__cast = function(key, value, old, meta) {
35 36
   }
36 37
 }
37 38
 LSD.Properties.Microdata.prototype.___hash = function(key, value, old, meta) {
  39
+  if (this._nonenumerable[key])
  40
+    return;
38 41
   if (value && value.lsd) {
39 42
     var storage = this._elements || (this._elements = {});
40 43
     var group = storage[key];
@@ -59,6 +62,7 @@ LSD.Properties.Microdata.prototype.___hash = function(key, value, old, meta) {
59 62
     return true;
60 63
 }
61 64
 LSD.Properties.Microdata.prototype._shared = true;
  65
+LSD.Properties.Microdata.prototype._owning = false;
62 66
 LSD.Properties.Microdata.prototype._trigger = 'lsd';
63 67
 LSD.Properties.Microdata.prototype._nonenumerable = LSD.Struct.implement(LSD.Properties.Microdata.prototype._nonenumerable, {
64 68
   _values: true,
4  Source/Properties/Proxies.js
@@ -34,10 +34,10 @@ LSD.Properties.Proxies.prototype._hash = function(key) {
34 34
     // return this.content || this.content 
35 35
     case '_owner':
36 36
       return;
37  
-    default:  
  37
+    default:
38 38
       if (typeof key == 'string') {
39 39
         var object = this._selectors || (this._selectors = {});
40  
-      } else if (key && key.exec) {
  40
+      } else if (key.exec) {
41 41
         var object = this._wildcards || (this._wildcards = {});
42 42
         var regexes = this._regexes || (this._regexes = {});
43 43
         if (!regexes[key]) regexes[key] = key;
2  Source/Properties/Request.js
@@ -119,7 +119,7 @@ LSD.Request.prototype.isSuccess = function() {
119 119
   return this.status > 199 && this.status < 300;
120 120
 };
121 121
 LSD.Request.prototype.___hash = function(key) {
122  
-  debugger
  122
+  if (typeof key != 'string') return;
123 123
   var first = key.charAt(0);
124 124
   if (first != '_' && first === first.toUpperCase())
125 125
     return 'headers.' + key;
66  Source/Script/Script.js
@@ -51,9 +51,8 @@ provides:
51 51
 LSD.Script = function(input, scope, output) {
52 52
   var regex = this._regexp, type = typeof input;
53 53
   if (regex) {
54  
-    if (scope) {
  54
+    if (scope)
55 55
       this.scope = scope;
56  
-    }
57 56
     if (output)
58 57
       if (output.nodeType == 9) this.document = output;
59 58
       else this.output = output;
@@ -93,7 +92,7 @@ LSD.Script = function(input, scope, output) {
93 92
       if (!this.proto && this.locals) this.findLocals(this.locals);
94 93
       if (this.locals) {
95 94
         this.variables = new LSD.Journal;
96  
-        if (this.scope) this.mix('variables', this.scope.variables || this.scope, undefined, undefined, true);
  95
+        if (this.scope) this.mix('variables', this.scope.variables || this.scope, undefined, undefined, 'under');
97 96
         this.parentScope = this.scope;
98 97
         this.scope = this;
99 98
       }
@@ -208,11 +207,11 @@ LSD.Script.prototype = new (LSD.Struct({
208 207
           self.value = value;
209 208
         }
210 209
         this._enumerated = true;
211  
-        value.watch(this._enumerator);
  210
+        value.watch(undefined, this._enumerator);
212 211
       }  
213 212
     }
214 213
     if (old != null && old.push && old._watch && this._enumerated) {
215  
-      old.unwatch(this._enumerator);
  214
+      old.unwatch(undefined, this._enumerator);
216 215
       delete this._enumerated;
217 216
     }
218 217
     var output = this.output;
@@ -237,7 +236,7 @@ LSD.Script.prototype = new (LSD.Struct({
237 236
               break;
238 237
             default:
239 238
               if (output.push) this._callback(output, null, value, old, this);
240  
-              else this._callback(output, value, null, old);
  239
+              else this._callback(output, null, value, old);
241 240
           }
242 241
       }
243 242
     }
@@ -298,51 +297,9 @@ LSD.Script.prototype = new (LSD.Struct({
298 297
         this.execute(!!value, meta);
299 298
     }
300 299
   },
301  
-
302  
-/*
303  
-  Selectors can be used without escaping them in strings in LSD.Script.
304  
-  A selector targetted at widgets updates the collection as the widgets
305  
-  change and recalculates the expression in real time.
306  
-
307  
-  The only tricky part is that a simple selector may be recognized as
308  
-  a variable (e.g. `div.container`) or logical expression (`ul > li`) and
309  
-  not fetch the elements. A combinator added before ambigious expression
310  
-  would help parser to recognize selector. Referential combinators
311  
-  `$`, `&`, `&&`, and `$$` may be used for that. Selectors are targetted
312  
-  at widgets by default, unless `$$` or `$` combinator is used.
313  
-
314  
-  You can learn more about selectors and combinators in LSD.Module.Selector
315  
-
316  
-  Examples of expressions with selectors:
317  
-
318  
-      // Following selectors will observe changes in DOM and update collection
319  
-      // Because they are targetted at widgets
320  
-
321  
-      // Count `item` children in `menu#main` widget
322  
-      "count(menu#main > item)"
323  
-
324  
-      // Returns collection of widgets related to `grid` as `items` that are `:selected`
325  
-      "grid::items:selected"
326  
-
327  
-      // Return next widget to current widget
328  
-      "& + *"
329  
-
330  
-      // Combinators that have $ or $$ as referential combinators will not observe changes
331  
-      // and only fetch element once from Element DOM
332  
-
333  
-      // Find all `item` children in `menu` in current element
334  
-      "$ menu > item"
335  
-
336  
-      // Find `section` in parents that has no `section` siblings, and a details element next to it
337  
-      "$ ! section:only-of-type() + details"
338  
-
339  
-      // Following example is INCORRECT, because it is AMBIGIOUS and will not be recognized selector
340  
-      "ul > li" // variable `ul` greater than `li`
341  
-
342  
-      // CORRECT way: Add a combinator to disambiguate
343  
-      "& ul > li"
344  
-
345  
-*/
  300
+  wrapper: function() {
  301
+    
  302
+  },
346 303
   selector: function(value, old) {
347 304
     console.log(value, old)
348 305
   },
@@ -629,14 +586,11 @@ LSD.Script.prototype.execute = function(value, meta) {
629 586
         case true:
630 587
           break;
631 588
         case false:
632  
-          debugger
633 589
           for (var k = i + 1; k < j; k++) {
634 590
             var argument = this.args[k];
635 591
             if (argument != null && argument._calculated && argument.attached)
636  
-            
637  
-            debugger
638  
-            if (argument != null && argument._calculated && argument.attached)
639  
-              argument._set('attached')
  592
+              if (argument.executed)
  593
+                argument.execute(false, false)
640 594
           }
641 595
           args = args[args.length - 1];
642 596
           break loop;
63  Source/Type/Array.js
@@ -76,8 +76,30 @@ LSD.Array.prototype._offset = 0;
76 76
   LSD.Objects, thus it does not affect their `._owner` link.
77 77
 */
78 78
 LSD.Array.prototype._owning = false;
79  
-LSD.Array.prototype._hash = function(key, value, old, meta, from, i) {
80  
-  if (arguments.length < 6) return;
  79
+LSD.Array.prototype._join = function(key, value, old, meta, from) {
  80
+  if (from != 'watch') return; 
  81
+  if (value) {
  82
+    for (var i = 0, j = this._length >>> 0; i < j; i++)
  83
+      if (value._object)
  84
+        this._callback(value, i, this[i], false);
  85
+      else
  86
+            this._callback(value, this[i], i, false);
  87
+    (this.__watchers || (this.__watchers = [])).push(value);
  88
+  }
  89
+  if (old) {
  90
+    for (var i = 0, j = this._length >>> 0; i < j; i++)
  91
+      if (old._object)
  92
+        this._callback(old, i, this[i], false);
  93
+      else
  94
+        this._callback(old, this[i], i, false);
  95
+    var watchers = this.__watchers;
  96
+    var index = watchers.indexOf(old);
  97
+    watchers.splice(index, 1);
  98
+  }
  99
+  return true;
  100
+}
  101
+LSD.Array.prototype._hash = function(key, value, old, meta, from) {
  102
+  if (from == 'get' || (key.indexOf && key.indexOf('.') > -1)) return;
81 103
   var index = parseInt(key);
82 104
   if (index != key) return;
83 105
   old = this[index];
@@ -141,8 +163,9 @@ LSD.Array.prototype.push = function() {
141 163
   return this._length;
142 164
 };
143 165
 LSD.Array.prototype.indexOf = function(object, from) {
144  
-  for (var method = '_hash'; hash === undefined && this[method]; method = '_' + method)
145  
-    var hash = this[method](hash || object);
  166
+  if (object != null)
  167
+    for (var method = '_hash'; hash === undefined && this[method]; method = '_' + method)
  168
+      var hash = this[method](hash || object, undefined, undefined, undefined, 'get');
146 169
   var length = this._length >>> 0;
147 170
   for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
148 171
     var value = this[i];
@@ -308,26 +331,6 @@ LSD.Array.prototype.unshift = function() {
308 331
   this.splice.apply(this, [0, 0].concat(Array.prototype.slice.call(arguments, 0)))
309 332
   return this._length;
310 333
 };
311  
-LSD.Array.prototype.watch = function(callback, fn, meta) {
312  
-  if (typeof fn != 'undefined') return this._watch(callback, fn);
313  
-  for (var i = 0, j = this._length >>> 0; i < j; i++)
314  
-    if (callback._object)
315  
-      this._callback(callback, i, this[i], false);
316  
-    else
317  
-      this._callback(callback, this[i], i, false);
318  
-  (this.__watchers || (this.__watchers = [])).push(callback);
319  
-};
320  
-LSD.Array.prototype.unwatch = function(callback, fn, meta) {
321  
-  if (typeof fn != 'undefined') return this._watch(callback, fn);
322  
-  for (var i = 0, j = this._length >>> 0; i < j; i++)
323  
-    if (callback._object)
324  
-      this._callback(callback, i, this[i], false);
325  
-    else
326  
-      this._callback(callback, this[i], i, false);
327  
-  var watchers = this.__watchers;
328  
-  var index = watchers.indexOf(callback);
329  
-  watchers.splice(index, 1);
330  
-};
331 334
 LSD.Array.prototype._seeker = function(call, index, value, old, meta, from) {
332 335
   var block = call.block, invoker = call.invoker, array = call.meta, length = array && array.length;
333 336
   if (value !== undefined) {
@@ -427,7 +430,7 @@ LSD.Array.prototype.sort = function(callback, plain) {
427 430
   if (!callback) callback = this._sorter;
428 431
   var sorted = plain ? [] : new LSD.Array;
429 432
   var map = [];
430  
-  this.watch(function(value, index, old, meta, from) {
  433
+  this.watch(undefined, function(value, index, old, meta, from) {
431 434
     var moving = meta & 0x1;
432 435
     if (value !== undefined) {
433 436
       for (var i = sorted._length || sorted.length; i > 0; i--)
@@ -453,10 +456,16 @@ LSD.Array.prototype.sort = function(callback, plain) {
453 456
   return sorted;
454 457
 };
455 458
 LSD.Array.prototype.limit = function(number) {
456  
-  return (this._origin ? this : (new this.constructor).mix('_origin', this)).mix('_limit', number);
  459
+  var array = this._origin ? this : new this.constructor
  460
+  array.set('_origin', this)
  461
+  array.set('_limit', number);
  462
+  return array;
457 463
 };
458 464
 LSD.Array.prototype.offset = function(number) {
459  
-  return (this._origin ? this : (new this.constructor).mix('_origin', this)).mix('_offset', number);
  465
+  var array = this._origin ? this : new this.constructor
  466
+  array.set('_origin', this)
  467
+  array.set('_offset', number);
  468
+  return array;
460 469
 };
461 470
 LSD.Array.prototype.every = function(callback) {
462 471
   if (callback.result != null) return callback.result === 0;
5  Source/Type/Data.js
@@ -22,7 +22,7 @@ LSD.Data = function(object) {
22 22
   if (typeof object == 'string')
23 23
     subject.fromString(object);
24 24
   else if (object) 
25  
-    subject.mix(undefined, object);
  25
+    subject._mix(undefined, object);
26 26
   return subject;
27 27
 };
28 28
 LSD.Data.prototype = new LSD.Object;
@@ -92,4 +92,5 @@ LSD.Data.prototype._hash = function(key, value, old, meta) {
92 92
     }
93 93
   }
94 94
 }
95  
-LSD.Data.prototype._watchable = /^[a-zA-Z0-9._\[\]-]+?$/;
  95
+
  96
+LSD.Data.prototype._composite = true;
2  Source/Type/Group.js
@@ -27,7 +27,7 @@ LSD.Group = function(object, constructor) {
27 27
     object = null;
28 28
   }
29 29
   if (constructor) this.__constructor = typeof constructor == 'string' ? LSD[constructor] : constructor;
30  
-  if (object != null) this.mix(undefined, object)
  30
+  if (object != null) this._mix(undefined, object)
31 31
 };
32 32
 LSD.Group.prototype = new LSD.Object;
33 33
 LSD.Group.prototype.constructor = LSD.Group;
108  Source/Type/Journal.js
@@ -49,35 +49,34 @@ provides:
49 49
 */
50 50
 
51 51
 LSD.Journal = function(object) {
52  
-  if (object != null) this.mix(undefined, object)
  52
+  if (object != null) this._mix(undefined, object)
53 53
 };
54 54
 
55 55
 LSD.Journal.prototype = new LSD.Object;
56 56
 LSD.Journal.prototype.constructor = LSD.Journal;
57  
-LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
58  
-  if (arguments.length < 6) return;
59  
-  if (typeof index != 'number') 
60  
-    index = key.indexOf('.');
61  
-  if (index > -1) return;
62  
-/*
63  
-  Most of hash table implementations have a simplistic way to delete
64  
-  a key - they just erase the value. LSD.Journal's unset function 
65  
-  call may result in one of 3 cases
66  
-
67  
-  * Value becomes undefined, like after a `delete object.key` call in
68  
-    javascript. It only happens if there was a single logged value by
69  
-    that key. Callbacks are called and passed that value as a second
70  
-    argument.
71  
-  * Value is reverted to previous value on the stack. Callbacks are
72  
-    fired with both new and old value as arguments.
73  
-  * Value does not change, if the value being unset was not on top of
74  
-    the stack. It may also happen if there were two identical values 
75  
-    on top of the stack, so removing the top value falls back to the
76  
-    same value. Callbacks don't fire.
77  
-*/
  57
+LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, get) {
  58
+  if (prepend == 'watch' || prepend == 'get' || key.indexOf('.') > -1) return;
78 59
   var property = this._properties;
79 60
   if (property && (property = property[key]) && property.journal === false)
80 61
     return;
  62
+  switch (typeof prepend) {
  63
+    case 'number':
  64
+      var position = prepend;
  65
+      prepend = false;
  66
+      break;
  67
+    case 'string':
  68
+      switch (prepend) {
  69
+        case 'over': case 'under':
  70
+          var val = value || old;
  71
+          if (typeof val == 'object' && !val.exec && !val.push && !val.nodeType)
  72
+              return;
  73
+          prepend = prepend == 'under';
  74
+          break;
  75
+        default:
  76
+          prepend = prepend == 'before';
  77
+          break;
  78
+      }
  79
+  }
81 80
   var journal = this._journal;
82 81
   if (journal) {
83 82
     var group = journal[key];
@@ -91,19 +90,10 @@ LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
91 90
           old = group[k];
92 91
     }
93 92
   }
94  
-  if (typeof prepend == 'number') {
95  
-    var position = prepend;
96  
-    prepend = false;
97  
-  }    
98 93
   var chunked = property && property.chunked, current = this[key];
99 94
   if (positioned == null) positioned = -1;
100 95
   if (before) positioned ++;
101 96
   if (after) positioned ++;
102  
-/*
103  
-  When Journal setter is given an old value, it removes it from the
104  
-  journal. If a new value is not given and the old value was on top
105  
-  of the stack, it falls back to a previous value from the stack.
106  
-*/  
107 97
   if (old !== undefined) {
108 98
     var erasing = old === current;
109 99
     if (j && position == null)
@@ -118,19 +108,6 @@ LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
118 108
     if (old && old._calculated)
119 109
       this._watch(key, undefined, old, meta)
120 110
   } else old = current;
121  
-/*
122  
-  Journal setters accept a position at which the value should be written in
123  
-  journal. Positioned values are inserted in the beginning of the stack before
124  
-  regular values and may only be unset with the same position argument. In
125  
-  addition to regular numerical indecies, setters accept Infinity and
126  
-  -Infinity positions that insert a value after/before other indexed values.
127  
-
128  
-  That adds up to 5 distinct sections in the journal. Journal can have multiple
129  
-  indexed, prepended or appended values. But only one value for Infinity and
130  
-  one for -Infinity positions.
131  
-  
132  
-  [-Infinity, ... 0, 1, 2 .., Infinity, ... 'prepended' .., 'regular' ...]
133  
-*/
134 111
   if (position != null) {
135 112
     if (!group) {
136 113
       if (!journal) journal = this._journal = {};
@@ -154,12 +131,12 @@ LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
154 131
       group.before = value;
155 132
     }
156 133
     var diff = position - positioned;
157  
-    if (diff > 0) {
  134
+    if (diff > 0)
158 135
       for (var i = j, k = positioned; --i > k;)
159 136
         group[i + diff] = group[i];
160  
-    }
161 137
     if (value !== undefined) {
162  
-      if (diff > 0) j += diff;
  138
+      if (diff > 0)
  139
+        j += diff;
163 140
       group[position] = value;
164 141
     } else
165 142
       delete group[position];
@@ -173,7 +150,7 @@ LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
173 150
             break;
174 151
           } else if (k > position) return true;
175 152
     if (k > -1)
176  
-      return this.set(key, value, undefined, meta, prepend, index, false);
  153
+      return this._set(key, value, undefined, meta, undefined, false);
177 154
     return;
178 155
   }
179 156
   if (value !== undefined) {
@@ -204,47 +181,16 @@ LSD.Journal.prototype._hash = function(key, value, old, meta, prepend, index) {
204 181
       } else return;
205 182
     } else if (old !== current) 
206 183
       return false;
207  
-    return this.set(key, value, undefined, meta, prepend, index, false);
  184
+    return this._set(key, value, undefined, meta, undefined, false);
208 185
   }
209 186
     
210 187
 };
211  
-/*
212  
-  If a given value was transformed and the journal was not initialized yet,
213  
-  create a journal and write the given value
214  
-*/
215 188
 LSD.Journal.prototype._finalize = function(key, value, old, meta, prepend, hash, val) {
216 189
   if (val === value) return;
217 190
   var journal = this._journal;
218 191
   var group = journal && journal[key];
219 192
   if (!group) (journal || (this._journal = {}))[key] = [val];
220 193
 }
221  
-/*
222  
-  LSD.Journal is a subclass of LSD.Object and thus it inherits a method
223  
-  named `change` that is an alias to `set` with predefined `old` argument.
224  
-  As a LSD.Object method it does nothing of interest, but in LSD.Journal
225  
-  it pops the value on top the stack and then adds a new value instead.
226  
-  
227  
-  The method is useful to alter the value by the key in journalized hash
228  
-  from the outside:
229  
-    
230  
-    object.set('a', 1);             // adds value to stack
231  
-    console.log(object._journal.a)  // [1]
232  
-    object.set('a', 2);             // adds another value to the stack
233  
-    console.log(object._journal.a)  // [1, 2]
234  
-    object.change('a', 3);          // changes the value on top of the stack
235  
-    console.log(object._journal.a)  // [1, 3]
236  
-  
237  
-  Change method removes a value on top from the journal, but that may lead to
238  
-  unexpected results, if the top value was set by another entity that does
239  
-  not expect that value to be removed. It is possible to avoid side-effects
240  
-  completely by unsetting specific value that is known to be given by the party
241  
-  that invokes `change`. It is easy to do within a callback, because callbacks
242  
-  in LSD receive both old and new value:
243  
-  
244  
-    object.watch('a', function(value, old, meta) {
245  
-      object.set('b', value, old, meta);
246  
-    })
247  
-*/
248 194
 LSD.Journal.prototype.change = function(key, value, old, meta, prepend) {
249 195
   if (old === undefined) {
250 196
     var group = this._journal;
@@ -258,7 +204,7 @@ LSD.Journal.prototype.change = function(key, value, old, meta, prepend) {
258 204
       }
259 205
     } else old = this[key];
260 206
   }
261  
-  return this.set(key, value, old, meta, prepend);
  207
+  return this._set(key, value, old, meta, prepend);
262 208
 };
263 209
 LSD.Struct.implement({
264 210
   _nonenumerable: {
875  Source/Type/Object.js
<
@@ -18,48 +18,17 @@ provides:
18 18
 ...
19 19
 */
20 20
 LSD.Object = function(object) {
21  
-  if (object != null) this._mix(undefined, object)
  21
+  if (object != null)
  22
+    this._mix(undefined, object)
22 23
 };
23 24
 LSD.Object.prototype.constructor = LSD.Object;
24  
-/*
25  
-  Objects in LSD are different from regular objects in the way that LSD objects
26  
-  dont use or define getters at all. Values are precomputed and the good time
27  
-  to format or transform values is just before it was set. Object work with
28  
-  property observers and global listeners. Observing a property replaces some
29  
-  simple uses of Aspect Oriented Programming, an event system, and a pub/sub.
30  
-  It is possible because of the fact that all Object methods accept optional
31  
-  third parameter called `meta` that may describe what kind of operation led
32  
-  to the state change. It is easy to make dependent properties to
33  
-  know where the change come from, or what kind of change it is and react
34  
-  accordingly - e.g. avoid multiple or circular updates If a `meta` argument is
35  
-  not given, LSD automatically records cascade of changed properties and
36  
-  prevent circular property callbacks.
37  
-*/
38  
-LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash) {
39  
-/*
40  
-  Objects may have a special `_hash` hook method that is invoked every time
41  
-  setter is called. It may do various things depending on the return value.
42  
-  
43  
-    * `undefined` value makes setter proceed as usual.
44  
-    * A string or number is used as a new key. In that case a hook may be used
45  
-      as a way to hash or transform keys. 
46  
-    * `true` or `false` aborts setter, no callbacks are invoked. Useful for 
47  
-      structs that need to extend or override the way setters work.
48  
-    * Other values make object invoke onChange callbacks 
49  
-      without changing the state of an object. So callbacks or superclasses 
50  
-      may implement custom storage logic. It is also possible to use an object
51  
-      as an immutable message dispatcher that way.
52  
-      
53  
-  A single object can have multiple hashing hooks. Each additional hashing 
54  
-  function should be prefixed with underscore (like `__hash` and `___hash`). 
55  
-  The hasher that is prefixed the most is called first.
56  
- 
57  
-*/
  25
+LSD.Object.prototype.set = function(key, value, old, meta, prepend, hash) {
  26
+  //run hashing hooks to transform key
58 27
   var stringy = typeof key == 'string';
59  
-  switch (hash) {
  28
+  hasher: switch (hash) {
60 29
     case undefined:
61  
-      hasher: for (var method = '_hash'; this[method]; method = '_' + method) {
62  
-        hash = this[method](key, value, old, meta, prepend, index);
  30
+      for (var method = key == null ? '_join' : '_hash'; this[method]; method = '_' + method) {
  31
+        hash = this[method](key, value, old, meta, prepend);
63 32
         switch (typeof hash) {
64 33
           case 'boolean':
65 34
             return hash;
@@ -76,20 +45,168 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
76 45
             break hasher;
77 46
         }
78 47
       };
79  
-      break;
  48
+      break hasher;
80 49
     case false:
81 50
       hash = undefined;
82 51
   }
83  
-/*
84  
-  Object setters accept composite keys. LSD.Object constructs objects in the 
85  
-  path (e.g. setting `post.title` will create a `post` object), and observes
86  
-  the whole path (post object may be changed and title property will be unset
87  
-  from the previous object and set to the new object)
88  
-*/
89  
-  if (stringy && hash === undefined && typeof index != 'number')
90  
-    index = key.indexOf('.');
91  
-  if (index > -1) 
92  
-    return this._mix(key, value, old, meta, null, null, null, index);
  52
+  if (!meta && this._delegate) meta = this;
  53
+  // merge objects
  54
+  if (key == null) {
  55
+    var unstorable = meta && meta._delegateble, val;
  56
+    if (value) {
  57
+      if (typeof value._watch == 'function') value._watch(undefined, {
  58
+        fn: this._merger,
  59
+        bind: this,
  60
+        callback: this,
  61
+        prepend: prepend
  62
+      });
  63
+      var skip = value._nonenumerable;
  64
+      for (var prop in value)
  65
+        if (value.hasOwnProperty(prop) 
  66
+        && (unstorable == null || !unstorable[prop]) 
  67
+        && (skip == null || !skip[prop])
  68
+        && (val = value[prop]) !== undefined) { 
  69
+          this._set(prop, val, undefined, meta, prepend);
  70
+        }
  71
+    };
  72
+    if (old && typeof old == 'object') {
  73
+      if (typeof old._unwatch == 'function') 
  74
+        old._unwatch(undefined, this);
  75
+      var skip = old._nonenumerable;
  76
+      for (var prop in old)
  77
+        if (old.hasOwnProperty(prop) 
  78
+        && (unstorable == null || !unstorable[prop]) 
  79
+        && (skip == null || !skip[prop])
  80
+        && (val = old[prop]) !== undefined) {
  81
+          this._set(prop, undefined, val, meta, prepend);
  82
+        }
  83
+    }
  84
+    return true;
  85
+  }
  86
+  // set property in a foreign object, observe objects in path
  87
+  if (!hash && stringy)
  88
+    var index = key.indexOf('.', -1);
  89
+  if (index > -1) {
  90
+    var name = key.substr(key.lastIndexOf('.', index - 1) + 1, index) || '_owner';
  91
+    var subkey = key.substring(index + 1);
  92
+    if (this.onStore && this.onStore(name, value, old, meta, prepend, subkey) === false) return;
  93
+    // store arguments to reuse when object in path changes
  94
+    var storage = (this._stored || (this._stored = {}));
  95
+    var group = (storage[name] || (storage[name] = []));
  96
+    if (value !== undefined)
  97
+    group.push([subkey, value, undefined, meta, prepend, hash]);
  98
+    if (old !== undefined) 
  99
+      for (var i = 0, j = group.length; i < j; i++)
  100
+        if (group[i][1] === old) {
  101
+          group.splice(i, 1);
  102
+          break;
  103
+        }
  104
+    var obj = this[name];
  105
+    // build object in path
  106
+    if (obj == null) {
  107
+      if (value !== undefined && !this._nonenumerable[name] && !hash)
  108
+        obj = this._construct(name, null, meta);
  109
+      if (obj == null && this.onConstructRefused)
  110
+        this.onConstructRefused(key, value, meta, old, prepend, hash)
  111
+    // broadcast value to array
  112
+    } else if (obj.push && obj._object !== true) {
  113
+      var subindex = subkey.indexOf('.');
  114
+      var prop = (subindex > -1) ? subkey.substring(0, subindex) : subkey;
  115
+      if (parseInt(prop) == prop)
  116
+        obj._mix(subkey, value, old, meta, prepend, hash)
  117
+      else for (var i = 0, j = obj.length; i < j; i++)
  118
+        obj[i]._mix(subkey, value, old, meta, prepend, hash);
  119
+    // invoke a function
  120
+    } else if (obj.apply) {
  121
+      if (value !== undefined) 
  122
+        this[name](subkey, value);
  123
+      if (old !== undefined) {
  124
+        var negated = LSD.negated[name] || (LSD.negated[name] = LSD.negate(name));
  125
+        this[negated](subkey, old)
  126
+      }
  127
+    // set property in object
  128
+    } else {
  129
+      if (obj._mix && obj._ownable !== false) {
  130
+        if (!this._nonenumerable[name] 
  131
+        && value !== undefined
  132
+        && old !== obj && this._owning !== false
  133
+        && obj._shared !== true && obj._owner !== this)
  134
+          obj = this._construct(name, null, 'copy', obj)
  135
+        else
  136
+          obj._set(subkey, value, old, meta, prepend, hash);
  137
+      } else {
  138
+        for (var previous, k, object = obj; (subindex = subkey.indexOf('.', previous)) > -1;) {
  139
+          k = subkey.substring(previous || 0, subindex)
  140
+          if (previous > -1 && object._mix) {
  141
+            object._set(subkey.substring(subindex), value, old, meta, prepend, hash);
  142
+            break;
  143
+          } else if (object[k] != null) 
  144
+            object = object[k];
  145
+          previous = subindex + 1;
  146
+        }
  147
+        k = subkey.substring(previous);
  148
+        if (object._set)
  149
+          object._set(k, value, old, meta, prepend, hash)
  150
+        else
  151
+          object[k] = value;
  152
+      }
  153
+    }
  154
+    return true;
  155
+  // merge objects by key
  156
+  } else if (prepend === 'over' || prepend === 'under') {
  157
+    var arg = value || old;
  158
+    if (arg && typeof arg == 'object' && !arg.exec && !arg.push && !arg.nodeType) {
  159
+      if (this.onStore && this.onStore(key, value, meta, old, prepend) === false) return;
  160
+      var storage = (this._stored || (this._stored = {}));
  161
+      var group = storage[key] || (storage[key] = []);
  162
+      if (value !== undefined) 
  163
+        group.push([undefined, value, undefined, meta, prepend, hash, index]);
  164
+      if (old !== undefined) 
  165
+        for (var i = 0, j = group.length; i < j; i++)
  166
+          if (group[i][1] === old) {
  167
+            group.splice(i, 1);
  168
+            break;
  169
+          }
  170
+      var obj = this[key];
  171
+      // set a reference to remote object without copying it
  172
+      if (obj == null) {
  173
+        if (value !== undefined && !this._nonenumerable[key])
  174
+          obj = (value && value._mix && this._set(key, value, undefined, 'reference') && value)
  175
+               || this._construct(key, null, meta);
  176
+      // if remote object is array, merge object with every item in it
  177
+      } else if (obj.push && obj._object !== true) {
  178
+        for (var i = 0, j = obj.length; i < j; i++)
  179
+          if (!meta || !meta._delegate || !meta._delegate(obj[i], key, value, old, meta))
  180
+            obj[i]._mix(undefined, value, old, meta, prepend, hash);
  181
+      } else if (obj._mix) {
  182
+        var ref = obj._reference, owner = obj._owner;
  183
+        // if there was an object referenced by that key, copy it
  184
+        if (!this._nonenumerable[name] 
  185
+        && value !== undefined && obj !== old 
  186
+        && (!meta || !meta._delegate)
  187
+        && !value._shared
  188
+        && (ref && ref !== key || owner !== this)) {
  189
+          obj = this._construct(key, null, 'copy', obj)
  190
+        } else {
  191
+          // swap objects
  192
+          if (obj === old)
  193
+            this._set(key, value, old, meta)
  194
+          // merge objects
  195
+          else if (obj !== value) 
  196
+            obj._mix(undefined, value, old, meta, prepend)
  197
+        }
  198
+      // merge into regular javascript object (possible side effects)
  199
+      } else {
  200
+        if (value !== undefined) for (var prop in value) 
  201
+          obj[prop] = value[prop];
  202
+        if (old !== undefined) for (var prop in old) 
  203
+          if (old[prop] === obj[prop] && (value === undefined || old[prop] !== value[prop])) 
  204
+            delete old[prop];
  205
+      }
  206
+      return true;
  207
+    }
  208
+  }
  209
+  // set value by key
93 210
   var skip = this._nonenumerable;
94 211
   var nonenum = skip[key];
95 212
   var deleting = value === undefined
@@ -100,36 +217,24 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
100 217
       return false;
101 218
     if (deleting) 
102 219
       delete this[key];
103  
-    else this[key] = value;
  220
+    else
  221
+      this[key] = value;
104 222
   }
105  
-/*
106  
-  When objects link to other objects they write a link back to remote object.
107  
-  A linked object can access object that linked it with a private observable
108  
-  `_owner` property. Both linkee and linker objects may decide to avoid
109  
-  writing a link (e.g. Arrays dont write a link to its object values, and DOM
110  
-  elements dont let any objects write a link either).
111  
-*/
112 223
   if (nonenum !== true) {
113  
-    if (value != null && value._set && !value._owner && this._owning !== false)
114  
-      if (meta !== 'reference') {
  224
+    if (value != null && value._set && this._owning !== false)
  225
+      if (meta !== 'reference' && !value._owner) {
115 226
         if (value._ownable !== false) {
116 227
           value._reference = key;
117 228
           value._set('_owner', this);
118 229
         }
119  
-      } else value._references = (value._references || 0) + 1;
120  
-    if (old != null && old._owner === this)
  230
+      } 
  231
+    if (old != null && old._owner === this && old._reference === key)
121 232
       if (meta !== 'reference') {
122 233
         old._set('_owner', undefined, this);
123 234
         delete old._reference;
124  
-      } else old._references --;
  235
+      }
125 236
   }
126  
-  
127  
-/*
128  
-  Most of the keys that start with `_` underscore do not trigger calls to
129  
-  global object listeners. But they can be watched individually. A list of
130  
-  the skipped properties is defined in `._nonenumerable` object in the end of a file. 
131  
-  Builtin listeners may reject or transform value.
132  
-*/
  237
+  // run casting hooks to transform value
133 238
   var changed, val = value;
134 239
   for (var method = '_cast', i = 0; (changed === undefined || !nonenum) && this[method]; method = '_' + method)
135 240
     if ((changed = this[method](key, value, old, meta, prepend, hash)) !== undefined)
@@ -140,24 +245,16 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
140 245
   for (var method = '_finalize'; this[method]; method = '_' + method)
141 246
     if (this[method](key, value, old, meta, prepend, hash, val) === true)
142 247
       return true;
143  
-/*
144  
-  Watchers are listeners that observe every property in an object. It may be
145  
-  a function (called on change) or another object (property change in
146  
-  original object will change in the watcher object)
147  
-*/
  248
+  // notify observers
148 249
   var watchers = this._watchers;
149 250
   if (watchers && nonenum !== true) 
150 251
     for (var i = 0, j = watchers.length, watcher, fn; i < j; i++) {
151 252
       if ((watcher = watchers[i]) == null) continue;
152 253
       this._callback(watcher, key, value, old, meta, prepend, hash, val);
153 254
     }
154  
-/*
155  
-  An alternative to listening for all properties, is to watch a specific
156  
-  property. Callback observers recieve key, new and old value on each property
157  
-  change. 
158  
-*/  
159 255
   if (stringy && !(index > -1)) {
160  
-    if (!deleting && hash === undefined && this[key] !== value) this[key] = value;
  256
+    if (!deleting && hash === undefined && this[key] !== value)
  257
+      this[key] = value;
161 258
     var watched = this._watched;
162 259
     if (watched && (watched = watched[key]))
163 260
       for (var i = 0, fn; fn = watched[i++];)
@@ -165,14 +262,7 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
165 262
           fn.call(this, value, old, meta, prepend, hash, val);
166 263
         else
167 264
           this._callback(fn, key, value, old, meta, prepend, hash, val);
168  
-/*
169  
-  When an LSD.Object is mixed with a deep object, it builds missing objects
170  
-  to apply nested values. It also observes those objects for changes, so
171  
-  if any of them change it could re-apply the specific sub-tree of original nested
172  
-  object. Observing happens passively by storing links to sub-trees for each
173  
-  property that has nested object. When an object changes, it looks if it has
174  
-  any values stored for it to apply.
175  
-*/
  265
+    // apply stored arguments
176 266
     var stored = this._stored, mem, k;
177 267
     if (stored && (stored = stored[key])) 
178 268
       for (var i = 0, args; args = stored[i++];) {
@@ -180,7 +270,7 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
180 270
         if (val === value) continue;
181 271
         if (value != null && (!mem || !mem._delegate || !mem._delegate(value, key, val)))
182 272
           if (value._mix)
183  
-            value._mix.apply(value, args);
  273
+            value._set.apply(value, args);
184 274
           else if (k == null)  {
185 275
             if (typeof value == 'object')
186 276
               for (var p in val)
@@ -190,85 +280,19 @@ LSD.Object.prototype.set = function(key, value, old, meta, prepend, index, hash)
190 280
         if (old != null && typeof old == 'object' && meta !== 'copy' && val !== old 
191 281
         && (!mem || !mem._delegate || !mem._delegate(old, key, undefined, val, meta)))
192 282
           if (old._unmix)
193  
-            old._unmix.apply(old, args);
  283
+            old._unset.apply(old, args);
194 284
       }
195 285
   }
196 286
   return true;
197 287
 };
198 288
 
199  
-/*
200  
-  Unset method cleans object key resetting its value to undefined.
201  
-  
202  
-   Unsetting is an important concept, that bears close resemblance to `delete`
203  
-  keyword in javascript. Using unset directly or indirectly throughout the
204  
-  code enables clean objects with predictable reusability patterns. Removing
205  
-  values and cleaning up side effects with `unset` and `unmix` methods goes a
206  
-  long way of programs without race conditions, that can be seemlessly
207  
-  assembled and disassembled at run time.
208  
-  
209  
-   LSD.Object#unset uses `delete` keyword to remove value from the object.
210  
-  It usually makes the key `undefined` in an object, if an object prototype
211  
-  does not have such key. If an object prototype has a value with the same
212  
-  key, the object will still reference that value after `delete` is called.
213  
-  LSD.Journal setters have similar behavior, it is possible to overwrite
214  
-  any value, but `unset`ting may revert the value to the one that was set
215  
-  previously. Repeated calls to `unset` will remove all values from the 
216  
-  journal and the key will finally may become undefined.
217  
-  
218  
-   `unset` method does all things that `set` does in the same order: hashes its
219  
-  key, deals with ownership reference, transforms values, notifies 
220  
-  observers, fires callbacks and processes stored arguments
221  
-*/
222 289
 LSD.Object.prototype.unset = function(key, value, old, meta, index, hash) {
223 290
   return this._set(key, old, value, meta, index, hash);
224 291
 };
225  
-/*
226  
-  Get method fetches a value by a simple or composite keys. If an
227  
-  optional construct argument is given, it creates objects in place of
228  
-  missing properties.
229  
-  
230  
-   Despite the fact that older implementations of JavaScript do not allow
231  
-  native getter functions, `get` method in LSD is private and should not be
232  
-  used in places other than internals. The reason is that values in LSD are
233  
-  precomputed, and can be accessed like regular properties. There's no such
234  
-  thing as a getter method for a property in LSD.Object. If a property needs 
235  
-  to be set in a right format, it gets transformed, constructed or parsed 
236  
-  on set before it is assigned to the object.
237  
-  
238  
-   That also means that composite properties can not be described by a
239  
-  simple function that can be only executed when the property was requested.
240  
-  Properties that depend on more than one value may be implemented in two
241  
-  ways:
242  
-  
243  
-   * with LSD.Script expression in `imports` object that lazily creates
244  
-     individual observer objects for each variable used in parsed expression
245  
-   * or by listening to all properties and reacting to all involved
246  
-     properties and running a shared routine that tries to compose property
247  
-     from values it is observing.
248  
-  
249  
-  LSD.Script is fully magical, updates are lazy, happen in the right time and
250  
-  computed properties are always up to date. It's efficient in re-computation
251  
-  of expression, because it saves intermediate results for every branch of an
252  
-  expression syntax tree. So when values in expression change, it only
253  
-  recomputes affected parts of an expression. Still, LSD.Script has its own
254  
-  recursive evaluation model that has a large overhead and may be inappropriate
255  
-  in scenarios with many objects.
256  
-
257  
-   LSD internals follow the second way of composing properties manually. It's
258  
-  the most memory efficient and LSD provides a few tools that may help in
259  
-  writing repetetive callbacks. Doing it manually may be very peformant, but
260  
-  needs tests that handle all possible situations.
261 292
 
262  
-   Another reason why the `get` method is so underused, is because LSD deals
263  
-  with observable objects and `get` function returns the value that object has
264  
-  at the time of getter invocation. If the value is changed, there's no way to
265  
-  know it for a variable that holds reference to a previous value. That is why
266  
-  `watch(key, callback) ` should be used instead, because it hides all the
267  
-  complexity of mutable state of objects without all the glue code
268  
-*/
269 293
 LSD.Object.prototype.get = function(key, construct, meta) {
270 294
   for (var method = '_hash'; this[method] && hash === undefined; method = '_' + method) {
271  
-    var hash = this[method](key, undefined, undefined, meta);
  295
+    var hash = this[method](key, undefined, undefined, meta, 'get');
272 296
     switch (typeof hash) {
273 297
       case 'boolean':
274 298
         return;
@@ -296,412 +320,64 @@ LSD.Object.prototype.get = function(key, construct, meta) {
296 320
     } else break;
297 321
   }
298 322
 };
299  
-/*
300  
-  Mixing is a higher level abstraction above simply setting properties. `mix`
301  
-  method accepts both pairs of keys and values and whole objects to set and
302  
-  unset properties.
303  
-
304  
-   Mixed values are stored twice in the object. Once, as the keys and values
305  
-  processed by setters, and another time is when original arguments are
306  
-  stored to be used later. For example, when an pair like
307  
-  `attributes.tabindex`: `1` is mixed into the object, the arguments are
308  
-  stored and then `tabindex` property is applied to `attributes` object. When
309  
-  `attributes` object changes, arguments are used to clean up the old object,
310  
-  and assign properties to the new object. Similar thing happens when deep
311  
-  nested objects are merged, it stores values on each level of the original
312  
-  object and can re-apply it to related struct objects when they change.
313 323
 
314  
-   When an observable object is mixed, it can be opted-in for "live" merging,
315  
-  when updates to the merged object will propagate into the object it was
316  
-  merged into. By default, all new and updated values are appended on top,
317  
-  overwriting values that were set previously. When `prepend` argument is
318  
-  given, reverse merging will be used instead, applying values to the bottom
319  
-  of the stack. That will make merged object never overwrite the values that
320  
-  were there before. Those will only be used when the values that shadows the
321  
-  merged values will be unset.
322  
-*/
323  
-LSD.Object.prototype.mix = function(key, value, old, meta, merge, prepend, lazy, index) {
324  
-  if (!meta && this._delegate) meta = this;
325  
-/*
326  
-    // mix an object
327  
-    this.mix(undefined, object)
328  
-    // mix object, unmix old object
329  
-    this.mix(undefined, object, old) 
330  
-  
331  
-    // unmix method is an alias to mix that passes value as old value
332  
-    this.unmix(undefined, object);
333  
-    // is the same as:
334  
-    this.mix(undefined, undefined, object)
335  
-  
336  
-    // mix & observe objects
337  
-    this.mix(undefined, object, old, meta, true) 
338  
-    // reverse merge, does not overwrite present keys
339  
-    this.mix(undefined, object, old, meta, true, true) 
340  
-*/
341  
-  if (key == null) {
342  
-    var unstorable = meta && meta._delegateble, val;
343  
-    if (value) {
344  
-      if (value._watch) value._watch({
345  
-        fn: this._merger,
346  
-        bind: this,
347  
-        callback: this,
348  
-        prepend: prepend
349  
-      });
350  
-      var skip = value._nonenumerable;
351  
-      for (var prop in value)
352  
-        if (value.hasOwnProperty(prop) && (unstorable == null || !unstorable[prop]) && (skip == null || !skip[prop]))
353  
-          if ((val = value[prop]) != null && val._ownable === false)
354  
-            this._set(prop, val, undefined, meta, prepend);
355  
-          else
356  
-            this._mix(prop, val, undefined, meta, merge, prepend, lazy);
357  
-    };
358  
-    if (old && typeof old == 'object') {
359  
-      if (old._unwatch) 
360  
-        old._unwatch(this);
361  
-      var skip = old._nonenumerable;
362  
-      for (var prop in old)
363  
-        if (old.hasOwnProperty(prop) && (unstorable == null || !unstorable[prop]) && (skip == null || !skip[prop]))
364  
-          if ((val = old[prop]) != null && val._ownable === false)
365  
-            this._set(prop, undefined, val, meta, prepend);
366  
-          else
367  
-            this._mix(prop, undefined, val, meta, merge, prepend, lazy);
368  
-    }
369  
-    return this;
370  
-  }
371  
-/*
372  
-  A string in the key may contain dots `.` that denote nested objects. The
373  
-  values are passed through to the related objects, but they are also stored
374  
-  in original object, so whenever related object reference is changed, the