Show:
  1. /* Copyright © 2015-2016 David Valdman */
  2.  
  3. define(function(require, exports, module) {
  4. var EventHandler = require('../../events/EventHandler');
  5. var Stream = require('../../streams/Stream');
  6. var ResizeStream = require('../../streams/ResizeStream');
  7. var SizeNode = require('../SizeNode');
  8. var LayoutNode = require('../LayoutNode');
  9. var layoutAlgebra = require('../algebras/layout');
  10. var sizeAlgebra = require('../algebras/size');
  11.  
  12. var SIZE_KEYS = SizeNode.KEYS;
  13. var LAYOUT_KEYS = LayoutNode.KEYS;
  14.  
  15. /**
  16. * A node in the render tree. As such, it wraps a layout or size node,
  17. * providing them with an `add` method. By adding nodes, the render tree
  18. * is constructed, the leaves of which are `Surfaces`.
  19. *
  20. * @constructor
  21. * @class RenderTreeNode
  22. * @private
  23. * @param object {Object|SizeNode|LayoutNode|Surface|View}
  24. */
  25. function RenderTreeNode(object) {
  26. // layout and size inputs
  27. this._layout = new EventHandler();
  28. this._size = new EventHandler();
  29.  
  30. // layout and size streams
  31. this.size = null;
  32. this.layout = null;
  33.  
  34. this.root = null;
  35.  
  36. if (object) _set.call(this, object);
  37. }
  38.  
  39. /**
  40. * Extends the render tree with a new node. Similar to how a tree data structure
  41. * is created, but instead of a node with an array of children, children subscribe
  42. * to notifications from the parent.
  43. *
  44. * Nodes can be instances of `LayoutNode`, `SizeNode`, or Object literals with
  45. * size and layout properties, in which case, appropriate nodes will be created.
  46. *
  47. * This method also takes `Views` (subtrees) and `Surfaces` (leaves).
  48. *
  49. * @method add
  50. * @chainable
  51. * @param node {Object|SizeNode|LayoutNode|Surface|View} Node
  52. * @return {RenderTreeNode}
  53. */
  54. RenderTreeNode.prototype.add = function add(node) {
  55. var childNode;
  56.  
  57. if (node.constructor === Object){
  58. // Object literal case
  59. return _createNodeFromObjectLiteral.call(this, node);
  60. }
  61. else if (node._isView){
  62. // View case
  63. if (this.root)
  64. node._node.root = this.root;
  65. else if (this.tempRoot)
  66. node._node.tempRoot = this.tempRoot;
  67. childNode = node;
  68. }
  69. else {
  70. // Node case
  71. childNode = new RenderTreeNode(node);
  72. if (this.tempRoot)
  73. childNode.tempRoot = this.tempRoot;
  74. else childNode.root = _getRootNode.call(this);
  75. }
  76.  
  77. childNode._layout.subscribe(this.layout || this._layout);
  78. childNode._size.subscribe(this.size || this._size);
  79.  
  80. return childNode;
  81. };
  82.  
  83. function _createNodeFromObjectLiteral(object){
  84. var sizeKeys = {};
  85. var layoutKeys = {};
  86.  
  87. for (var key in object){
  88. if (SIZE_KEYS[key]) sizeKeys[key] = object[key];
  89. else if (LAYOUT_KEYS[key]) layoutKeys[key] = object[key];
  90. }
  91.  
  92. var node = this;
  93. var needsSize = Object.keys(sizeKeys).length > 0;
  94. var needsLayout = Object.keys(layoutKeys).length > 0;
  95.  
  96. // create extra align node if needed
  97. if (needsSize && layoutKeys.align){
  98. var alignNode = new LayoutNode({
  99. align : layoutKeys.align
  100. });
  101. delete layoutKeys.align;
  102. node = node.add(alignNode);
  103. }
  104.  
  105. // create size node first if needed
  106. if (needsSize)
  107. node = node.add(new SizeNode(sizeKeys));
  108.  
  109. // create layout node if needed
  110. if (needsLayout)
  111. node = node.add(new LayoutNode(layoutKeys));
  112.  
  113. return node;
  114. }
  115.  
  116. function _getRootNode(){
  117. if (this.root) return this.root;
  118. if (this.tempRoot) return _getRootNode.call(this.tempRoot);
  119. return this;
  120. }
  121.  
  122. function _set(object) {
  123. if (object instanceof SizeNode){
  124. this.size = ResizeStream.lift(
  125. function SGSizeAlgebra (objectSpec, parentSize){
  126. if (!parentSize) return false;
  127. return (objectSpec)
  128. ? sizeAlgebra(objectSpec, parentSize)
  129. : parentSize;
  130. },
  131. [object, this._size]
  132. );
  133. return;
  134. }
  135. else if (object instanceof LayoutNode){
  136. this.layout = Stream.lift(
  137. function SGLayoutAlgebra (objectSpec, parentSpec, size){
  138. if (!parentSpec || !size) return false;
  139. return (objectSpec)
  140. ? layoutAlgebra(objectSpec, parentSpec, size)
  141. : parentSpec;
  142. },
  143. [object, this._layout, this._size]
  144. );
  145. return;
  146. }
  147.  
  148. // object is a leaf node
  149. object._size.subscribe(this._size);
  150. object._layout.subscribe(this._layout);
  151. object._getRoot = _getRootNode.bind(this);
  152. }
  153.  
  154. module.exports = RenderTreeNode;
  155. });
  156.